Android 自己来尝试性解读《Android照片墙完整版,完美结合LruCache和DiskLruCache》

基于主管建议我看下缓存这方面的资料,所以找到了郭神的《Android照片墙完整版,完美结合LruCache和DiskLruCache》

http://blog.csdn.net/guolin_blog/article/details/34093441

和《Android DiskLruCache完全解析,硬盘缓存的最佳方案》

http://blog.csdn.net/guolin_blog/article/details/28863651

很荣幸能了解到大神对缓存的理解。所以特地把我对前篇代码阅读的心得体会记录下来,若有哪里说得不好、说错的,欢迎指正,至于后一篇大神已经说得非常非常好了,我也没啥说的。

public class PhotoWallAdapter extends ArrayAdapter<String>

public PhotoWallAdapter(Context context, int resource, String[] objects, GridView photoWall) {
        super(context, resource, objects);
        taskCollection = new HashSet<BitmapWorkerTask>();
        mOkHttpClient = new OkHttpClient();
        mPhotoWall = photoWall;
        //获取应用程序最大的可用内存
        int maxMemory = (int) Runtime.getRuntime().maxMemory();
        int cacheSize = maxMemory / 8;
        //设置图片缓存大小为最大可用内存的1/8
        mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
            @Override
            protected int sizeOf(String key, Bitmap value) {
                return value.getByteCount();
            }
        };

        //获取图片缓存路径
        File cacheDir = getDiskCacheDir(context, "pzh");
        if (!cacheDir.exists()) {
            cacheDir.mkdirs();
        }

        try {
            //设置10M的磁盘缓存
            mDiskLruCache = DiskLruCache.open(cacheDir, getAppVersion(context), 1, 10 * 1024 * 1024);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

可以看到郭神,是自己定义了一个适配器,这个适配器中,需要传入上下文、Url和GridView。原文中是用HttpUrlConnection,与时俱进,我就用OkHttp来代替。因为这是结合内存缓存和磁盘缓存来使用,所以思路是:先从内存里根据Key获取,若没有则从磁盘缓存里获取,还没有就下载,然后放进内存缓存(若有空间,没有就放磁盘缓存),接着再呈现出来。所以构造方法里步骤如下:
1、先获取获取程序最大运行内存Runtime.getRuntime().maxMemory(),再分配其中的八分之一给图片缓存new LruCache

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        //这里的数据是之前构造方法String[] objects里得到的
        String url = getItem(position);

        View view;

        if (convertView != null) {
            view = convertView;
        } else {
            view = View.inflate(getContext(), R.layout.photo_layout, null);
        }

        ImageView imageView = (ImageView) view.findViewById(R.id.photo);
        //设置item的高度
        if (imageView.getLayoutParams().height != mItemHeight) {
            imageView.getLayoutParams().height = mItemHeight;
        }
        //给Imageview设置,避免异步加载时图片不会乱序
        imageView.setTag(url);
        imageView.setImageResource(R.mipmap.ic_launcher);
        loadBitmaps(imageView, url);

        return view;
    }
这里主要是想看loadBitmaps(imageView, url);
 private void loadBitmaps(ImageView imageView, String url) {
        Bitmap bitmap = getBitmapFromMemoryCache(url);
        if (bitmap == null) {
            BitmapWorkerTask task = new BitmapWorkerTask();
            taskCollection.add(task);
            task.execute(url);
        } else {
            if (imageView != null && bitmap != null) {
                imageView.setImageBitmap(bitmap);
            }
        }
    }

1、首先getBitmapFromMemoryCache(url)从缓存中得到图片
2、若图片为空,则new BitmapWorkerTask()新建一个异步加载任务, taskCollection.add(task);同时把这个任务记录在HashSet中,以便可以取消这个加载任务。
3、若缓存中有图片,就直接显示出来

接下来看看如何从缓存中的到图片

    private Bitmap getBitmapFromMemoryCache(String url) {
        return mMemoryCache.get(url);
    }

很简单,根据key来可以获取。当然第一次加载为空,没什么可说的,那么就需要看看如何异步加载图片,放进缓存的操作

 class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap> {

        private String imageUrl;

        @Override
        protected Bitmap doInBackground(String... params) {
            //这个url是task.execute(url);这里传进来的,只传进来一个数,所以是params数组里的第0位
            imageUrl = params[0];
            FileDescriptor fileDescriptor = null;
            FileInputStream fileInputStream = null;
            DiskLruCache.Snapshot snapshot = null;
            //生成Url对应Md5加密的key
            String key = hashKeyForDisk(imageUrl);
            try {
                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();
                        }

                    }

                    snapshot = mDiskLruCache.get(key);
                }

                if(snapshot != null){
                    fileInputStream = (FileInputStream) snapshot.getInputStream(0);
                    fileDescriptor = fileInputStream.getFD();
                }

                //将数据解析成bitmap对象
                Bitmap bitmap = null;
                if(fileDescriptor != null){
                    bitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor);
                }

                if(bitmap != null){
                    addBitmapToMemoryCache(params[0],bitmap);
                }
                return bitmap;

            } catch (IOException e) {
                e.printStackTrace();
            }finally {
                if (fileInputStream != null) {
                    try {
                        fileInputStream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }


            return null;
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            super.onPostExecute(bitmap);

            ImageView imageView = (ImageView) mPhotoWall.findViewWithTag(imageUrl);
            if(imageView != null && bitmap != null){
                imageView.setImageBitmap(bitmap);
            }
            taskCollection.remove(this);
        }
    }

1、内存缓存没有图片,下载前先读取磁盘,就用Snapshot 这个类来读取磁盘缓存,若不为空,那么snapshot.getInputStream(0)可以读取到输入流,接着转成Bitmap,addBitmapToMemoryCache(params[0],bitmap)把url作为key和Bitmap放进缓存里面,当然key是url有些不规范,可以md5加密
2、若磁盘没有图片,就只能下载了,downLoadUrlToStream(imageUrl, outputStream)

  private boolean downLoadUrlToStream(String imageUrl, final OutputStream outputStream) {


        Request request = new Request.Builder()
                .url(imageUrl)
                .build();

        Call call = mOkHttpClient.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Request request, IOException e) {

            }

            @Override
            public void onResponse(Response response) throws IOException {
                InputStream is = null;
                BufferedOutputStream out = null;
                BufferedInputStream in = null;

                try {
                    is = response.body().byteStream();
                    in = new BufferedInputStream(is, 8 * 1024);
                    out = new BufferedOutputStream(outputStream, 8 * 1024);

                    int b;
                    while ((b = in.read()) != -1) {
                        out.write(b);
                    }
                    isSuccess = true;


                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    if (is != null) {
                        is.close();
                    }

                    if (in != null) {
                        in.close();
                    }

                    if(out != null){
                        out.close();
                    }
                }
            }

        });

        return isSuccess;

    }

使用OkHttp,需要定义Request和Call参数,response.body().byteStream()得到下载的输入流,把它写进输出流即可

这样就可以 editor.commit();就可以生成磁盘的缓存文件了, editor.abort();若下载不成功就废弃掉

最后加上两个方法

 public void fluchCache(){
        if(mDiskLruCache != null){
            try {
                mDiskLruCache.flush();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

将缓存记录同步到journal文件中。

public void cancelAllTasks(){
        if(taskCollection != null){
            for(BitmapWorkerTask task : taskCollection){
                task.cancel(false);
            }
        }
    }

取消所有下载任务

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值