上一篇写了什么是OOM还有和OOM相关的一些概念,而这一篇就是OOM的一个案例
(视频讲解请前往慕课网)
OOM问题一般都是发生在加载图片时候的,现在的手机像素越来越高,图片越来越高清,那么所占的内存也就越来越大
下面这个实例将从一下几个功能探索OOM,以及OOM的优化
1.打开一个图片,查看图片所占内存
2.在不影响显示效果的情况下将图片进行缩放,查看所占内存
3.调整图片的清晰度,查看所占内存
4.将图片进行部分显示,通过模拟手指滑动图片,显示不同的部分,查看内存占用
打开手机相册图片,以Bitmap显示,并获取大小
图片的获取是在手机相册,创建 一个Intent,打开手机相册
//打开手机相册的图片的动作
private void getPhonePic() {
Intent intent = new Intent(Intent.ACTION_PICK, null);
intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");
startActivityForResult(intent, 1);
}
在回调的方法中选取图片显示,并得到正常情况下图片的大小
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
try {
//通过回调数据获取所选中图片地址的方法
String path = getRealPathFromURI(data.getData());
file = new File(path);
if (file == null) return;
if (file != null) {
if (file.length() == 0) {
file.delete();
return;
}
}
//打印图片文件的大小
Log.e("TAG", "file:" + file.getName() + ",length:" + file.length());
//将文件转化成Bitmap进行显示
FileInputStream inputStream = new FileInputStream(file);
bitmap = BitmapFactory.decodeStream(inputStream);
img.setImageBitmap(bitmap);
//得到当文件转换成Bitmap后的大小
Log.e("TAG", "bitmap.length:" + bitmap.getByteCount());
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
//获取手机图片地址
public String getRealPathFromURI(Uri contentUri) {
String res = null;
String[] proj = {MediaStore.Images.Media.DATA};
Cursor cursor = this.getContentResolver().query(contentUri, proj, null, null, null);
if (cursor.moveToFirst()) {
int colum_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
res = cursor.getString(colum_index);
}
cursor.close();
return res;
}
用这种方法打开手机图片并显示可以看到以下效果
将图片按照宽高比进行缩放,显示图片
//将图片缩放显示优化图片
private void changePicOpti() {
try {
if (file == null) {
return;
}
BitmapFactory.Options o = new BitmapFactory.Options();
//仅仅获取文件,但 Bitmap为空,后面加上对内存的设置后,则才会为Bitmap对象
o.inJustDecodeBounds = true;
BitmapFactory.decodeStream(new FileInputStream(file), null, o);
//获取图片的宽和高
int width_tmp = o.outWidth;
int height = o.outHeight;
//宽和高比,如果是2,宽高比缩小两倍
int scale = 2;
while (true) {
//不断地缩小图片,直至图片抽样后的宽度小于屏幕宽度
if (width_tmp / scale < SCREEN_WIDTH) break;
scale *= 2;
}
scale /= 2;//最后再倒退一次
//在得到是缩放比的情况下,重新得到bitmap图片
BitmapFactory.Options o2 = new BitmapFactory.Options();
//设置宽高缩放比
o2.inSampleSize = scale;
Log.d("TAG", "bitmap scale:" + scale);
FileInputStream inputStream = new FileInputStream(file);
bitmap = BitmapFactory.decodeStream(inputStream, null, o2);
Log.d("TAG", "scale-bitmap.length:" + bitmap.getByteCount());
img.setImageBitmap(bitmap);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
这个显示的效果和上面差别不大用肉眼是看出不差别的,但是大小却相差很多(681568)
改变图片的配置(高清度)
//RGB的方法
private void changeRGB() {
try {
if (file == null) {
return;
}
BitmapFactory.Options o = new BitmapFactory.Options();
//将图片的配置改成565
o.inPreferredConfig = Bitmap.Config.RGB_565;
FileInputStream inputStream = new FileInputStream(file);
bitmap = BitmapFactory.decodeStream(inputStream, null, o);
Log.e("TAG", "RGB_565-bitmap.length:" + bitmap.getByteCount());
img.setImageBitmap(bitmap);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
只显示图片的一部分,通过拖拽显示图片不同部分
这里是放置了一个按钮实现图片的拖拽效果的,每点击一次按钮图片就会移动
变量shiftpx就是每点击一次向右的按钮,像素值就会加几,这样它就会向右移动了
//部分加载图片的方法
private void setPartload() {
try {
if (file == null) {
return;
}
//获取图片的宽高
FileInputStream inputStream = new FileInputStream(file);
BitmapFactory.Options tmpOption = new BitmapFactory.Options();
tmpOption.inJustDecodeBounds = true;
BitmapFactory.decodeStream(inputStream, null, tmpOption);
int width = tmpOption.outWidth;
int height = tmpOption.outHeight;
//设置显示图片的中心区域
inputStream = new FileInputStream(file);
//这个类会为图片绘制一个矩形的区域,用来显示大的图片的一部分
BitmapRegionDecoder bitmapRegionDecoder = BitmapRegionDecoder.newInstance(inputStream, false);
BitmapFactory.Options options = new BitmapFactory.Options();
//绘制Bitmap所显示的区域,以手机屏幕的中心点截取图片大小
bitmap = bitmapRegionDecoder.decodeRegion(new Rect
(width / 2 - SCREEN_WIDTH / 2 + shiftpx, height / 2 - SCREEN_HEIGHT / 2 + shiftpx,
width / 2 + SCREEN_WIDTH / 2 + shiftpx, height / 2 + SCREEN_HEIGHT / 2), options
);
Log.d("TAG", "partLoad-bitmap.length:" + bitmap.getByteCount());
img.setImageBitmap(bitmap);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
这是不断的点击 向右后的效果能看出不一样吧~
其实方法还有很多种,比如
1.将当前已经不再显示的图片及时回收掉,但是这样比较麻烦
2.将图片设置成一个软引用,内存不够时被回收,但是这样就不知道哪个图片被回收了
但是有一个Lru算法,可以根据图片使用情况(先回收不经常使用的,经常使用的后回收)进行软引用回收,有个大神讲解了是什么使用的
最后附上整章视频的GitHub地址: