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

来源:互联网 发布:利用网络赚钱的方法 编辑:程序博客网 时间:2024/04/28 01:55

基于主管建议我看下缓存这方面的资料,所以找到了郭神的《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
原创粉丝点击