Android bitmap高效显示和优化

第一部分:Bitmap高效显示

应用场景:
有时候我们想在界面上显示一个网络图片或者显示一张本地的图片,
但是图片本身是很大的有几兆,但是显示的位置很小或者说我们可以用更小
的图片来满足这样的需求,如果把整个图片都显示出来会非常的耗内存,甚至可以导致
内存溢出,这就需要我们来处理,如何高效的显示图片,减少内存消耗。

 1 BitmapFactory.Options options = new BitmapFactory.Options();
 2
 3 options.inJustDecodeBounds = true;
 4
 5 BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
 6
 7 int imageHeight = options.outHeight;
 8 int imageWidth = options.outWidth;
 9
10 String imageType = options.outMimeType;

设置 inJustDecodeBounds 属性为true可以在decoding的时候避免内存的分配,
它会返回一个null的bitmap,
但是 outWidth, outHeight 与 outMimeType 还是可以获取。
这个技术可以允许你在构造bitmap之前优先读图片的尺寸与类型。

将本地一张大图片显示到页面,为了节省内存对图片进行压缩
下面的代码是计算压缩的比例:

 1 public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
 2     // Raw height and width of image
 3     final int height = options.outHeight;
 4     final int width = options.outWidth;
 5     int inSampleSize = 1;
 6
 7     if (height > reqHeight || width > reqWidth) {
 8
 9         final int halfHeight = height / 2;
10         final int halfWidth = width / 2;
11
12         // Calculate the largest inSampleSize value that is a power of 2 and keeps both
13         // height and width larger than the requested height and width.
14         while ((halfHeight / inSampleSize) > reqHeight&& (halfWidth / inSampleSize) > reqWidth) {
15             inSampleSize *= 2;
16         }
17     }
18
19     return inSampleSize;
20 }

设置inSampleSize为2的幂是因为decoder最终还是会对非2的幂的数进行向下处理,获取到最靠近2的幂的数。

 1 public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) {
 2
 3     // First decode with inJustDecodeBounds=true to check dimensions
 4     final BitmapFactory.Options options = new BitmapFactory.Options();
 5     options.inJustDecodeBounds = true;
 6     BitmapFactory.decodeResource(res, resId, options);
 7
 8     // Calculate inSampleSize
 9     options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
10
11     // Decode bitmap with inSampleSize set
12     options.inJustDecodeBounds = false;
13     return BitmapFactory.decodeResource(res, resId, options);
14 }

为了使用这个方法,首先需要设置 inJustDecodeBounds 为 true,
把options的值传递过来,然后使用 inSampleSize 的值并设置
inJustDecodeBounds 为 false 来重新Decode一遍。

mImageView.setImageBitmap(decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));

以上就是将一张图片压缩成100*100来显示的,大大的降低了显示原图片所占的内存。

注意:千万要记得,在退出程序,或者退出该界面的时候一定要对生成的bitmap进行回收

// 先判断是否已经回收  
if(bitmap != null && !bitmap.isRecycled()){  
    // 回收并且置为null  
    bitmap.recycle();  
    bitmap = null;  
}  
System.gc();

第二部分:Bitmap缓存

内存缓存:LruCache类特别适合缓存bitmap的任务,保持最近引用的对象在一个强引用的LinkedHashMap中
,在缓存扩张到指定大小之前,移除最近最少使用的成员

创建LruCache缓存

 1     private LruCache<String, Bitmap> mMemoryCache;
 2
 3     @Override
 4     protected void onCreate(Bundle savedInstanceState) {
 5         ...
 6
 7         final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);  //获取系统内存大小
 8
 9
10         final int cacheSize = maxMemory / 8; //设置缓存为内存大小的8分之1
11
12       //初始化缓存
13         mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
14             @Override
15             protected int sizeOf(String key, Bitmap bitmap) {
16
17                 return bitmap.getByteCount() / 1024;
18             }
19         };
20         ...
21     }
22
23     public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
24         if (getBitmapFromMemCache(key) == null) {
25             mMemoryCache.put(key, bitmap);
26         }
27     }
28
29     public Bitmap getBitmapFromMemCache(String key) {
30         return mMemoryCache.get(key);
31     }  

加载缓存中的图片:
    当加载一个bitmap到ImageView中的时候,先检查LruCache
如果找到了一个实体,那就马上更新到ImageView上面,否则使用一个后台线程来处理这张图片:

 1  public void loadBitmap(int resId, ImageView imageView) {
 2         final String imageKey = String.valueOf(resId);
 3
 4         final Bitmap bitmap = getBitmapFromMemCache(imageKey);
 5         if (bitmap != null) {
 6             mImageView.setImageBitmap(bitmap);
 7         } else {
 8             mImageView.setImageResource(R.drawable.image_placeholder);
 9             BitmapWorkerTask task = new BitmapWorkerTask(mImageView);
10             task.execute(resId);
11         }
12     }  

BitmapWorkerTask也需要更新来以便加实体到内存缓存中

 1 //缓存中没有图片就需重新加载
 2     class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
 3         ...
 4         // Decode image in background.
 5         @Override
 6         protected Bitmap doInBackground(Integer... params) {
 7             final Bitmap bitmap = decodeSampledBitmapFromResource(
 8                     getResources(), params[0], 100, 100));
 9             addBitmapToMemoryCache(String.valueOf(params[0]), bitmap);
10             return bitmap;
11         }
12         ...
13     }  

使用磁盘缓存

创建一个磁盘缓存

  1  private DiskLruCache mDiskLruCache;
  2     private final Object mDiskCacheLock = new Object();
  3     private boolean mDiskCacheStarting = true;
  4     private static final int DISK_CACHE_SIZE = 1024 * 1024 * 10; // 10MB
  5     private static final String DISK_CACHE_SUBDIR = "thumbnails";
  6
  7     @Override
  8     protected void onCreate(Bundle savedInstanceState) {
  9         ...
 10         // Initialize memory cache
 11         ...
 12         // Initialize disk cache on background thread
 13         File cacheDir = getDiskCacheDir(this, DISK_CACHE_SUBDIR);  //创建缓存目录
 14         new InitDiskCacheTask().execute(cacheDir);  //创建硬盘缓存
 15         ...
 16     }
 17
 18       //创建硬盘缓存的线程
 19     class InitDiskCacheTask extends AsyncTask<File, Void, Void> {
 20         @Override
 21         protected Void doInBackground(File... params) {
 22             synchronized (mDiskCacheLock) {
 23                 File cacheDir = params[0];
 24                 mDiskLruCache = DiskLruCache.open(cacheDir, DISK_CACHE_SIZE);
 25                 mDiskCacheStarting = false; // Finished initialization
 26                 mDiskCacheLock.notifyAll(); // Wake any waiting threads
 27             }
 28             return null;
 29         }
 30     }
 31
 32       //如果缓存中没有图片就从硬盘加载图片
 33     class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
 34         ...
 35         // Decode image in background.
 36         @Override
 37         protected Bitmap doInBackground(Integer... params) {
 38             final String imageKey = String.valueOf(params[0]);
 39
 40             // Check disk cache in background thread
 41             Bitmap bitmap = getBitmapFromDiskCache(imageKey);
 42
 43             if (bitmap == null) { // Not found in disk cache
 44                 // Process as normal
 45                 final Bitmap bitmap = decodeSampledBitmapFromResource(
 46                         getResources(), params[0], 100, 100));
 47             }
 48
 49             // Add final bitmap to caches
 50             addBitmapToCache(imageKey, bitmap);
 51
 52             return bitmap;
 53         }
 54         ...
 55     }
 56
 57       //添加bitmap到缓存
 58     public void addBitmapToCache(String key, Bitmap bitmap) {
 59         // Add to memory cache as before
 60         if (getBitmapFromMemCache(key) == null) {
 61             mMemoryCache.put(key, bitmap);
 62         }
 63
 64         // Also add to disk cache
 65         synchronized (mDiskCacheLock) {
 66             if (mDiskLruCache != null && mDiskLruCache.get(key) == null) {
 67                 mDiskLruCache.put(key, bitmap);
 68             }
 69         }
 70     }
 71
 72       //获取缓存的bitmap
 73     public Bitmap getBitmapFromDiskCache(String key) {
 74         synchronized (mDiskCacheLock) {
 75             // Wait while disk cache is started from background thread
 76             while (mDiskCacheStarting) {
 77                 try {
 78                     mDiskCacheLock.wait();
 79                 } catch (InterruptedException e) {}
 80             }
 81             if (mDiskLruCache != null) {
 82                 return mDiskLruCache.get(key);
 83             }
 84         }
 85         return null;
 86     }
 87
 88     //创建缓存目录的方法
 89     // Creates a unique subdirectory of the designated app cache directory. Tries to use external
 90     // but if not mounted, falls back on internal storage.
 91     public static File getDiskCacheDir(Context context, String uniqueName) {
 92         // Check if media is mounted or storage is built-in, if so, try and use external cache dir
 93         // otherwise use internal cache dir
 94         final String cachePath =
 95                 Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) ||
 96                         !isExternalStorageRemovable() ? getExternalCacheDir(context).getPath() :
 97                                 context.getCacheDir().getPath();
 98
 99         return new File(cachePath + File.separator + uniqueName);
100     }
101     
时间: 2024-08-01 14:29:20

Android bitmap高效显示和优化的相关文章

【Android开发经验】Bitmap高效显示系列——如何有效的加载大尺寸Bitmap

转载请注明出处:http://blog.csdn.net/zhaokaiqiang1992 Bitmap的处理在Android开发中一直是一个大问题,因为稍不注意,Bitmap就能够吃掉我们的所有内存,然后崩溃退出!但是,只要我们掌握了Bitmap的一些常见处理技巧,就可以有效的避免这个问题,从此不再惧怕Bitmap. 图片有不同的形状与大小.在大多数情况下它们的实际大小都比需要呈现出来的要大很多.例如,系统的Gallery程序会显示那些你使用设备camera拍摄的图片,但是那些图片的分辨率通常

【Android应用开发技术:图像处理】Bitmap显示性能优化分析

作者:郭孝星 微博:郭孝星的新浪微博 邮箱:[email protected] 博客:http://blog.csdn.net/allenwells Github:https://github.com/AllenWells [Android应用开发技术:图像处理]章节列表 Bitmap经常会消耗大量内存而导致程序崩溃,常见的异常如下所示:java.lang.OutofMemoryError:bitmap size extends VM budget,因此为了保证程序的稳定性,我们应该小心处理程序

Android Bitmap 开源图片框架分析(精华三)

主要介绍这三个框架,都挺有名的,其他的框架估计也差不多了 Android-Universal-Image-Loaderhttps://github.com/nostra13/Android-Universal-Image-Loader ImageLoaderhttps://github.com/novoda/ImageLoader Volley(综合框架,包含图片部分)https://github.com/mcxiaoke/android-volley 扯淡时间,可以跳过这段这些开源框架的源码还

Android Bitmap 开源图片框架分析(精华五)

本帖最后由 boredream 于 2014-5-27 09:07 编辑 ImageLoader和Volley图片部分还包括其他大部分图片框架,基本上图片处理都差不多,区别仅在于部分优化了,而优化方面UIL即Universal-Image-Loader框架做的最好,所以这部分章节算是温习一下图片处理以及寻找下其他框架里面一些不一样的图片处理方式(只关注图片方面) 首先是ImageLoaderhttps://github.com/novoda/ImageLoader主要还是分析图片加载的核心代码部

Android Bitmap压缩策略

一.为什么Bitmap需要高效加载? 现在的高清大图,动辄就要好几M,而Android对单个应用所施加的内存限制,只有小几十M,如16M,这导致加载Bitmap的时候很容易出现内存溢出.如下异常信息,便是在开发中经常需要的: java.lang.OutofMemoryError:bitmap size exceeds VM budget 为了解决这个问题,就出现了Bitmap的高效加载策略.其实核心思想很简单.假设通过ImageView来显示图片,很多时候ImageView并没有原始图片的尺寸那

[FMX] Android APP 启动黑屏优化补丁

使用说明 *************************************************** Android APP 启动黑屏优化补丁 作者: Swish, YangYxd 2016.01.16 Version: 1.0.0 QDAC官方群: 250530692 *************************************************** [安装方式] 双击Setup.bat安装补丁文件(安装时会作备份) 双击Uninstall.bat可以恢复. [

Android Bitmap 用法总结

android Bitmap用法总结 Normal 0 7.8 磅 0 2 false false false MicrosoftInternetExplorer4 1.Drawable → Bitmap public static Bitmap drawableToBitmap(Drawable drawable) { Bitmap bitmap = Bitmap .createBitmap( drawable.getIntrinsicWidth(), drawable.getIntrinsi

Android Bitmap 开源图片框架分析(精华四)

disk缓存主要难点在于内存缓存,disk缓存其实比较简单,就是图片加载完成后把图片文件存到本地方便下次使用 同样,先贴一下官方主页的介绍(主页地址见文章最开始处)和内存缓存差不多,根据算法不同提供了几种类别,可以自行通过ImageLoaderConfiguration.discCache(..)设置<ignore_js_op> 硬盘缓存,保存是以文件的形式框架提供了4种类型,具体算法规则不同,看名字我们大概也能知道对应意思 UnlimitedDiscCache                

学习Android之SimpleAdapter显示网络图片

效果图: 此程序主要的知识点是:SimpleAdapter本身是不支持网络图片的, 如果在Map.put(a,b)中 b为一个Bitmap,程序不会报红色字体,而是在控制台输出绿色的字体,如下 05-10 15:46:45.474: I/System.out(846): resolveUri failed on bad bitmap uri: [email protected] 要想实现显示网络图片其实很简单,使用SimpleAdapter中的方法simpleAdapter.setViewBin