Activity


public class MainActivity extends Activity {
    private GridView mPhotoWall;
    private PhotoWallAdapter mAdapter;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mPhotoWall = (GridView) findViewById(R.id.photo_wall);
        mAdapter = new PhotoWallAdapter(this, Images.imageThumbUrls, mPhotoWall);
        mPhotoWall.setAdapter(mAdapter);
    }
    @Override
    protected void onPause() {
        super.onPause();
        mAdapter.fluchCache();//将内存中的操作记录同步到日志文件journal当中,在Activity的onPause()方法中去调用一次flush()方法就可以了
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        mAdapter.cancelAllTasks();// 退出程序时结束所有的下载任务
    }
}

Adapter

/**
 * GridView的适配器,负责异步从网络上下载图片展示在照片墙上。
 */
public class PhotoWallAdapter extends BaseAdapter {
    private String[] urls;
    private Context context;
    /**记录所有正在下载或等待下载的任务 */
    private Set<BitmapWorkerTask> taskCollection;
    /**图片缓存技术的核心类,用于缓存所有下载好的图片,在程序内存达到设定值时会将最少最近使用的图片移除掉 */
    private LruCache<String, Bitmap> mMemoryCache;
    /**图片硬盘缓存核心类 */
    private DiskLruCache mDiskLruCache;
    /**记录每个子项的高度     */
    private int mItemHeight = 0;
    /** GridView的实例 */
    private GridView mPhotoWall;
    //******************************************************************************************************************************
    public PhotoWallAdapter(Context context, String[] urls, GridView mPhotoWall) {
        this.context = context;
        this.urls = urls;
        this.mPhotoWall = mPhotoWall;
        taskCollection = new HashSet<BitmapWorkerTask>();
        // 获取应用程序最大可用内存
        int maxMemory = (int) Runtime.getRuntime().maxMemory();
        // 设置图片缓存大小为程序最大可用内存的1/8
        int cacheSize = maxMemory / 8;
        mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
            @Override
            protected int sizeOf(String key, Bitmap bitmap) {
                return bitmap.getByteCount();
            }
        };
        // 获取图片缓存路径
        File cacheDir = getDiskCacheDir(context, "thumb");
        if (!cacheDir.exists()) cacheDir.mkdirs();
        // 创建DiskLruCache实例,初始化缓存数据
        try {
            mDiskLruCache = DiskLruCache.open(cacheDir, getAppVersion(context), 1, 10 * 1024 * 1024);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    //******************************************************************************************************************************
    @SuppressLint("InflateParams")
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View view;
        if (convertView == null) view = LayoutInflater.from(context).inflate(R.layout.photo_layout, null);
        else view = convertView;
        ImageView imageView = (ImageView) view.findViewById(R.id.photo);
        imageView.setTag(urls[position]);// 给ImageView设置一个Tag,保证异步加载图片时不会乱序
        imageView.setImageResource(R.drawable.ic_launcher);
        //每次加载图片的时候都优先去内存缓存当中读取,当读取不到的时候则回去硬盘缓存中读取,而如果硬盘缓存仍然读取不到的话,就从网络上请求原始数据
        //不管是从硬盘缓存还是从网络获取,读取到了数据之后都添加到内存缓存当中,这样的话我们下次再去读取图片的时候就能迅速从内存当中读取到
        loadBitmaps(imageView, urls[position]);
        return view;
    }
    @Override
    public int getCount() {
        return urls == null ? 0 : urls.length;
    }
    @Override
    public Object getItem(int position) {
        return urls == null ? null : urls[position];
    }
    @Override
    public long getItemId(int position) {
        return position;
    }
    //******************************************************************************************************************************
    /**
     * 将一张图片存储到LruCache中。
     * @param key LruCache的键,这里传入图片的URL地址。
     * @param bitmap  LruCache的值,这里传入从网络上下载的Bitmap对象。
     */
    public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
        if (getBitmapFromMemoryCache(key) == null) mMemoryCache.put(key, bitmap);
    }
    /**
     * 从LruCache中获取一张图片,如果不存在就返回null。
     * @param key LruCache的键,这里传入图片的URL地址。
     * @return 对应传入键的Bitmap对象,或者null。
     */
    public Bitmap getBitmapFromMemoryCache(String key) {
        return mMemoryCache.get(key);
    }
    /**
     * 加载Bitmap对象。此方法会在LruCache中检查所有屏幕中可见的ImageView的Bitmap对象
     * 如果发现任何一个ImageView的Bitmap对象不在缓存中,就会开启异步线程去下载图片。
     */
    public void loadBitmaps(ImageView imageView, String imageUrl) {
        try {
            //从内存中获取缓存,如果获取到了则直接调用ImageView的setImageBitmap()方法将图片显示到界面上
            Bitmap bitmap = getBitmapFromMemoryCache(imageUrl);
            if (bitmap != null) imageView.setImageBitmap(bitmap);
            else {//如果内存中没有获取到,则开启一个BitmapWorkerTask任务来去异步加载图片
                BitmapWorkerTask task = new BitmapWorkerTask();
                taskCollection.add(task);//记录所有正在下载或等待下载的任务
                task.execute(imageUrl);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 取消所有正在下载或等待下载的任务。
     */
    public void cancelAllTasks() {
        if (taskCollection != null) {
            for (BitmapWorkerTask task : taskCollection) {
                task.cancel(false);
            }
        }
    }
    /**
     * 根据传入的uniqueName获取硬盘缓存的路径地址。
     */
    public File getDiskCacheDir(Context context, String uniqueName) {
        String cachePath;
        //当SD卡【存在】或者SD卡【不可被移除】时
        if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) || !Environment.isExternalStorageRemovable()) {
            cachePath = context.getExternalCacheDir().getPath();//【SDCard/Android/data/包名/cache/】目录
        } else cachePath = context.getCacheDir().getPath();//【/data/data/包名/cache/】目录
        return new File(cachePath + File.separator + uniqueName);
    }
    /**
     * 获取当前应用程序的版本号。
     */
    public int getAppVersion(Context context) {
        try {
            PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
            return info.versionCode;
        } catch (NameNotFoundException e) {
            e.printStackTrace();
        }
        return 1;
    }
    /**
     * 设置item子项的高度。
     */
    public void setItemHeight(int height) {
        if (height == mItemHeight) return;
        mItemHeight = height;
        notifyDataSetChanged();
    }
    /**
     * 使用MD5算法对传入的key进行加密并返回。
     */
    public String hashKeyForDisk(String key) {
        String cacheKey;
        try {
            //为应用程序提供信息摘要算法的功能,如 MD5 或 SHA 算法。信息摘要是安全的单向哈希函数,它接收任意大小的数据,并输出固定长度的哈希值。
            MessageDigest mDigest = MessageDigest.getInstance("MD5");
            mDigest.update(key.getBytes());//使用指定的 byte 数组更新摘要
            byte[] bytes = mDigest.digest();//通过执行诸如填充之类的最终操作完成哈希计算,返回存放哈希值结果的 byte 数组
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < bytes.length; i++) {
                String hex = Integer.toHexString(0xFF & bytes[i]);//以十六进制无符号整数形式返回一个整数参数的字符串表示形式
                if (hex.length() == 1) sb.append('0');
                sb.append(hex);
            }
            cacheKey = sb.toString();
        } catch (NoSuchAlgorithmException e) {
            cacheKey = String.valueOf(key.hashCode());
        }
        return cacheKey;
    }
    /**
     * 将缓存记录同步到journal文件中。
     */
    public void fluchCache() {
        if (mDiskLruCache != null) {
            try {
                mDiskLruCache.flush();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    //**********************************************************************************************************************
    //                                                                                异步下载图片,并使用DiskLruCache进行缓存
    //**********************************************************************************************************************
    /**
     * 异步下载图片的任务。
     */
    class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap> {
        /**图片的URL地址 */
        private String imageUrl;
        @Override
        protected Bitmap doInBackground(String... params) {
            imageUrl = params[0];
            FileDescriptor fileDescriptor = null;//与此流有关的文件描述符对象,主要实际用途是创建一个包含该结构的 FileInputStream 或 FileOutputStream
            Snapshot snapShot = null;
            FileInputStream fileInputStream = null;//缓存文件的输入流
            try {
                // 首先根据图片的URL生成对应的MD5
                final String key = hashKeyForDisk(imageUrl);
                // 查找key对应的缓存
                snapShot = mDiskLruCache.get(key);
                // 如果没有找到对应的缓存,则准备从网络上请求数据,并写入缓存
                if (snapShot == null) {
                    DiskLruCache.Editor editor = mDiskLruCache.edit(key);
                    if (editor != null) {
                        OutputStream outputStream = editor.newOutputStream(0);
                        if (downloadUrlToStream(imageUrl, outputStream)) editor.commit();
                        else editor.abort();
                    }
                    // 缓存被写入后,再次查找key对应的缓存
                    snapShot = mDiskLruCache.get(key);
                }
                if (snapShot != null) {
                    fileInputStream = (FileInputStream) snapShot.getInputStream(0);
                    fileDescriptor = fileInputStream.getFD();//返回表示到文件系统中实际文件的连接的 FileDescriptor 对象,该文件系统正被此 FileInputStream 使用
                }
                // 将缓存数据解析成Bitmap对象
                Bitmap bitmap = null;
                if (fileDescriptor != null) bitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor);
                // 将Bitmap对象添加到内存缓存当中
                if (bitmap != null) addBitmapToMemoryCache(params[0], bitmap);
                return bitmap;
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (fileDescriptor == null && fileInputStream != null) {
                    try {
                        fileInputStream.close();
                    } catch (IOException e) {
                    }
                }
            }
            return null;
        }
        @Override
        protected void onPostExecute(Bitmap bitmap) {
            super.onPostExecute(bitmap);
            // 根据Tag找到相应的ImageView控件,将下载好的图片显示出来。
            ImageView imageView = (ImageView) mPhotoWall.findViewWithTag(imageUrl);
            if (imageView != null && bitmap != null) imageView.setImageBitmap(bitmap);
            taskCollection.remove(this);
        }
        /**
         * 建立HTTP请求,并获取Bitmap对象。
         * @param imageUrl 图片的URL地址
         * @return 解析后的Bitmap对象
         */
        private boolean downloadUrlToStream(String urlString, OutputStream outputStream) {
            HttpURLConnection urlConnection = null;
            BufferedOutputStream out = null;
            BufferedInputStream in = null;
            try {
                final URL url = new URL(urlString);
                urlConnection = (HttpURLConnection) url.openConnection();
                in = new BufferedInputStream(urlConnection.getInputStream(), 8 * 1024);
                out = new BufferedOutputStream(outputStream, 8 * 1024);
                int b;
                while ((b = in.read()) != -1) {
                    out.write(b);
                }
                return true;
            } catch (final IOException e) {
                e.printStackTrace();
            } finally {
                if (urlConnection != null) {
                    urlConnection.disconnect();
                }
                try {
                    if (out != null) {
                        out.close();
                    }
                    if (in != null) {
                        in.close();
                    }
                } catch (final IOException e) {
                    e.printStackTrace();
                }
            }
            return false;
        }
    }
}

清单文件

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.bqt.cache"
    android:versionCode="1"
    android:versionName="1.0" >
    <uses-sdk
        android:minSdkVersion="14"
        android:targetSdkVersion="17" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

附件列表

综合使用LruCache和DiskLruCache 缓存图片的更多相关文章

  1. android--------Universal-Image-Loader图片加载框架和结合LruCache缓存图片

    本博客包含包含Android-Universal-Image-Loader 网络图片加载框架实现图片加载和结合universal-image-loader与LruCache来自定义缓存图片,可以设置缓 ...

  2. 安卓开发笔记——关于图片的三级缓存策略(内存LruCache+磁盘DiskLruCache+网络Volley)

    在开发安卓应用中避免不了要使用到网络图片,获取网络图片很简单,但是需要付出一定的代价——流量.对于少数的图片而言问题不大,但如果手机应用中包含大量的图片,这势必会耗费用户的一定流量,如果我们不加以处理 ...

  3. Android Volley框架的使用(四)图片的三级缓存策略(内存LruCache+磁盘DiskLruCache+网络Volley)

    在开发安卓应用中避免不了要使用到网络图片,获取网络图片很简单,但是需要付出一定的代价——流量.对于少数的图片而言问题不大,但如果手机应用中包含大量的图片,这势必会耗费用户的一定流量,如果我们不加以处理 ...

  4. Cache【硬盘缓存工具类(包含内存缓存LruCache和磁盘缓存DiskLruCache)】

    版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 内存缓存LruCache和磁盘缓存DiskLruCache的封装类,主要用于图片缓存. 效果图 代码分析 内存缓存LruCache和 ...

  5. LruCache DiskLruCache 缓存 简介 案例 MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  6. 网络图片的获取以及二级缓存策略(Volley框架+内存LruCache+磁盘DiskLruCache)

    在开发安卓应用中避免不了要使用到网络图片,获取网络图片很简单,但是需要付出一定的代价——流量.对于少数的图片而言问题不大,但如果手机应用中包含大量的图片,这势必会耗费用户的一定流量,如果我们不加以处理 ...

  7. Android 使用 LruCache 缓存图片

    在你应用程序的 UI 界面加载一张图片是一件很简单的事情,但是当你需要在界面上加载一大堆图片的时候,情况就变得复杂起来.在很多情况下,(比如使用 ListView, GridView 或者 ViewP ...

  8. Android使用 LruCache 缓存图片

    摘要:在你应用程序的UI界面加载一张图片是一件很简单的事情,但是当你需要在界面上加载一大堆图片的时候,情况就变得复杂起来. 使用图片缓存技术 在 你应用程序的UI界面加载一张图片是一件很简单的事情,但 ...

  9. LruCache--远程图片获取与本地缓存

    Class Overview A cache that holds strong references to a limited number of values. Each time a value ...

随机推荐

  1. winform下调用webservice,传参List<string>

    用c#做了一个webservice,其中一个接口是public bool AddReturns(List<string> SQLStringList). 然后在另一个c#做的winform ...

  2. System.InvalidOperationException: 无法加载协定为“ServiceReference1.XXXXXXXXXXXXXXXX”的终结点配置部分,因为找到了该协定的多个终结点配置。请按名称指示首选的终结点配置部分。

    <system.serviceModel> <bindings> <basicHttpBinding> <binding name="testWeb ...

  3. Websocket 与代理服务器如何交互? How HTML5 Web Sockets Interact With Proxy Servers

    How HTML5 Web Sockets Interact With Proxy Servers Posted by Peter Lubberson Mar 16, 2010 With the re ...

  4. NSIndexSet-入门浅析

    NSIndexSet-入门浅析   记得上一次,用到,关于删除UITableView分组的方法 [tableView deleteSections:[NSIndexSet indexSetWithIn ...

  5. java Html2Image 实现html转图片功能

    //java Html2Image 实现html转图片功能 // html2image  HtmlImageGenerator imageGenerator = new HtmlImageGenera ...

  6. Node.js 入门(2)

    1.http 请求 //调用Node.js自带的http模块 var http = require("http"); //调用http模块提供的函数createServer htt ...

  7. poj 2892 &&hdu 1540 Tunnel Warfare

    http://poj.org/problem?id=2892 #include <cstdio> #include <cstring> #include <algorit ...

  8. 汇顶指纹传感器GF919深度解析

    前言: 随着指纹识别技术的日益普遍,其在手机上的应用也得到了广泛关注.作为全球第一款Android正面按压指纹识别手机,魅族MX4 Pro所搭载的国产指纹识别系统可谓是赚足了眼球,这就是由汇顶科技提供 ...

  9. 话说python

    前几年就职于一个大的软件公司时,开发过程中,有一个流程化的步骤要做,就是要由一个excel文档导出生成一堆sql语句.当时使用一个高手写的代码,只不过有限几行代码(应该不到100行),就完美实现此功能 ...

  10. 【笔试&面试】C#的托管代码与非托管代码

    1. C#中的托管代码是什么? 答:托管代码(ManagedCode)实际上就是中间语言(IL)代码.代码编写完毕后进行编译,此时编译器把代码编译成中间语言(IL),而不是能直接在你的电脑上运行的机器 ...