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的問題了。