最近公司的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相册的大图预览效果。
源码分析如下:
http://www.cnblogs.com/idealgrass/p/4429798.html
使用示例如下:
/**
* 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的问题了。