zoukankan      html  css  js  c++  java
  • 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     
  • 相关阅读:
    noexcept(c++11)
    右值引用和std::move函数(c++11)
    mint-ui 取值
    apicloud 注意事项
    倒计时
    获取第n天日期
    防止split没有切割的变量报错
    return
    时间戳转为日期
    echarts 中 请求后台改变数据
  • 原文地址:https://www.cnblogs.com/all88/p/4679368.html
Copyright © 2011-2022 走看看