Android之批量載入圖片OOM問題解決方案
一、OOM問題出現的場景和原因
一個好的app總少不了精美的圖片,所以Android開發中圖片的載入總是避免不了的,而在載入圖片過程中,如果處理不當則會出現OOM的問題。那麼如何徹底解決這個問題呢?本文將具體介紹這方面的知識。
首先我們來總結一下,在載入圖片過程中出現的OOM的場景無非就這麼幾種:
1、 載入的圖片過大
2、 一次載入的圖片過多
3、 以上兩種情況兼有
那麼為什麼在以上場景下會出現OOM問題呢?實際上在API文件中有著明確的說明,出現OMM的主要原因有兩點:
1、移動裝置會限制每個app所能夠使用的記憶體,最小為16M,有的裝置分配的會更多,如24、32M、64M等等不一,總之會有限制,不會讓你無限制的使用。
2、在andorid中圖片載入到記憶體中是以點陣圖的方式儲存的,在android2.3之後預設情況下使用ARGB_8888,這種方式下每個畫素要使用4各位元組來儲存。所以載入圖片是會佔用大量的記憶體。
場景和原因我們都分析完了,下面我們來看看如何解決這些問題。
二、解決大圖載入問題
首先先來解決大圖載入的問題,一般在實際應用中展示圖片時,因螢幕尺寸及佈局顯示的原因,我們沒有必要載入原始大圖,只需要按照比例取樣縮放即可。這樣即節省記憶體又能保證圖片不失真,具體實施步驟如下:
1、在不載入圖片內容的基礎上,去解碼圖片得到圖片的尺寸資訊
這裡需要用的BitmapFactory的decode系列方法和BitmapFactory.Options。當使用decode系列方法載入圖片時,一定要將Options的inJustDecodeBounds屬性設定為true。
BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds=true; BitmapFactory.decodeFile(path, options);
2、根據獲取的圖片的尺寸和要展示在介面的尺寸計算縮放比例。
public int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) { // Raw height and width of image final int height = options.outHeight; final int width = options.outWidth; int inSampleSize = 1; if (height > reqHeight || width > reqWidth) { if (width > height) { inSampleSize = Math.round((float) height / (float) reqHeight); } else { inSampleSize = Math.round((float) width / (float) reqWidth); } } return inSampleSize; }
3、根據計算的比例縮放圖片。
//計算圖片的縮放比例 options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); options.inJustDecodeBounds = false; Bitmap bitmap= BitmapFactory.decodeFile(path, options);
根據縮放比例,會比原始大圖節省很多記憶體,效果圖如下:
三、批量載入大圖
下面我們看看如何批量載入大圖,首先第一步還是我們上面所講到的,要根據介面展示圖片控制元件的大小來確定圖片的縮放比例。在此我們使用gridview載入本地圖片為例,具體步驟如下:
1、通過系統提供的contentprovider載入外部儲存器中的所有圖片地址
private void loadPhotoPaths(){ Cursor cursor= getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, null, null, null, null); while(cursor.moveToNext()){ String path = cursor.getString(cursor.getColumnIndex(MediaColumns.DATA)); paths.add(path); } cursor.close(); }
2、自定義adapter,在adapter的getview方法中載入圖片
@Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder=null; if(convertView==null){ convertView = LayoutInflater.from(this.mContext).inflate(R.layout.grid_item_layout, null); holder = new ViewHolder(); holder.photo=(ImageView)convertView.findViewById(R.id.photo); convertView.setTag(holder); }else{ holder=(ViewHolder)convertView.getTag(); } final String path = this.paths.get(position); holder.photo.setImageBitmap(imageLoader.getBitmapFromCache(path)); return convertView; }
通過以上關鍵兩個步驟後,我們發現程式執行後,使用者體驗特別差,半天沒有反應,很明顯這是因為我們在主執行緒中載入大量的圖片,這是不合適的。在這裡我們要將圖片的載入工作放到子執行緒中進行,改造自定義的ImageLoader工具類,為其新增一個執行緒池物件,用來管理用於下載圖片的子執行緒。
private ExecutorService executor; private ImageLoader(Context mContxt) { super(); executor = Executors.newFixedThreadPool(3); } //載入圖片的非同步方法,含有回撥監聽 public void loadImage(final ImageView view, final String path, final int reqWidth, final int reqHeight, final onBitmapLoadedListener callback){ final Handler mHandler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what) { case 1: Bitmap bitmap = (Bitmap)msg.obj; callback.displayImage(view, bitmap); break; default: break; } } }; executor.execute(new Runnable() { @Override public void run() { Bitmap bitmap = loadBitmapInBackground(path, reqWidth, reqHeight); putBitmapInMemey(path, bitmap); Message msg = mHandler.obtainMessage(1); msg.obj = bitmap; mHandler.sendMessage(msg); } }); }
通過改造後使用者體驗明顯好多了,效果圖如下:
雖然效果有所提升,但是在載入過程中還存在兩個比較嚴重的問題:
1、圖片錯位顯示
2、當我們滑動速度過快的時候,圖片載入速度過慢
經過分析原因不難找出,主要是因為我們時候holder快取了grid的item進行重用和執行緒池中的載入任務過多所造成的,只需要對程式稍作修改,具體如下:
Adapter中:
holder.photo.setImageResource(R.drawable.ic_launcher); holder.photo.setTag(path); imageLoader.loadImage(holder.photo, path, DensityUtil.dip2px(80), DensityUtil.dip2px(80), new onBitmapLoadedListener() { @Override public void displayImage(ImageView view, Bitmap bitmap) { String imagePath= view.getTag().toString(); if(imagePath.equals(path)){ view.setImageBitmap(bitmap); } } });
ImageLoader中:
executor.execute(new Runnable() { @Override public void run() { String key = view.getTag().toString(); if (key.equals(path)) { Bitmap bitmap = loadBitmapInBackground(path, reqWidth, reqHeight); putBitmapInMemey(path, bitmap); Message msg = mHandler.obtainMessage(1); msg.obj = bitmap; mHandler.sendMessage(msg); } } });
為了獲得更好的使用者體驗,我們還可以繼續優化,即對圖片進行快取,快取我們可以分為兩個部分記憶體快取磁碟快取,本文例子載入的是本地圖片所有隻進行了記憶體快取。對ImageLoader物件繼續修改,新增LruCache物件用於快取圖片。
private ImageLoader(Context mContxt) { super(); executor = Executors.newFixedThreadPool(3); //將應用的八分之一作為圖片快取 ActivityManager am=(ActivityManager)mContxt.getSystemService(Context.ACTIVITY_SERVICE); int maxSize = am.getMemoryClass()*1024*1024/8; mCache = new LruCache<String, Bitmap>(maxSize){ @Override protected int sizeOf(String key, Bitmap value) { return value.getRowBytes()*value.getHeight(); } }; } //存圖片到快取 public void putBitmapInMemey(String path,Bitmap bitmap){ if(path==null) return; if(bitmap==null) return; if(getBitmapFromCache(path)==null){ this.mCache.put(path, bitmap); } } public Bitmap getBitmapFromCache(String path){ return mCache.get(path); }
在loadImage方法中非同步載入圖片前先從記憶體中取,具體程式碼請下載案例。
四、總結
總結一下解決載入圖片出現OOM的問題主要有以下方法:
1、 不要載入原始大圖,根據顯示控制元件進行比例縮放後載入其縮圖。
2、 不要在主執行緒中載入圖片,主要在listview和gridview中使用非同步載入圖片是要注意處理圖片錯位和無用執行緒的問題。
3、 使用快取,根據實際情況確定是否使用雙快取和快取大小。
小夥伴們看懂了嘛?想要自己測試的,可以點選“下載工程”執行測試哦!
相關文章
- Android 載入大圖片時報OOM的解決方案(原始碼)AndroidOOM原始碼
- Android OOM 排查與解決——圖片載入優化AndroidOOM優化
- Android高效載入大圖、多圖解決方案,有效避免程式OOMAndroid圖解OOM
- android 載入圖片輕鬆避免OOM(out of memory)AndroidOOM
- Android有效解決載入大圖片時記憶體溢位的問題Android記憶體溢位
- 載入圖片的問題
- 圖片載入失敗解決方案 以及canvas即時生成提示圖片Canvas
- Android 圖片高斯模糊解決方案Android
- Android9.0使用Glide載入圖片問題AndroidIDE
- OOM問題解決實踐OOM
- Android 基礎之圖片載入(二)Android
- 解決相鄰圖片之間的空隙問題
- insert into select 批量載入出錯解決方案
- Android ImageLoader框架之圖片載入與載入策略Android框架
- Android 高清載入巨圖方案 拒絕壓縮圖片Android
- android listview 滾動時非同步載入圖片的問題AndroidView非同步
- 前端圖片解決方案前端
- nginx 解決圖片跨域問題Nginx跨域
- Android 圖片載入框架Android框架
- android 載入大量圖片Android
- IE瀏覽器下圖片載入onload事件失效解決方案瀏覽器事件
- Android圖片載入框架Fresco使用詳解Android框架
- service worker輕度探索 - 解決運營活動需求中的圖片載入問題?
- CSS鏤空圖片transition過渡初載入背景色塊問題解決CSS
- 解決圖片訪問403 Forbidden問題ORB
- 載入GIF圖片優化方案優化
- 真的炸了:讓人頭痛的小程式之『圖片懶載入』終極解決方案
- Android圖片解析度dpi的相關問題解決Android
- Android 載入大圖片,不壓縮圖片Android
- 微信登入-6問題解決方案
- 解決Hexo關於圖片的問題Hexo
- Android 高效安全載入圖片Android
- OOM分析之問題一)OOM
- Android圖片載入的框架Fresco使用詳解Android框架
- 重拾圖片純淨之美:Topaz DeNoise AI,一鍵解決圖片降噪問題AI
- Android圖片記憶體溢位的解決方案Android記憶體溢位
- 小程式記憶體問題–圖片懶載入記憶體
- 討論TableLayoutPanel載入緩慢和閃爍問題解決方案