小结
1:关于内存泄漏的一点点总结。
以前写项目,对于内存的泄漏不太注重,直到有一天。有个朋友写得一个拍照然后裁剪的小demo。为了保证演示的时候,没有问题,他就测试了上百次。就是拍照。然后去裁剪。裁剪完成写个自定义的view把bitmap显示出来。测试了几十次的时候。。崩溃了。。。一看日志,oom..呀。。这个问题有点棘手啊。。于是我们开始了找bug之旅。
拍照的代码:
private void takePhoto() {
File dir=new File(Environment.getExternalStorageDirectory() + "/"+"test");
if(!dir.exists())dir.mkdirs();
Intent intent=new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
localTempImgFileName="test.jpg";
File f=new File(dir, localTempImgFileName);//localTempImgDir和localTempImageFileName是自己定义的名字
Uri u=Uri.fromFile(f);
intent.putExtra(MediaStore.Images.Media.ORIENTATION, 0);
intent.putExtra(MediaStore.EXTRA_OUTPUT, u);
startActivityForResult(intent, CODE_TAKE_PICTUE);
}
a;因为裁剪用的是ucrop这个三方的裁剪库。这个库支持传个uri进去,我们在一开始是直接用拍照时候传进去的u,但是经过测试,在6.0的系统上这个u有时候会为null,于是我们是在onActivityResult里面直接用之前传进的路劲,然后拿到的uri.传进ucrop里面。
代码:
if (requestCode == CODE_TAKE_PICTUE) {
File f = new File(Environment.getExternalStorageDirectory()
+ "/" + "test" + "/" + localTempImgFileName);
Log.e("zmm", "path-->" + f.getAbsolutePath());
Uri u =
Uri.parse(android.provider.MediaStore.Images.Media.insertImage(getContentResolver(),
f.getAbsolutePath(), null, null));
Log.e("zmm", "uri-->" + u.toString())
}
但是我们拍照拿到的uri是原始的图片的uri,所以,我们想要不。我们拿到uri以后先稍微压缩下。然后再传给ucrop。代码:
//压缩
photoBmp = UriUtils.getBitmapFormUri(MainActivity.this, Uri.fromFile(f));
int degree = UriUtils.getBitmapDegree(f.getAbsolutePath());
/**
* 把图片旋转为正的方向
*/
Bitmap newbitmap = UriUtils.rotateBitmapByDegree(photoBmp, degree);
String comPath = Environment.getExternalStorageDirectory()
+ "/" + "test";
String path = UriUtils.saveBitmap(newbitmap, comPath);
Log.e("zmm", "path--->" + path);
// File comfile=new File(path);
Uri comu =
Uri.parse(android.provider.MediaStore.Images.Media.insertImage(getContentResolver(),
path, null, null));
//去裁剪
UCropUtils.startCropActivity(this, comu);
结果很失望。。并不是这个原因。
b:我们又想到有没有可能使我们的bitmap用的太多,而且没有recycle.
于是我们在bitmap用完以后把bitmap都回收了;
回收代码:
if (mBitmap != null && !mBitmap.isRecycled()) {
mBitmap.recycle();
}
这样做了以后,,稳定了一点,跑到了100多次才崩溃。所以,bitmap用完以后一定要回收。不要觉得一次两次没关系。我们要养成这个思想,因为如果你的app是一天24小时运行着的话。资源的不回收。肯定会oom的。
c:但是我们的问题并没有解决。因为还是崩溃了。这个时候,我们学着去看运行时的memory,以前都没看过,不看不要紧。。一看吓半死。。以为。我们可以清楚的看到,每次拍完照,裁剪完成以后返回我们的主界面把该图片显示出来。这个内存都会升高一点。。而且不会下来,,一直升高。。直到oom…所以,,这个问题就很扎心了。。是我们资源没有回收,且一直创建新的对象。。大概知道问题,我们开始找代码里面的致命的错误。终于,我们找到了。。,,我之前说过,我们主界面是一个自定义的view,然后把裁剪完成的图片用canvas画上去的。。我们在代码里面是
MyView myview=new MyView(context);
mylayout.addview(myview)
mylayout是个framLayout,问题就出现在myview。。我们把myview=null。以为就是资源回收了。。每次裁剪完成以后,我们都是
myview=null;
MyView myview=new MyView(context);mylayout.addview(myview);
你讲气不气。。。我们竟然没有把子view从viewgroup里面移除。。。而是直接myview=null。。。。后来我们进到viewgroup.addview方法里面就可以看到,其实viewgroup里面是用的数组来保存所有的子view,我们在addview的时候。viewgroup已经把该view加到数组里面了。所以,我们把view=null.没有任何作用,找到问题以后,我们就好解决了。。我们用viewgroup.removeView这个方法就可以了。这样修改了以后,内存果然下来了。。不过还是会增长一点。这是因为,我们自定义view里面的canvas和画笔什么的都没有回收。这都是优化的问题了。。
至此问题就解决了。。所以,以后我们还是得注意回收资源。。。
2:我们如果遇到负责界面类似电商的很复杂的界面。。那我们应该怎么处理。肯定不能嵌套。。那样会卡顿。阿里有个开源的控件:vlayout.具体的用法请自行搜索。。
3:进行机顶盒开发的时候。用recycleView的时候。请注意,当更新数据的时候。,我们可能会调用adapter.notifydatasetchanged();这个方法,你会发现这样的话。焦点就丢失了。。所以,如果我们是对部分数据进行更新。我们可以调用。adapter.notifyItemChanged();这样焦点就不会丢失。。。
嗯。。每日一语录:
后来,明白。*不是想做什么就做什么,而是, 不想做什么就不做什么。。。哈哈可是我们活在这个世界上。如果大家都随着自己的性子来,这个世界会变成什么样??所以,,我们还是需要世俗一点,还是要看下别人的眼光,听下别人的看法。。愿,大家都能好好的过自己的生活。每天开心的睡着,有所期待的醒来。。。加油吧。。
单曲循环:《钟无艳》