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();這樣焦點就不會丟失。。。
嗯。。每日一語錄:
後來,明白。自由不是想做什麼就做什麼,而是, 不想做什麼就不做什麼。。。哈哈可是我們活在這個世界上。如果大家都隨着自己的性子來,這個世界會變成什麼樣??所以,,我們還是需要世俗一點,還是要看下別人的眼光,聽下別人的看法。。願,大家都能好好的過自己的生活。每天開心的睡着,有所期待的醒來。。。加油吧。。
單曲循環:《鍾無豔》