1. 程式人生 > >APP效能優化系列:ViewPager載入大圖出現OOM優化

APP效能優化系列:ViewPager載入大圖出現OOM優化

  最近公司的app有一些醫生反饋說:預覽患者傳送的圖片載入的特別慢,並且經常載入不出來。

  仔細分析這個問題的由來,之前客戶端預覽大圖頁載入圖片設定的畫素數是1024*720,即一張圖片佔用的記憶體為:1024*720*2=1.4M(大概).大圖預覽頁面採用的是viewpager,viewpager預設佔用的記憶體為3*1.4M,不易出現OOM。

  後來做了一次大圖預覽調整,最終的解決方案是調整預覽大圖頁載入圖片的畫素改為:1024*2*720*2,即一張圖片佔用的記憶體為:1024*2*720*2*2=5.12M(大概), 大圖預覽頁面採用的是viewpager,viewpager預設佔用的記憶體為3*5.12M,易出現OOM。

  一般情況下Android手機為每個應用分配的記憶體大概是64M。而一個大圖預覽就佔用了15M左右,更別說如果viewpager中有十幾張大圖,如果bitmap沒有得到及時的釋放,就是十幾*5.12M了。OOM就更易出現了。

目前的思考解決方案如下:

解決步驟1:在viewpager destroyitem的時候及時釋放bitmap

來自網路的一段程式碼:

BitmapDrawable bitmapDrawable = (BitmapDrawable) imageView.getDrawable();
            if (bitmapDrawable != null) {
                Bitmap bm = bitmapDrawable.getBitmap
(); if (bm!=null && !bm.isRecycled()) { Log.d("...desimg..", "被回收了" + bm.getByteCount()); imageView.setImageResource(0); bm.recycle(); } }

驗證日誌:

08-11 20:40:52.660 13100-13100/com.haodf.android.doctor
D/...desfrag..: 我執行了 08-11 20:40:52.660 13100-13100/com.haodf.android.doctor D/...desimg..: 我執行了 08-11 20:40:52.660 13100-13100/com.haodf.android.doctor D/...desimg..: 被回收了2646720 2.5m 08-11 20:40:55.140 13100-13100/com.haodf.android.doctor D/...desfrag..: 我執行了 08-11 20:40:55.140 13100-13100/com.haodf.android.doctor D/...desimg..: 我執行了 08-11 20:40:55.140 13100-13100/com.haodf.android.doctor D/...desimg..: 被回收了2764800 08-11 20:40:57.360 13100-13100/com.haodf.android.doctor D/...desfrag..: 我執行了 08-11 20:40:57.360 13100-13100/com.haodf.android.doctor D/...desimg..: 我執行了 08-11 20:40:57.360 13100-13100/com.haodf.android.doctor D/...desimg..: 被回收了4991040 4.7m

有效果。

解決步驟2:讓系統為應用分配更多的記憶體

  目前APP對圖片的質量要求越來越高,已經具備引入largeHeap屬性的條件。如果設定了這個屬性,這樣就能增加系統為當前app分配的記憶體了,甚至到100M以上。可以明顯減少OOM的問題。

解決步驟3:圖片區域性載入

  參考了一些開源的相簿類應用和桌布類應用,發現他們使用了局部載入的技術。這種技術會先將圖片下載到本地,然後去載入,只加載當前可視區域,在手指拖動的時候再去載入另外的區域,可以徹底解決oom的問題,測驗在viewpager的情況下載入10000*10000解析度的圖片毫無壓力。

  這塊有一個非常好的開源第三方可以參考:SubsamplingScaleImageView。這個控制元件支援區域性載入,類似qq相簿的大圖預覽效果。

使用示例如下:

/**
 * Created by niehongtao on 16/8/12.
 */
public class BigImgTestActivity extends BaseActivity {
    @InjectView(R.id.imageView)
    SubsamplingScaleImageView imageView;

    @Override
    protected int getLayoutId() {
        return R.layout.activity_bigimg;
    }

    @Override
    protected void init(Bundle savedInstanceState) {
        super.mLoadingDialog.hideLoading();
        final String testUrl = "http://n2.hdfimg.com/g9/M00/1E/B4/uoYBAFepon-AKjTRANKobLODE_M932.jpg";
        final File downDir = Environment.getExternalStorageDirectory();
        //使用Glide下載圖片,儲存到本地
        Glide.with(this)
                .load(testUrl)
                .asBitmap()
                .into(new SimpleTarget<Bitmap>() {
                    @Override
                    public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> glideAnimation) {
                        File file = new File(downDir, "m_1385635534691.jpg");
                        if (!file.exists()) {
                            try {
                                file.createNewFile();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                        FileOutputStream fout = null;
                        try {
                            //儲存圖片
                            fout = new FileOutputStream(file);
                            resource.compress(Bitmap.CompressFormat.JPEG, 100, fout);
                            // 將儲存的地址給SubsamplingScaleImageView,這裡注意設定ImageViewState
                            imageView.setImage(ImageSource.uri(file.getAbsolutePath()));
                        } catch (FileNotFoundException e) {
                            e.printStackTrace();
                        } finally {
                            try {
                                if (fout != null) fout.close();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                });
    }

    @Override
    protected void initView() {

    }

    @Override
    protected void initData() {

    }
}

相信通過以上三步可以完全杜絕預覽大圖時的OOM的問題了。