高效显示Bitmap之UI线程外处理Bitmap
来源:互联网 发布:淘宝图标 编辑:程序博客网 时间:2024/05/16 01:05
如果数据源从硬盘读取或者网络, BitmapFactory.decode*
方法不应该在main线程中执行。加载这些数据的时间是不可预知的,受限于多种因素(硬盘读取速度,图片大小,CPU等等)。如果任一因素锁住了UI线程,系统会标记你的APP无响应,用户会有选择关闭其操作(see Designing for Responsiveness
for more information)。
这节课教你通过AsyncTask
在后台线程加载bitmap以及怎么解决并发性问题。
Use an AsyncTask
AsyncTask提供一个简单的方式在后台处理,之后把结果提交到前台。如何使用,创建一个子类重写方法。下面是一个例子:用AsyncTask和 decodeSampledBitmapFromResource()
方法加载一张大图到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也许不再存在,比如,在线程退出之前,用户离开当前activity或者配置发生改变。开始异步加载Bitmap之前,简单的创建任务并执行即可。
public void loadBitmap(int resId, ImageView imageView) { BitmapWorkerTask task = new BitmapWorkerTask(imageView); task.execute(resId);}
Handle Concurrency
在之前, 在使用AsyncTask
时, 通常 ListView
和 GridView
视图组件会引发另一个问题。为了有效的利用内存,在用户滚动视图时,这些组件会重复利用子布局。如果每个子视图触发一个AsyncTask,没法确保它是否完成,与之绑定的view还没来的及回收利用。此外,无法确保异步任务的完成的次序跟起动时一致。
专门创建一个 Drawable
子类来存储异步任务的引用。这样,当异步任务完成时,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方法检查是否另一个运行着的线程已经与这个IamgeView绑定。如果是, 通过 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以及其他重复利用子View的组件中,用以上方法都是可行的。在ImageView设置图片的地方简单的调用 loadBitmap即可。例如,在GridView组件中的getView()中调用。
0 0
- 高效显示Bitmap之UI线程外处理Bitmap
- 高效显示Bitmap2--->非UI线程处理Bitmap
- Android进阶练习 - 高效显示Bitmap(在UI主线程外处理Bitmap)
- 【Android Training - 09】高效地显示Bitmap图片 [ Lesson 2 - 在UI线程之外处理Bitmaps ]
- 高效地显示Bitmap图片 2 - 在UI线程之外处理Bitmaps
- 高效显示Bitmap之缓存Bitmap
- 在UI线程外处理Bitmap
- 非UI线程处理Bitmap
- 非UI线程处理Bitmap
- 非UI线程处理Bitmap
- 在非UI线程处理Bitmap
- 在非UI线程处理Bitmap
- 在非UI线程处理Bitmap
- 在非UI线程处理Bitmap(实用)
- android 非UI线程处理Bitmap
- 非UI线程处理Bitmap(官方文档)
- 在非UI线程处理Bitmap
- 在非UI线程中处理Bitmap
- FDS - Project2
- 51Nod 1417 天堂里的游戏
- axis2开发webservice教程
- 括号匹配 NYOJ 2
- Ceph--架构概述
- 高效显示Bitmap之UI线程外处理Bitmap
- 在/proc文件系统中添加和删除文件
- Marching Cube(C++ OpenGl代码)读取医学三维图像*.raw进行三维重建
- Oracle-Soft Parse/Hard Parse/Soft Soft Parse解读
- 解决Python下pip install MySQL-python失败的问题
- Levenberg–Marquardt算法学习
- timestamps 字段按天去group by的写法
- logstash使用grok正则解析日志和kibana遇到的问题
- 操作系统(Linux)--按优先数调度算法实现处理器调度