我的第一篇博客--图片加载器

来源:互联网 发布:全息投影 知乎 编辑:程序博客网 时间:2024/05/16 01:30

写在开头:毕业一年了,从零基础到现在接触android也已经一年多了,由一只小小菜鸟变成了一只小菜鸟。这一年来收获了很多,总想写些东西来总结过去一年的收获,奈何不知从何入手。刚好最近有空,想着是不是该学点新的东西呢,突然又想到自己的“拿来主义”太严重了,虽然整体上有完成一个项目,但是项目用到的一些框架,其实我也是一知半解的。所以我决定重新对整个项目熟悉一遍,分某块去总结技术点,刚好也是对过去的一点总结。想想还是有点小激动的

男怕入错行,女怕嫁错郎。以前我一直认为我不会从事it工作的,印象中加班如无底洞一样,所以我避之不及呢。一年多前,我甚至不懂java常见的数据类型有哪几种,什么mvc更不用说了,参加过笔试,这类题目只好放空了,想想那时候真是搞笑,已经是菜到不能再菜了。一次偶然的机会,我接触到Android,也不知道为啥,我就觉得这门技术很有前途。于是在大四上学期,我开始自学android,学习java的基本知识,终于有了一定的基础,顺便完成毕业设计,通过毕业答辩,也找到了一份android开发的工作。现在回头想想,我还是挺感谢那时候的自己,没有在最后一年继续荒废,不然也不会现在写这篇文章了。

额......废话好像有点多了,不好意思哈!现在进入主题。过去一年我就开发过一个项目,项目经验好少啊。项目改来改去的,迭代了十几个版本,好无奈啊。。。在此我就不吐槽公司了。这个项目我觉得主要重点技术有几个方面:1.图片的加载与处理;2.网络的加载和处理;3.自定义控件的使用;4.动画的使用...似乎每个应用都离不开以上几个知识点,当然还有其他的一些技术 ,在此我就不一一罗列了.......一般每个应用都会跟图片打交道的,我还没见过没有图片的应用哦。那么本篇文章我们将重点总结项目中的图片加载,我会将项目中应用的抽离出来写个demo,以后也将是这样,有不对的地方,欢迎大家吐槽啊,毕竟我也还是一只小菜鸟而已。

好了啥都不都说,先上图:(图一)从SD卡上读取图片

除了加载本地图片,我们还可以读取网络图片(图二)

由于制作gif麻烦,图片有点模糊,与实际情况不符合,实际运行结果清晰。接下来,我们开始着手学习。

一、谷歌对图片处理的建议

Google为Android开发提供了一个培训教程,在加载图片一节中提供了示例程序BitmapFun,实现了图片下载、缓存、解析加载的功能,至于BitmapFun的使用,我就不废话了,早有人对其进行了剖析了一番。你若想了解你可以到官网去看一下,或者搜一下博客,我随便搜了一下,发现这个仁兄讲得不错,你可以点击去看一下。当然也有人受到BitmapFun的迫害,哈哈,你也可以点击去瞧瞧。

二、你不得不知的LruCache和DiskLruCache

1.LruCache

Android用LruCache来取代原来强引用和软引用实现内存缓存,因为据说自2.3以后Android将更频繁的调用GC,导致软引用缓存的数据极易被释放。LruCache使用一个LinkedHashMap简单的实现内存的缓存,没有软引用,都是强引用。如果添加的数据大于设置的最大值,就删除最先缓存的数据来调整内存。他的主要原理在trimToSize方法中。需要了解两个主要的变量size和maxSize。maxSize是通过构造方法初始化的值,他表示这个缓存能缓存的最大值是多少。size在添加和移除缓存都被更新值,他通过safeSizeOf这个方法更新值。safeSizeOf默认返回1,但一般我们会根据maxSize重写这个方法,比如认为maxSize代表是KB的话,那么就以KB为单位返回该项所占的内存大小。LruCache的初始化如下:

// 获取应用程序最大可用内存int maxMemory = (int) Runtime.getRuntime().maxMemory();int cacheSize = maxMemory / 8;// 设置图片缓存大小为程序最大可用内存的1/8mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {@SuppressLint("NewApi")@Overrideprotected int sizeOf(String key, Bitmap bitmap) {return bitmap.getByteCount();}};

2.DiskLruCache

磁盘缓存利用原理是:开辟一定的空间用来缓存文件,将文件保存到磁盘上,利用最近最少使用算法,当空间已满时,写入新的文件替换最近最少使用的那个文件。DiskLruCache比较麻烦,利用了日志文件journal,保存着文件存储过程。具体原理大伙可以参考这位大神写的这篇文章,本篇文章的一部分代码也是引自其博客。下面我们就来看看怎么使用这个DiskLruCache:

初始化:

/** * 创建磁盘缓存 *  * @param context */private void createDiskLruCache(Context context) {try {// 获取图片缓存路径File cacheDir = FileHelper.getDiskCacheDir(context, "thumb");if (!cacheDir.exists()) {cacheDir.mkdirs();}// 创建DiskLruCache实例,初始化缓存数据mDiskLruCache = DiskLruCache.open(cacheDir, FileHelper.getAppVersion(context), 1, 20 * 1024 * 1024);} catch (IOException e) {e.printStackTrace();}}
写入缓存:

Snapshot snapShot = null;try {// 生成图片URL对应的keyfinal String key = FileHelper.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 (DownloadUtil.downloadUrlToStream(imageUrl, outputStream)) {editor.commit();} else {editor.abort();}}mDiskLruCache.flush();// 缓存被写入后,再次查找key对应的缓存snapShot = mDiskLruCache.get(key);} else {System.out.println("从DiskLruCache上加载");}if (snapShot != null) {InputStream is = snapShot.getInputStream(0);bitmap = BitmapUtil.decodeBitmapFromInputStream(is, mReqWidth, mReqHeight);}} catch (IOException e) {e.printStackTrace();} catch (Exception e) {e.printStackTrace();} catch (OutOfMemoryError oom) {oom.printStackTrace();System.gc();}
好了,了解了LruCache和DiskLruCache的使用后,我们就要来完善一下其他细节了.。

三、LruCache和DiskLruCache结合使用。

经过上面的初始化后,图片加载器ImageLoader对外提供了DisplayImageView()至于该方法的参数,大伙可以根据自己需要来扩展。

/** * 加载图片 *  * @param url * @param imageView */public void DisplayImage(String url, ImageView imageView) {imageView.setTag(url);mIamgeViews.put(url, imageView);Bitmap bitmap = getBitmapFromMemoryCache(url);if (bitmap != null) {System.out.println("从LruCache上加载");imageView.setImageBitmap(bitmap);} else {Log.d(TAG, "isLock:" + isLock);if (isLock)return;BitmapWorkerTask task = new BitmapWorkerTask();taskCollection.add(task);task.execute(url);}}
BitmapWorkTask 是一个异步操作,学过android的人都知道,一些耗时的操作一定不能在主线程进行,你可以选择普通线程runnable或者使用异步线程AsyncTask。由于篇幅关系,我就不贴它的全部代码了,有兴趣的可以到文章的末尾去下载Demo的源码,这边就说一个地方。

@Overrideprotected void onPostExecute(Bitmap bitmap) {super.onPostExecute(bitmap);// 防止图片错位String tag = (String) imageView.getTag();if (tag.equals(imageUrl) && imageView != null && bitmap != null) {imageView.setImageBitmap(bitmap);}taskCollection.remove(this);}
一定要做图片错位防止措施哦......

还以为要结束这篇文章呢,发现忘了说一个问题,ListView和gridView快速滑动时候,不用加载图片,节省内存的同时,还是提高加载其他图片的速度。

// 添加滑动监听,快速滑动时候不加载数据gridView.setOnScrollListener(new OnScrollListener() {@Overridepublic void onScrollStateChanged(AbsListView view, int scrollState) {switch (scrollState) {case OnScrollListener.SCROLL_STATE_FLING:mImageLoader.lock();break;case OnScrollListener.SCROLL_STATE_IDLE:mImageLoader.unLock();break;case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:mImageLoader.unLock();break;default:break;}if (mAdapter != null) {mAdapter.notifyDataSetChanged();}}@Overridepublic void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {}});
嗦嘎!终于要写完这篇文章了,那个鸡冻啊。第一次写博客,写得不好,望读者原谅哈,希望下次能写得越来越好。最后我得感谢下,郭神的博客让我学习了很多。关于图片加载,他有更好的文章可以供大伙阅读哦,见连接

Android照片墙完整版,完美结合LruCache和DiskLruCache
Android DiskLruCache完全解析,硬盘缓存的最佳方案

Android高效加载大图、多图解决方案,有效避免程序OOM

收工,end~

源码在此




0 0