Android 加載圖片的優化


  Android應用中常常有加載圖片資源的操作,隨着Android手機平板的分辨率越來越高,圖片資源越來越大,在加載高清圖片的時候,由於瞬間產生大量的內存消耗,有時java GC來不及進行垃圾回收,就很容易發生OOM現象,怎么優化加載圖片呢?本文簡單介紹一下圖片加載的優化。

  方法一:BitmapFactory.Options的兩個參數inPurgeable、inNativeAlloc

  先來看一段神奇的代碼

 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     }

  關於inPurgeable

  處理過位圖加載的人可能對BitmapFactory.Options的inPurgeable參數比較熟悉,當inPurgeable==true時,可以讓java系統內存不足時先行回收部分的內存,這個方法其實已經解決大部分的問題了。

  關於inNativeAlloc

  那么try-catch里面做了什么呢?在看了source code 之后,我發現在BitmapFactory.Options里竟然有一個inNativeAlloc的public變量,可以直接不把使用的內存算到VM里,相應的,inPurgeable生出來的內存還是算在java 的VM里。需要注意的這個變量是個隱藏的變量,不能直接用,需要用反射將這個變量設成true。如此一來bitmap OOM的問題發生的機率又更低了,有需要的人可以參考一下。

  方法二:使用BitmapFactory.decodeStream

  同樣先show code

 

1 public Bitmap ReadBitMap(Context context, int resId){ 2        BitmapFactory.Options opt = new BitmapFactory.Options(); 3        opt.inPreferredConfig = Bitmap.Config.RGB_565; 4        opt.inPurgeable = true; 5        opt.inInputShareable = true; 6       InputStream is = context.getResources().openRawResource(resId); 7        return BitmapFactory.decodeStream(is,null,opt); 8 }

  關於BitmapFactory.decodeStream

  當我們給view設置圖片資源時,使用像 setBackgroundResource,setImageResource,或者 BitmapFactory.decodeResource 這樣的方法來設置一張高清圖片的時候,這些函數在完成decode后,最終都是通過java層的createBitmap來完成的,需要消耗更多內存。而改用先通過BitmapFactory.decodeStream方法,創建出一個bitmap,再將其設為ImageView的source。decodeStream的優勢在於其直接調用JNI>>nativeDecodeAsset()來完成decode,無需再使用java層的createBitmap,從而節省了java層的空間。如果在讀取時加上圖片的Config參數,可以跟有效減少加載的內存,從而跟有效阻止拋out of Memory異常。

  另外,需要特別注意:decodeStream是直接讀取圖片資料的字節碼了, 不會根據機器的各種分辨率來自動適應,使用了decodeStream之后,需要在hdpi和mdpi,ldpi中配置相應的圖片資源,否則在不同分辨率機器上都是同樣大小(像素點數量),顯示出來的大小就不對了。

  方法三:使用內存緩存

  內存緩存很多人都用,但是內存緩存設置需要得當,太小了會導致緩存不夠用,太大了會導致其他應用可用內存減小,也容易造成內存溢出。

 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     // 只使用1/8的空閑內存作為緩存空間.
10     final int cacheSize = maxMemory / 8; 11 
12     mMemoryCache = new LruCache<String, Bitmap>(cacheSize) { 13  @Override 14         protected int sizeOf(String key, Bitmap bitmap) { 15             return bitmap.getByteCount() / 1024; 16  } 17  }; 18  ... 19 } 20 
21 public void addBitmapToMemoryCache(String key, Bitmap bitmap) { 22     if (getBitmapFromMemCache(key) == null) { 23  mMemoryCache.put(key, bitmap); 24  } 25 } 26 
27 public Bitmap getBitmapFromMemCache(String key) { 28     return mMemoryCache.get(key); 29 }

 

  設置好內存緩存區,我們就可以在需要的時候從內存緩存區直接拿到圖片了:

 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.default_drawable);  9         BitmapWorkerTask task = new BitmapWorkerTask(mImageView); 10  task.execute(resId); 11  } 12 }

  當然了,在bitmap是新的圖片時,需要把圖片放到緩存區中去:

 1 class BitmapWorkerTask extends AsyncTask {  2  ...  3  @Override  4     protected Bitmap doInBackground(Integer... params) {  5         final Bitmap bitmap = decodeSampledBitmapFromResource(  6                 getResources(), params[0], 100, 100));  7         addBitmapToMemoryCache(String.valueOf(params[0]), bitmap);  8         return bitmap;  9  } 10  ... 11 }  

  附:別的一些優化思想

  總結了下別的一些優化思想,有的是對圖片進行壓縮,比如說圖片寬高像素大於空間時,就先把圖片進行壓縮,再decodeResource,不過一般應用開發時較少遇到這樣的情況。還有的是使用SDcard卡進行緩存,這樣即使應用被意外退出了,也不會丟失緩存,但是這中間需要應用到IO操作,IO操作向來是比較慢的,也不是很推薦。

  參考: http://blog.csdn.net/yudajun/article/details/9323941

      http://263229365.iteye.com/blog/1562924


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM