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     
原文地址:https://www.cnblogs.com/all88/p/4679368.html