Android应用开发图片异步加载
来源:互联网 发布:mac 键盘控制声音 编辑:程序博客网 时间:2024/05/22 01:55
众所周知Android应用开发中不能在UI线程中做耗时的操作,否则就会弹出烦人的ANR窗口。
应用开发中如果需要加载来自网络、磁盘或其他非内存中图片资源时,因加载时间会受到其他因素(如磁盘、网络、图片大小、CPU等等)的影响,很容易产生耗时操作。所以在进行类似操作时要避免在UI线程中进行。今天就和大家分享一下如何通过AsyncTask异步加载图片和怎么处理多线程并发问题。
如何使用 AsyncTask加载图片?
通过AysncTask可以很容易的在启动后台线程加载资源,然后将结果返回到UI线程中。使用它时,需要创建它的子类并实现相应的方法,如下是一个通过AysncTask和decodeSampledBitmapFromResource()方法加载一张大图片到ImageView中的例子:
1 class BitmapWorkerTask extends AsyncTask { 2 private final WeakReference imageViewReference; 3 private int data = 0; 4 5 public BitmapWorkerTask(ImageView imageView) { 6 // Use a WeakReference to ensure the ImageView can be garbage collected 7 imageViewReference = new WeakReference(imageView); 8 } 9 10 // Decode image in background.11 @Override12 protected Bitmap doInBackground(Integer... params) {13 data = params[0];14 return decodeSampledBitmapFromResource(getResources(), data, 100, 100));15 }16 17 // Once complete, see if ImageView is still around and set bitmap.18 @Override19 protected void onPostExecute(Bitmap bitmap) {20 if (imageViewReference != null && bitmap != null) {21 final ImageView imageView = imageViewReference.get();22 if (imageView != null) {23 imageView.setImageBitmap(bitmap);24 }25 }26 }27 }
使用WeakReference 保存ImageView的原因,是为了在内存资源紧张时确保AsyncTask 不会阻止对其进行资源回收,因此当task结束时不能保证Imageview还存在,所以你应该在onPostExecute中对它进行验证(本例中在Task结束前如果用户关闭Activity,或系统设置改变时,ImageView可能会被回收)。
通过以下方式我们就可以异步加载图片:
1 public void loadBitmap(int resId, ImageView imageView) {2 BitmapWorkerTask task = new BitmapWorkerTask(imageView);3 task.execute(resId);4 }
如何处理并发操作?
常用的View组件中 像ListView、GridView等 为了高效实用内存,用户在进行View滚动操作时系统会对不再使用子View进行资源回收,,采用上面的方式进行图片加载时会引入另外一个问题。如果在每个子View中开启AsyncTask,不能保证在任务完成时,相关的View是否已经被回收。此外,也不能保证他们加载完成的顺序
我们可以通过将AsyncTask的引用保存ImageView关联Drawable中,任务完成时检查引用是否存在.
创建一个专用的Drawable子类,存储工作任务线程的引用。这样在任务完成时即可将图片设置在ImageView中
1 static class AsyncDrawable extends BitmapDrawable { 2 private final WeakReference bitmapWorkerTaskReference; 3 public AsyncDrawable(Resources res, Bitmap bitmap, 4 BitmapWorkerTask bitmapWorkerTask) { 5 super(res, bitmap); 6 bitmapWorkerTaskReference = 7 new WeakReference(bitmapWorkerTask); 8 } 9 10 public BitmapWorkerTask getBitmapWorkerTask() {11 return bitmapWorkerTaskReference.get();12 }13 }
在执行BitmapTask前,你可以创建AsyncDrawable并将其绑定到ImageView中
1 public void loadBitmap(int resId, ImageView imageView) {2 if (cancelPotentialWork(resId, imageView)) {3 final BitmapWorkerTask task = new BitmapWorkerTask(imageView);4 final AsyncDrawable asyncDrawable =5 new AsyncDrawable(getResources(), mPlaceHolderBitmap, task);6 imageView.setImageDrawable(asyncDrawable);7 task.execute(resId);8 }9 }
上面代码中通过cancelPotentialWork判断是否已经存在正在运行的任务绑定在ImageView中,若有,通过执行任务cancel方法取消它,当然这种情况不常发生,
下面是cancelPotentialWork的实现:
1 public static boolean cancelPotentialWork(int data, ImageView imageView) { 2 final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView); 3 4 if (bitmapWorkerTask != null) { 5 final int bitmapData = bitmapWorkerTask.data; 6 if (bitmapData != data) { 7 // Cancel previous task 8 bitmapWorkerTask.cancel(true); 9 } else {10 // The same work is already in progress11 return false;12 }13 }14 // No task associated with the ImageView, or an existing task was cancelled15 return true;16 }
下面是一个辅助方法,通过ImageView查找与其关联的异步任务;
1 private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) { 2 if (imageView != null) { 3 final Drawable drawable = imageView.getDrawable(); 4 if (drawable instanceof AsyncDrawable) { 5 final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable; 6 return asyncDrawable.getBitmapWorkerTask(); 7 } 8 } 9 return null;10 }
下一步需要在BitmapWorkerTask中的onPostExecute中执行更新操作,
首先检查任务是否取消,如后更行与其关联的ImageView:
1 class BitmapWorkerTask extends AsyncTask { 2 ... 3 @Override 4 protected void onPostExecute(Bitmap bitmap) { 5 if (isCancelled()) { 6 bitmap = null; 7 } 8 if (imageViewReference != null && bitmap != null) { 9 final ImageView imageView = imageViewReference.get();10 final BitmapWorkerTask bitmapWorkerTask =11 getBitmapWorkerTask(imageView);12 if (this == bitmapWorkerTask && imageView != null) {13 imageView.setImageBitmap(bitmap);14 }15 }16 }17 }
通过以上方法,你就可以在ListView、GridView或者其他具有子view回收处理的组件中使用,通过调用
loadBitmap你可以很简单的添加图片到ImageView中,如:在GirdView的 Adapter中的getView方法中调用。
- Android应用开发图片异步加载
- Android应用开发图片异步加载
- Android应用开发图片异步加载
- android开发-异步图片加载(一)
- Android开发ListView控件异步加载图片
- Android开发之ListView异步加载图片
- Android开发实现异步加载图片
- Android开发ListView控件异步加载图片
- android异步加载图片
- android 异步加载图片
- android异步加载图片
- android异步加载图片
- Android 异步加载图片
- Android 异步加载图片
- Android图片异步加载
- Android图片异步加载
- Android图片异步加载
- Android异步加载图片
- Linux TCP网络编程示例
- RTSP协议
- fopen函数
- 实现将student中的所有信息在一个模板页上显示,格式参考课件中的图片。并实现编辑功能。
- 修改Action类不重启tomcat方法 .
- Android应用开发图片异步加载
- flyweight pattern -- 享元模式
- java客户端调用C++ GSoap生成的webservice
- linux的nohup命令的用法
- 24/16/8位bmp图片文件头、信息头的二进制数据
- 关于网络编程的一些笔记
- SAP 收货时,根据信息记录中的价格段来控制收货价格
- More Effective C++读书笔记15
- Mysql,SqlServer,Oracle主键自动增长的设置