关于android 图片加载优化

Android应用对图片处理算是比较频繁的了,尤其是在程序加载大量图片和高分辨率图片时,最容易产生oom异常,下面是个人平时一些省内存加载方法


方法一:

[java]  view plain  copy
  1. public Bitmap decodeFile(String filePath) {  
  2.         Bitmap bitmap = null;  
  3.         BitmapFactory.Options options = new BitmapFactory.Options();  
  4.         options.inPurgeable = true;  
  5.         try {  
  6.             BitmapFactory.Options.class.getField("inNativeAlloc").setBoolean(  
  7.                     options, true);  
  8.         } catch (IllegalArgumentException e) {  
  9.             e.printStackTrace();  
  10.         } catch (SecurityException e) {  
  11.             e.printStackTrace();  
  12.         } catch (IllegalAccessException e) {  
  13.             e.printStackTrace();  
  14.         } catch (NoSuchFieldException e) {  
  15.             e.printStackTrace();  
  16.         }  
  17.         if (mFilePath != null) {  
  18.             bitmap = BitmapFactory.decodeFile(mFilePath, options);  
  19.         }  
  20.         return bitmap;  
  21.     }  

方法二:

[java]  view plain  copy
  1. public Bitmap ReadBitMap(Context context, int resId){      
  2.   
  3.             BitmapFactory.Options opt = new BitmapFactory.Options();      
  4.   
  5.             opt.inPreferredConfig = Bitmap.Config.RGB_565;   //小于ARGB_8888  样式大小  
  6.   
  7.             opt.inPurgeable = true;      
  8.   
  9.             opt.inInputShareable = true;      
  10.   
  11.             //获取资源图片      
  12.   
  13.             InputStream is = context.getResources().openRawResource(resId);      
  14.   
  15.                 return BitmapFactory.decodeStream(is,null,opt);      
  16.   
  17.         }      


how to calucate bitmap size

[java]  view plain  copy
  1. int calculateBitmapSize(Bitmap candidate,Resources res, int resId){  
  2.         BitmapFactory.Options targetOptions=new BitmapFactory.Options();  
  3.         targetOptions.inJustDecodeBounds = true;  
  4.         BitmapFactory.decodeResource(res, resId, targetOptions);  
  5.         int width = targetOptions.outWidth / targetOptions.inSampleSize;  
  6.         int height = targetOptions.outHeight / targetOptions.inSampleSize;  
  7.         int byteCount = width * height * getBytesPerPixel(candidate.getConfig());  
  8.         return byteCount;  
  9.     }  
  10.     /** 
  11.      * A helper function to return the byte usage per pixel of a bitmap based on its configuration. 
  12.      */  
  13.     static int getBytesPerPixel(Config config) {  
  14.         if (config == Config.ARGB_8888) {  
  15.             return 4;  
  16.         } else if (config == Config.RGB_565) {  
  17.             return 2;  
  18.         } else if (config == Config.ARGB_4444) {  
  19.             return 2;  
  20.         } else if (config == Config.ALPHA_8) {  
  21.             return 1;  
  22.         }  
  23.         return 1;  
  24.     }  


指定尺寸压缩:

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

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


如果你的控件大小小于原始图片大小,那么就需要对图片进行压缩处理,来减少内存使用。

现在知道了原图片的尺寸,根据实际情况决定你要加载它缩小多少倍后的图片。例如你用一个128x96的ImageView显示一张1024x768的原图,根本没有必要把原图读加载到内存。
加载一张缩小后的图片到内存,只需要把BitmapFactory.Options对象的inSampleSize设为true,
然后给inSampleSize设一个值就行了(可以理解inSampleSize为n,图片就缩小到1/n大小)。

[java]  view plain  copy
  1. public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,    
  2.         int reqWidth, int reqHeight) {    
  3.     
  4.     // First decode with inJustDecodeBounds=true to check dimensions    
  5.     final BitmapFactory.Options options = new BitmapFactory.Options();    
  6.     options.inJustDecodeBounds = true;    
  7.     BitmapFactory.decodeResource(res, resId, options);    
  8.     
  9.     // Calculate inSampleSize    
  10.     options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);    
  11.     
  12.     // Decode bitmap with inSampleSize set    
  13.     options.inJustDecodeBounds = false;    
  14.     return BitmapFactory.decodeResource(res, resId, options);    
  15. }    
  16. public static int calculateInSampleSize(    
  17.             BitmapFactory.Options options, int reqWidth, int reqHeight) {    
  18.     // Raw height and width of image    
  19.     final int height = options.outHeight;    
  20.     final int width = options.outWidth;    
  21.     int inSampleSize = 1;    
  22.     
  23.     if (height > reqHeight || width > reqWidth) {    
  24.         if (width > height) {    
  25.             inSampleSize = Math.round((float)height / (float)reqHeight);    
  26.         } else {    
  27.             inSampleSize = Math.round((float)width / (float)reqWidth);    
  28.         }    
  29.     }    
  30.     return inSampleSize;    
  31. }    


官方压缩计算:更新2016-07-16

[java]  view plain  copy
  1. public static int calculateInSampleSize(  
  2.             BitmapFactory.Options options, int reqWidth, int reqHeight) {  
  3.     // Raw height and width of image  
  4.     final int height = options.outHeight;  
  5.     final int width = options.outWidth;  
  6.     int inSampleSize = 1;  
  7.   
  8.     if (height > reqHeight || width > reqWidth) {  
  9.   
  10.         final int halfHeight = height / 2;  
  11.         final int halfWidth = width / 2;  
  12.   
  13.         // Calculate the largest inSampleSize value that is a power of 2 and keeps both  
  14.         // height and width larger than the requested height and width.  
  15.         while ((halfHeight / inSampleSize) >= reqHeight  
  16.                 && (halfWidth / inSampleSize) >= reqWidth) {  
  17.             inSampleSize *= 2;  
  18.         }  
  19.     }  
  20.   
  21.     return inSampleSize;  
  22. }  
  23.   
  24. public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,  
  25.         int reqWidth, int reqHeight) {  
  26.   
  27.     // First decode with inJustDecodeBounds=true to check dimensions  
  28.     final BitmapFactory.Options options = new BitmapFactory.Options();  
  29.     options.inJustDecodeBounds = true;  
  30.     BitmapFactory.decodeResource(res, resId, options);  
  31.   
  32.     // Calculate inSampleSize  
  33.     options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);  
  34.   
  35.     // Decode bitmap with inSampleSize set  
  36.     options.inJustDecodeBounds = false;  
  37.     return BitmapFactory.decodeResource(res, resId, options);  
  38. }  


方法三:使用内存缓存

对于缓存,没有大小或者规则适用于所有应用,它依赖于你分析自己应用的内存使用确定自己的方案。
缓存太小可能只会增加额外的内存使用,缓存太大可能会导致内存溢出或者应用其它模块可使用内存太小

[java]  view plain  copy
  1. private LruCache<String, Bitmap> mMemoryCache;  
  2.   
  3. @Override  
  4. protected void onCreate(Bundle savedInstanceState) {  
  5.     ...  
  6.     // Get max available VM memory, exceeding this amount will throw an  
  7.     // OutOfMemory exception. Stored in kilobytes as LruCache takes an  
  8.     // int in its constructor.  
  9.     final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);  
  10.   
  11.     // Use 1/8th of the available memory for this memory cache.  
  12.     final int cacheSize = maxMemory / 8;  
  13.   
  14.     mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {  
  15.         @Override  
  16.         protected int sizeOf(String key, Bitmap bitmap) {  
  17.             // The cache size will be measured in kilobytes rather than  
  18.             // number of items.  
  19.             return bitmap.getByteCount() / 1024;  
  20.         }  
  21.     };  
  22.     ...  
  23. }  
  24.   
  25. public void addBitmapToMemoryCache(String key, Bitmap bitmap) {  
  26.     if (getBitmapFromMemCache(key) == null) {  
  27.         mMemoryCache.put(key, bitmap);  
  28.     }  
  29. }  
  30.   
  31. public Bitmap getBitmapFromMemCache(String key) {  
  32.     return mMemoryCache.get(key);  
  33. }  


加载压缩后的图片到ImageView显示
[java]  view plain  copy
  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加载图片后,也要把图片缓存到内存中:

[java]  view plain  copy
  1. class BitmapWorkerTask extends AsyncTask {    
  2.     ...    
  3.     // Decode image in background.    
  4.     @Override    
  5.     protected Bitmap doInBackground(Integer... params) {    
  6.         final Bitmap bitmap = decodeSampledBitmapFromResource(    
  7.                 getResources(), params[0], 100100));    
  8.         addBitmapToMemoryCache(String.valueOf(params[0]), bitmap);    
  9.         return bitmap;    
  10.     }    
  11.     ...    
  12. }    

方法四:使用磁盘缓存

你的应用也有可能被其他任务打断,如电话呼入,应用在后台有可能会被结束,这样缓存的数据也会丢失。
当用户回到应用时,所有的图片还需要重新获取一遍。
磁盘缓存可应用到这种场景中,它可以减少你获取图片的次数,当然,从磁盘获取图片比从内存中获取要慢的多,所以它需要在非UI线程中完成。
示例代码中是磁盘缓存的一个实现,在Android4.0源码中(libcore/luni/src/main/Java/libcore/io/DiskLruCache.java),
有更加强大和推荐的一个实现,它的向后兼容使在已发布过的库中很方便使用它


[java]  view plain  copy
  1. private DiskLruCache mDiskCache;  
  2. private static final int DISK_CACHE_SIZE = 1024 * 1024 * 10// 10MB  
  3. private static final String DISK_CACHE_SUBDIR = "thumbnails";  
  4.   
  5. @Override  
  6. protected void onCreate(Bundle savedInstanceState) {  
  7.     ...  
  8.     // Initialize memory cache  
  9.     ...  
  10.     File cacheDir = getCacheDir(this, DISK_CACHE_SUBDIR);  
  11.     mDiskCache = DiskLruCache.openCache(this, cacheDir, DISK_CACHE_SIZE);  
  12.     ...  
  13. }  
  14.   
  15. class BitmapWorkerTask extends AsyncTask {  
  16.     ...  
  17.     // Decode image in background.  
  18.     @Override  
  19.     protected Bitmap doInBackground(Integer... params) {  
  20.         final String imageKey = String.valueOf(params[0]);  
  21.   
  22.         // Check disk cache in background thread  
  23.         Bitmap bitmap = getBitmapFromDiskCache(imageKey);  
  24.   
  25.         if (bitmap == null) { // Not found in disk cache  
  26.             // Process as normal  
  27.             final Bitmap bitmap = decodeSampledBitmapFromResource(  
  28.                     getResources(), params[0], 100100));  
  29.         }  
  30.   
  31.         // Add final bitmap to caches  
  32.         addBitmapToCache(String.valueOf(imageKey, bitmap);  
  33.   
  34.         return bitmap;  
  35.     }  
  36.     ...  
  37. }  
  38.   
  39. public void addBitmapToCache(String key, Bitmap bitmap) {  
  40.     // Add to memory cache as before  
  41.     if (getBitmapFromMemCache(key) == null) {  
  42.         mMemoryCache.put(key, bitmap);  
  43.     }  
  44.   
  45.     // Also add to disk cache  
  46.     if (!mDiskCache.containsKey(key)) {  
  47.         mDiskCache.put(key, bitmap);  
  48.     }  
  49. }  
  50.   
  51. public Bitmap getBitmapFromDiskCache(String key) {  
  52.     return mDiskCache.get(key);  
  53. }  
  54.   
  55. // Creates a unique subdirectory of the designated app cache directory. Tries to use external  
  56. // but if not mounted, falls back on internal storage.  
  57. public static File getCacheDir(Context context, String uniqueName) {  
  58.     // Check if media is mounted or storage is built-in, if so, try and use external cache dir  
  59.     // otherwise use internal cache dir  
  60.     final String cachePath = Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED  
  61.             || !Environment.isExternalStorageRemovable() ?  
  62.                     context.getExternalCacheDir().getPath() : context.getCacheDir().getPath();  
  63.   
  64.     return new File(cachePath + File.separator + uniqueName);  
  65. }  

运行时的配置改变,例如屏幕横竖屏切换了有好的用户体验,你可能不想在这种情况下,重新获取一遍图片。
幸好你可以使用上面讲的内存缓存。缓存可以通过使用一个Fragment(调用setRetainInstance(true)被传到新的Activity,
当新的Activity被创建后,只需要重新附加Fragment,你就可以得到这个Fragment并访问到存在的缓存,把里面的图片快速的显示出来

[java]  view plain  copy
  1. private LruCache mMemoryCache;  
  2.   
  3. @Override  
  4. protected void onCreate(Bundle savedInstanceState) {  
  5.     ...  
  6.     RetainFragment mRetainFragment =  
  7.             RetainFragment.findOrCreateRetainFragment(getFragmentManager());  
  8.     mMemoryCache = RetainFragment.mRetainedCache;  
  9.     if (mMemoryCache == null) {  
  10.         mMemoryCache = new LruCache(cacheSize) {  
  11.             ... // Initialize cache here as usual  
  12.         }  
  13.         mRetainFragment.mRetainedCache = mMemoryCache;  
  14.     }  
  15.     ...  
  16. }  
  17.   
  18. class RetainFragment extends Fragment {  
  19.     private static final String TAG = "RetainFragment";  
  20.     public LruCache mRetainedCache;  
  21.   
  22.     public RetainFragment() {}  
  23.   
  24.     public static RetainFragment findOrCreateRetainFragment(FragmentManager fm) {  
  25.         RetainFragment fragment = (RetainFragment) fm.findFragmentByTag(TAG);  
  26.         if (fragment == null) {  
  27.             fragment = new RetainFragment();  
  28.         }  
  29.         return fragment;  
  30.     }  
  31.   
  32.     @Override  
  33.     public void onCreate(Bundle savedInstanceState) {  
  34.         super.onCreate(savedInstanceState);  
  35.         setRetainInstance(true);  
  36.     }  
  37. }  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值