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. 安装 mysql

    1.安装mysql客户端 yum install mysql 2.安装mysql 服务器端 yum install mysql-server 3.配置 mysql字符集 /etc/my.cnf 加入 ...

  2. ftp文件操作

    PHP FTP操作类( 上传.拷贝.移动.删除文件/创建目录 ) 2016-06-01 PHP编程 /** * 作用:FTP操作类( 拷贝.移动.删除文件/创建目录 ) */ class class_ ...

  3. 深入理解javascript闭包(一)

    原文转自脚本之家(http://www.jb51.net/article/24101.htm) 闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现. ...

  4. C#编写的序列化通用类代码

    using System; using System.IO; using System.IO.Compression; using System.Runtime.Serialization.Forma ...

  5. matlab图像类型转换以及uint8、double、im2double、im2uint8和mat2gray等说明

    转自:http://blog.csdn.net/fx677588/article/details/53301740 1. matlab图像保存说明 matlab中读取图片后保存的数据是uint8类型( ...

  6. 自然语言处理(5)之Levenshtein最小编辑距离算法

    自然语言处理(5)之Levenshtein最小编辑距离算法 题记:之前在公司使用Levenshtein最小编辑距离算法来实现相似车牌的计算的特性开发,正好本节来总结下Levenshtein最小编辑距离 ...

  7. JS基于时间戳写的浏览访问人数

    Title:JS基于时间戳写的浏览访问人数  --2013-12-23 14:07 <script language="JavaScript"> var timesta ...

  8. 未能找到类型或命名空间名称“XXXX”(是否缺少 using 指令或程序集引用?) 转

    未能找到类型或命名空间名称“XXXX”(是否缺少 using 指令或程序集引用?)   项目中 App_Code 文件夹中的类的命名空间,在添加的页面的  using XXXX  时,提示 未能找到类 ...

  9. HDOJ(HDU) 1862 EXCEL排序(类对象的快排)

    Problem Description Excel可以对一组纪录按任意指定列排序.现请你编写程序实现类似功能. Input 测试输入包含若干测试用例.每个测试用例的第1行包含两个整数 N (<= ...

  10. cf701B Cells Not Under Attack

    Vasya has the square chessboard of size n × n and m rooks. Initially the chessboard is empty. Vasya ...