java内存优化实例----在非UI线程中处理图片
来源:互联网 发布:鸳鸯 知乎 编辑:程序博客网 时间:2024/06/06 09:27
课程内容
- 使用 AsyncTask
- 处理并发情况
您还应该阅读
- Designing for Responsiveness
- Multithreading for Performance
在 高效的加载大尺寸图片 中介绍的 用来解析图片的 BitmapFactory.decode*
函数,需要在非UI线程中调用。
如果是读取网络图片或者磁盘图片,在UI线程中可能会导致程序ANR;如果是解析已经在内存中的图片,则可以在UI线程中调用这些函数。
这节内容介绍如何使用AsyncTask
来处理图片,以及如何处理并发访问。
使用 AsyncTask
AsyncTask
类是一个在后台线程中处理任务,并把结果反馈给UI线程的工具类。使用该类,只需要继承她并且重写对应的函数即可。
下面的代码演示了如何用这个类来载入一个大尺寸图片并显示在ImageView
中:
class BitmapWorkerTask extends AsyncTask { private final WeakReference imageViewReference; private int data = 0; public BitmapWorkerTask(ImageView imageView) { // Use a WeakReference to ensure the ImageView can be garbage collected imageViewReference = new WeakReference(imageView); } // Decode image in background. @Override protected Bitmap doInBackground(Integer... params) { data = params[0]; return decodeSampledBitmapFromResource(getResources(), data, 100, 100)); } // Once complete, see if ImageView is still around and set bitmap. @Override protected void onPostExecute(Bitmap bitmap) { if (imageViewReference != null && bitmap != null) { final ImageView imageView = imageViewReference.get(); if (imageView != null) { imageView.setImageBitmap(bitmap); } } }}
引用到ImageView
的 WeakReference
确保AsyncTask
不会直接引用 ImageView
从而导致其无法被垃圾回收。
当解析完图片后,无法确保 ImageView
是否还存在,所以需要在 onPostExecute()
函数中检查下,如果用户在图片加载期间离开了当前界面,则当图片加载完后用来显示图片的 ImageView
可能已经不存在了。
只要创建该Task并执行即可开始异步加载图片了:
public void loadBitmap(int resId, ImageView imageView) { BitmapWorkerTask task = new BitmapWorkerTask(imageView); task.execute(resId);}
处理并发访问
像 ListView
和 GridView
这种控件使用上面介绍的方式来载入图片可能会引入新的问题。为了提高内存的使用率,当用户做滚动操作的时候这些控件会重复利用子控件,如果每个子控件都触发一个AsyncTask
,当任务完成的时候 无法保证该子控件是否已经被重用了。甚至,这些任务开始的顺序和完成的顺序也是不一样的。
这篇博文Multithreading for Performance 进一步
讨论了如何处理并发操作,并且提供了一个解决方案:ImageView
中保存了最近触发的AsyncTask
对象,当任务完成的时候可以用来检测该引用的对象。
使用相似的函数,在前面介绍的AsyncTask
对象可以使用相似的模式来扩展。
创建一个特殊的 Drawable
子类来保存载入图片的Task引用。
这里使用 BitmapDrawable
类,这样当任务完成的时候可以直接在ImageView
中显示:
static class AsyncDrawable extends BitmapDrawable { private final WeakReference bitmapWorkerTaskReference; public AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) { super(res, bitmap); bitmapWorkerTaskReference = new WeakReference(bitmapWorkerTask); } public BitmapWorkerTask getBitmapWorkerTask() { return bitmapWorkerTaskReference.get(); }}
在执行 BitmapWorkerTask
之前,您需要创建一个AsyncDrawable
并且绑定到需要显示该图片的那个 ImageView
中:
public void loadBitmap(int resId, ImageView imageView) { if (cancelPotentialWork(resId, imageView)) { final BitmapWorkerTask task = new BitmapWorkerTask(imageView); final AsyncDrawable asyncDrawable = new AsyncDrawable(getResources(), mPlaceHolderBitmap, task); imageView.setImageDrawable(asyncDrawable); task.execute(resId); }}
上面代码中的 cancelPotentialWork
函数用来检测是否已经有一个Task绑定到 ImageView
了。
如果已经有个Task了就尝试取消这个Task(调用 cancel()
函数)。
在一些情况下,如果新的任务和已经存在任务的数据一样,则不需要额外的处理。下面是 cancelPotentialWork
函数的一种实现方式:
public static boolean cancelPotentialWork(int data, ImageView imageView) { final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView); if (bitmapWorkerTask != null) { final int bitmapData = bitmapWorkerTask.data; if (bitmapData != data) { // Cancel previous task bitmapWorkerTask.cancel(true); } else { // The same work is already in progress return false; } } // No task associated with the ImageView, or an existing task was cancelled return true;}
上面用到的助手函数 getBitmapWorkerTask()
,用来获取和 ImageView
关联的任务:
private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) { if (imageView != null) { final Drawable drawable = imageView.getDrawable(); if (drawable instanceof AsyncDrawable) { final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable; return asyncDrawable.getBitmapWorkerTask(); } } return null;}
最后,需要更新 BitmapWorkerTask
类的 onPostExecute()
函数。
用来检测任务是否取消了,以及当前的任务和 ImageView
引用的任务是否为同一个任务:
class BitmapWorkerTask extends AsyncTask { ... @Override protected void onPostExecute(Bitmap bitmap) { if (isCancelled()) { bitmap = null; } if (imageViewReference != null && bitmap != null) { final ImageView imageView = imageViewReference.get(); final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView); if (this == bitmapWorkerTask && imageView != null) { imageView.setImageBitmap(bitmap); } } }}
这样就可以在 ListView
和 GridView
控件中使用该图片加载任务了。只需要在您设置ImageView
图片的地方调用loadBitmap
函数即可。
例如:在 GridView
中可能需要在Adapter中的 getView()
函数中调用loadBitmap
函数。
Read more: http://blog.chengyunfeng.com/?p=394#ixzz2Ww5pPAPc
Read more: http://blog.chengyunfeng.com/?p=394#ixzz2Ww5wXqK
J
- java内存优化实例----在非UI线程中处理图片
- 位图管理、图片下载缓存、管理图片内存 (三) 在非UI线程中处理位图
- 在非UI线程中处理Bitmap
- Android高性能加载大量图片系列课程2-在非UI线程中处理图片
- Android中高效的显示图片之二——在非UI线程中处理图片
- Android中高效的显示图片之二——在非UI线程中处理图片
- Android中高效的显示图片之二——在非UI线程中处理图片
- Android官方开发文档Training系列课程中文版:高效显示位图之在非UI线程中处理图片
- 在非UI线程处理Bitmap
- 在非UI线程处理Bitmap
- 在非UI线程处理Bitmap
- 在非UI线程处理Bitmap(实用)
- 在非UI线程处理Bitmap
- 在非UI线程中使用Toast
- 在非UI线程中显示Toast
- Android学习路线(三十二)在非UI线程中处理Bitmap
- Android在非UI线程中更新UI的方法
- Android开之在非UI线程中更新UI
- php有关函数
- js 验证身份证号码
- Python tuple 元组详解
- Ant编译打包Android项目
- struts2上传文件类型限制
- java内存优化实例----在非UI线程中处理图片
- linux学习第二课
- C#中Socket多线程编程实例 [转载]
- IO字符流
- Android中的Cursor
- 金融知识复习 1
- sprintf() 和 sscanf()
- IO之字节流
- java内存优化实例----缓存Bitmap