高效地显示Bitmap图片 2 - 在UI线程之外处理Bitmaps

Processing Bitmaps Off the UI Thread [在UI Thread之外处理Bitmap]

  • 在上一课中有介绍一系列的BitmapFactory.decode* 方法,当数据源是网络或者是磁盘时(或者是任何实际源不在内存的),这些方法都不应该在main UI 线程中执行。那些情况下加载数据是不可以预知的,它依赖于许多因素(从网络或者硬盘读取数据的速度, 图片的大小, CPU的速度, etc.)。如果其中任何一个任务卡住了UI
    thread, 系统会出现ANR的错误。
  • 这一节课会介绍如何使用 AsyncTask 在后台线程中处理bitmap并且演示了如何处理并发(concurrency)的问题。

Use an AsyncTask [使用AsyncTask]

  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.     @Override  
  12.     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.     @Override  
  19.     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. }  
  • ImageView使用WeakReference 确保了 AsyncTask 所引用的资源可以被GC(garbage
    collected)。因为当任务结束时不能确保 ImageView 仍然存在,因此你必须在 onPostExecute() 里面去检查引用.
     这个ImageView 也许已经不存在了,例如,在任务结束时用户已经不在那个Activity或者是设备已经发生配置改变(旋转屏幕等)。
  • 开始异步加载位图,只需要创建一个新的任务并执行它即可:
  1. public void loadBitmap(int resId, ImageView imageView) {  
  2.     BitmapWorkerTask task = new BitmapWorkerTask(imageView);  
  3.     task.execute(resId);  
  4. }  

Handle Concurrency [处理并发问题]

  • 通常类似 ListView 与 GridView 等视图组件在使用上面演示的AsyncTask 方法时会同时带来另外一个问题。为了更有效的处理内存,那些视图的子组件会在用户滑动屏幕时被循环使用(关于这个原理请参考【Android】ListView中getView的原理与解决多轮重复调用的方法).
    如果每一个子视图都触发一个 AsyncTask  ,那么就无法确保当前视图在结束时,分配的视图已经进入循环队列中给另外一个子视图进行重用。而且,
    无法确保所有的异步任务能够按顺序执行完毕。
  • Multithreading for Performance 这篇博文更进一步的讨论了如何处理并发并且提供了一种解决方法,当任务结束时ImageView 保存一个最近常使用的AsyncTask引用。(暂时打不开那篇博文,下次试试看,仔细看下)
    。使用类似的方法,  AsyncTask 可以扩展出一个类似的模型.
  • 创建一个专用的 Drawable 子类来保存一个可以回到当前工作任务的引用。
    在这种情况下,BitmapDrawable 被用来作为占位图片,它可以在任务结束时显示到ImageView中。
  1. static class AsyncDrawable extends BitmapDrawable {  
  2.     private final WeakReference bitmapWorkerTaskReference;  
  3.   
  4.     public AsyncDrawable(Resources res, Bitmap bitmap,  
  5.             BitmapWorkerTask bitmapWorkerTask) {  
  6.         super(res, bitmap);  
  7.         bitmapWorkerTaskReference =  
  8.             new WeakReference(bitmapWorkerTask);  
  9.     }  
  10.   
  11.     public BitmapWorkerTask getBitmapWorkerTask() {  
  12.         return bitmapWorkerTaskReference.get();  
  13.     }  
  14. }  
  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() 方法来取消之前的一个任务.
    在小部分情况下, New出来的任务有可能已经存在,这样就不需要执行这个任务了。下面演示了如何实现一个 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 progress  
  11.             return false;  
  12.         }  
  13.     }  
  14.     // No task associated with the ImageView, or an existing task was cancelled  
  15.     return true;  
  16. }  
  • 在上面有一个帮助方法, getBitmapWorkerTask(), 被用作检索任务是否已经被分配到指定的 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() 方法里面做更新操作:
  1. class BitmapWorkerTask extends AsyncTask {  
  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 与 GridView 组件,在那些需要循环利用子视图的组件中同样适用。只需要在设置图片到ImageView的地方调用 loadBitmap 方法。例如,在GridView 中实现这个方法会是在 getView() 方法里面调用。
时间: 2024-11-10 00:51:12

高效地显示Bitmap图片 2 - 在UI线程之外处理Bitmaps的相关文章

高效地显示Bitmap图片 3 - 两种缓存Bitmap的方式

http://blog.csdn.net/kesenhoo/article/details/7491588 Caching Bitmaps [缓存位图] 加载单个Bitmap到UI是简单直接的,但是如果你需要一次加载大量的图片,事情则会变得复杂起来.在大多数情况下(例如在ListView,GridView or ViewPager), 显示图片的数量通常是没有限制的. 通过循环利用子视图可以抑制内存的使用,GC(garbage collector)也会释放那些不再需要使用的bitmap.这些机制

高效地显示Bitmap图片 1 - 有效率地加载大尺寸的位图

http://blog.csdn.net/kesenhoo/article/details/7489243 Loading Large Bitmaps Efficiently [有效地加载大尺寸位图] 图片有不同的形状与大小.在大多数情况下它们的实际大小都比需要呈现出来的要大很多.例如,系统的Gallery程序会显示那些你使用设备camera拍摄的图片,但是那些图片的分辨率通常都比你的设备屏幕分辨率要高很多. 考虑到程序是在有限的内存下工作,理想情况是你只需要在内存中加载一个低分辨率的版本即可.

在非UI线程处理Bitmap

http://my.oschina.net/ryanhoo/blog/88344 译者:Ryan Hoo 来源:https://developer.android.com/develop/index.html 译者按: 在Google最新的文档中,提供了一系列含金量相当高的教程.因为种种原因而鲜为人知,真是可惜!Ryan将会细心整理,将之翻译成中文,希望对开发者有所帮助.         本系列是Google关于展示大Bitmap(位图)的官方演示,可以有效的解决内存限制,更加有效的加载并显示图

位图显示-MFC中OnPaint函数显示BMP图片的问题

问题描述 MFC中OnPaint函数显示BMP图片的问题 为什么我在MFC对话框中,在OnPaint函数中加入以下代码,BMP图像不能显示啊~~· BITMAP bm; CBitmap bmp; bmp.LoadBitmap(IDB_BITMAP1); CDC memdc; CDC dc; memdc.CreateCompatibleDC(&dc);/ bmp.GetBitmap(&bm); CBitmap *bmpold=memdc.SelectObject(&bmp);/ dc

J2ME游戏开发学习之高效漂亮显示积分

显示 游戏中能漂亮地显示积分是很重要的,手机自带的字体无疑太难看了,所以我们需要用图片来代替数字.开发学习之高效漂亮显示积分-"> 这个是图片资源, 新建一个图片数组:Image[] imgNumbers = new Image[10];然后读入即可 把这个函数加入到你的游戏画积分部分就可以了 /**    * 画得到的总分数    * @param g Graphics * @param totalScore    */   private void drawTotalScore(Gra

C#将image中的显示的图片转换成二进制

原文:C#将image中的显示的图片转换成二进制  1.将Image图像文件存入到数据库中 我们知道数据库里的Image类型的数据是"二进制数据",因此必须将图像文件转换成字节数组才能存入数据库中. View Code //将本地图片转换成二进制保存起来 private byte[] SetImageToByteArray(string fileName) { FileStream fs = null; try { fs = new FileStream(fileName, FileM

qtcreator-qt creator5无法显示背景图片

问题描述 qt creator5无法显示背景图片 关于Qmainwindow的添加背景图片,我采用的方法是改变样式表,之前已在资源文件中添加了png格式的图片 这是在ui设计界面的下的模样: 但在点击运行之后: 完全显示不了图片!已经在网上找了很多方法,就是解决不了-- 求大神教QAQ 解决方案 虽然楼上两位没能解决我的问题,但还是谢谢了.目前问题已解决-- 不太明白原理,但解决方法与解决qt creator无法打开文本文件的类似:将qt creator左栏的项目->概要->shadow bu

Android 使用ContentProvider扫描手机中的图片,仿微信显示本地图片效果

转载请注明本文出自xiaanming的博客(http://blog.csdn.net/xiaanming/article/details/18730223),请尊重他人的辛勤劳动成果,谢谢! 写这篇文章之前,先简单说几句,首先是先恭喜下自己获得了2013年的博客之星称号,很意外也很开心,自己是从2013年开始写博客,那时候也不知道怎么写,我从小就不喜欢写日记,作文什么的,所以刚开始都是贴代码,也没有人看,后面慢慢的,写的文章被推荐博客首页和CSDN首页(这里也要小小的感谢下小编MM),访问量逐渐

cimage-MFC dc显示超大图片 ,但比屏幕小的图片显示不出来

问题描述 MFC dc显示超大图片 ,但比屏幕小的图片显示不出来 CRect rect; CImage image; GetWindowRect(&rect); ScreenToClient(&rect); image.Load(m_strRollPhotos[m_stCurrentPhoto].c_str()); int nImgWidth = image.GetWidth(); int nImgHeight =image.GetHeight(); if(nImgHeight==0)re