Android高效显示图片详解(二)

转载 2013年12月02日 15:10:12

http://blog.csdn.net/zhiying201039/article/details/8665598

 上节课我们介绍了如何加载和显示大图,这节课我们就要把这个技巧与实际开发联系起来,在实际的开发过程中,最常见的场景就是用ListView,GridView等集合显示控件

来呈现图片,这节课,我们就要用这些控件来高效的显示图片。

       实际的使用环境中,如果图片来源是SD卡或者网络,那那么加载图片的过程一定不要放在UI线程中,这样会严重的阻塞UI线程,出现ANR,程序就废了。因此我们首先要实现异步加载。

第一步:利用AsyncTask实现图片的异步加载

将decodeSampledBitmapFromResource方法放入Task的doInBackground中后台执行。不熟悉AsyncTask的同学可以学习AsyncTask的相关知识,这里不再过多介绍。

代码:

[html] view plaincopy
  1. class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {  
  2.     private final WeakReference<ImageView> imageViewReference;  
  3.     private int data = 0;  
  4.   
  5.   
  6.     public BitmapWorkerTask(ImageView imageView) {  
  7.         // Use a WeakReference to ensure the ImageView can be garbage collected  
  8.         imageViewReference = new WeakReference<ImageView>(imageView);  
  9.     }  
  10.   
  11.   
  12.     // Decode image in background.  
  13.     @Override  
  14.     protected Bitmap doInBackground(Integer... params) {  
  15.         data = params[0];  
  16.         return decodeSampledBitmapFromResource(getResources(), data, 100, 100));  
  17.     }  
  18.   
  19.   
  20.     // Once complete, see if ImageView is still around and set bitmap.  
  21.     @Override  
  22.     protected void onPostExecute(Bitmap bitmap) {  
  23.         if (imageViewReference != null && bitmap != null) {  
  24.             final ImageView imageView = imageViewReference.get();  
  25.             if (imageView != null) {  
  26.                 imageView.setImageBitmap(bitmap);  
  27.             }  
  28.         }  
  29.     }  
  30. }  

注意,这里对ImageView使用 WeakReference弱引用的目的是确保 AsyncTask不会妨碍系统对ImageView必要时候的垃圾回收。否则可能会出现内存泄露,同时,我们一定要在Task执行完毕后对ImageView的存在性进行判断,因为不能保证Task执行完毕后,ImageView还会存在。

下来我们按照下面的代码就可以使用这个Task了:
[html] view plaincopy
  1. public void loadBitmap(int resId, ImageView imageView) {  
  2.     BitmapWorkerTask task = new BitmapWorkerTask(imageView);  
  3.     task.execute(resId);  
  4. }  

第二步:处理并发情况


ListView与GridView这种多子视图的控件会出现两个问题,

第一,一个ListView会有众多的ChildView,为了更高效的利用内存,控件会自动回收掉被用户滑动过去,不在当前有显示的ChildView,如果每一个ChildView都开启一个Task去加载图片,这样就不能保证开启Task的ChildView在Task执行完毕后没有被回收掉(很有可能用户滑动到其他地方去了)。


第二,因为每张图片的处理时间是不同的,因此同样不能保证加载完成的次序与开始的次序一致。


下来我们开始着手解决这些问题,我们要让ImageView与Task形成一种绑定的关系。

我们先来创建一个特殊的Drawable,这个Drawable有两个功能,一个是与Task形成一种绑定的关系,另外也充当了ImageView的临时占位图像,该Drawable的代码如下:

[html] view plaincopy
  1. static class AsyncDrawable extends BitmapDrawable {  
  2.     private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;  
  3.   
  4.     public AsyncDrawable(Resources res, Bitmap bitmap,  
  5.             BitmapWorkerTask bitmapWorkerTask) {  
  6.         super(res, bitmap);  
  7.         bitmapWorkerTaskReference =  
  8.             new WeakReference<BitmapWorkerTask>(bitmapWorkerTask);  
  9.     }  
  10.   
  11.     public BitmapWorkerTask getBitmapWorkerTask() {  
  12.         return bitmapWorkerTaskReference.get();  
  13.     }  
  14. }  

在该Drawable中通过弱引用能与对应的Task形成一种一一对应的捆绑关系。

我们可以这样使用它,在执行Task之前,先创建一个对应的Drawable,并把它当成将要呈现实际图片的ImageView占位图片,同时也与ImageView形成了绑定关系。

[html] view plaincopy
  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. }  

当然,我们需要判断下ImageView之前是否已经绑定了,如果之前绑定过但与本次的图片不同,那我们就要按最新的需要从新绑定下,如果之前与现在的一致,则保持原状,不再从新绑定,代码中的cancelPotentialWork就是做这个工作的,其代码如下:

[html] view plaincopy
  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 progress  
  11.             return false;  
  12.         }  
  13.     }  
  14.     // No task associated with the ImageView, or an existing task was cancelled  
  15.     return true;  
  16. }  

[html] view plaincopy
  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. }  

最后,我们在Task的onPostExecute函数中,把加载的图片更新到视图中去,在更新前我们需要检查下Task是否被取消,并且当前的Task是否是那个与ImageView关联的Task,一致则我们把图片更新到ImageView上去,代码如下:

[html] view plaincopy
  1. class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {  
  2.     ...  
  3.   
  4.     @Override  
  5.     protected void onPostExecute(Bitmap bitmap) {  
  6.         if (isCancelled()) {  
  7.             bitmap = null;  
  8.         }  
  9.   
  10.         if (imageViewReference != null && bitmap != null) {  
  11.             final ImageView imageView = imageViewReference.get();  
  12.             final BitmapWorkerTask bitmapWorkerTask =  
  13.                     getBitmapWorkerTask(imageView);  
  14.             if (this == bitmapWorkerTask && imageView != null) {  
  15.                 imageView.setImageBitmap(bitmap);  
  16.             }  
  17.         }  
  18.     }  
  19. }  

最后,实际的使用也相当简单,只需要在你的ListView适配器的getView函数中调用上面的loadBitmap函数就OK了~


下一节我们来说说缓存,加入缓存让这个机制更加强大。。


感谢收看! 多多好评,在此谢过!

相关文章推荐

Android中高效的显示图片之二——在非UI线程中处理图片

在“加载大图”文章中提到的BitmapFactory.decode*方法,如果源数据是在磁盘、网络或其它任何不是在内存中的位置,那么它都不应该在UI线程中执行。因为它的加载时间不可预测且依赖于一系列因...

Android中高效的显示图片之一 ——加载大图

在网上看了不少文章,发现还是官方文档介绍最详细,把重要的东西简单摘要出来。详细可看官方文档地址 ( http://www.bangchui.org/read.php?tid=9 ) 。 在应...

Android官方开发文档Training系列课程中文版:高效显示位图之在非UI线程中处理图片

原文地址:http://android.xsoftlab.net/training/displaying-bitmaps/process-bitmap.html 我们在上节课Load Large B...

【Android Training - 09】高效地显示Bitmap图片 [ Lesson 1 - 有效率地加载大尺寸的位图]

Loading Large Bitmaps Efficiently [有效地加载大尺寸位图] 图片有不同的形状与大小。在大多数情况下它们的实际大小都比需要呈现出来的要大很多。例如,系统的Ga...

【Android Training - 09】高效地显示Bitmap图片 [ Lesson 0 - 章节概览 ]

Displaying Bitmaps Efficiently[高效地显示Bitmaps] 这节课会介绍一些通用的用来处理与加载Bitmap对象的方法,这些技术能够使得不会卡到程序...

【Android Training - 09】高效地显示Bitmap图片 [ Lesson 2 - 在UI线程之外处理Bitmaps ]

Processing Bitmaps Off the UI Thread [在UI Thread之外处理Bitmap] 在上一课中有介绍一系列的BitmapFactory.decode* 方...

【Android Training - 09】高效地显示Bitmap图片 [ Lesson 5 - 使用ViewPager与GridView显示图片 ]

Displaying Bitmaps in Your UI [在UI上呈现Bitmap] 这一课会演示如何运用前面三节课的内容,使用后台线程与Cache机制来加载图片到 ViewPager 与 ...

android高效加载图片实例

  • 2015-05-06 18:03
  • 16.22MB
  • 下载

连载 1 - 深入讨论 Android 关于高效显示图片的问题 - 如何高效的加载大位图

更加详细的说明,可以参阅如下官网地址:http://developer.android.com/training/building-graphics.html    刚开始做 Android 应...
  • swdrt
  • swdrt
  • 2014-05-16 11:07
  • 582
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)