一個APP內存泄露問題的解決過程

一、如何發(fā)現(xiàn)內存泄露了

1.打開android studio,運行APP,android studio底部欄選擇 “Android Monitor”的“Monitors”視圖

2.在Monitors界面的上部分,左邊下拉框選擇運行APP的手機或模擬器,右邊下拉框選擇要調試的APP進程。

3.在Monitors界面的中間部分重點關注“Memory”這一塊的內存值的變化。

? 當打開一個Activity后,已分配內存“Allocated”值會變大,再退出,按一下gc按鈕

此時Activity正常情況下應該會被回收,已分配內存值“Allocated”應該會恢復成打開之前的值。

4.生成hprof文件進行驗證與分析

點擊“Dump java Heap”生成hprof文件

大概5秒后,hprof文件會被自動生成,并自動顯示在代碼瀏覽區(qū)域

此視圖會顯示對象的類型與實例個數(shù),我們可以按包名進行分類,這樣更方便查找自己定義的類

二、通過hprof文件分析內存泄露

用Package Tree View分類,能很快找到我們需要分析的Activity

(本APP的 launcher Activity點擊進去,第二級的Activity名為MainActivity,當按返回按鈕后,MainActivity正常情況下要被回收,我們正是分析MainActivity為什么發(fā)生內存泄露)

我在launcher Activity里點擊進MainActivity 4次并返回,通過上圖可以發(fā)現(xiàn),回到launcher Activity后,MainActivty每次創(chuàng)建的Activity實例在返回后并沒有被回收,如果這樣重復操作很多次,程序肯定會因內存不足而崩潰。

點擊上圖的右上“Instance”區(qū)域里的某一個實例,在下方“Reference Tree”區(qū)域里會列出所有持有該實例的引用對象。


只靠人工分析hprof文件是否能找出內存泄露點?

??????? 本APP的MainActivity非常復雜,所有子界面都采用的是Fragment來進行切換,持有MainActivity引用的其它對象眾多,所以只靠人工這樣去看,很難發(fā)現(xiàn)問題所在。有人建議使用MAT工具打開hprof文件進行分析,本篇有更簡單的方法,即使用LeakCanary工具。

三、使用LeakCanary工具查找內存泄漏

1.進入“https://github.com/square/leakcanary”查看LeakCanary的最新版本與使用方法

最新版本是1.5.1,使用方法很簡單,只有兩步。

2.集成LeakCanary到APP中

第一步:build.gradle里添加依賴

debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5.1'
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.1'
testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.1'

第二步:在自定義Application中初始化

public class ExampleApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
if (LeakCanary.isInAnalyzerProcess(this)) {
// This process is dedicated to LeakCanary for heap analysis.
// You should not init your app in this process.
return;
}
LeakCanary.install(this);
// Normal app init code...
}
}

OK。

3.重新RUN APP,在模擬器中進行測試

?? 我進入MainActivity4次,做一些操作,最后每次都返回到launcher Activity中,不一會兒,模擬器標題欄收到4個通知圖標

下拉點擊某一個

4.進入details界面

上圖中MainActivity對象生成后,從下到上一直追述到 android.view.inputmethod.InputMethodManager$ControlledInputConnectionWrapper.mMainLooper,

同時我們可以查看logcat

顯示android.view.inputmethod.InputMethodManager$ControlledInputConnectionWrapper.mMainLooper為GC ROOT,正是因為MainActivtiy被GC ROOT所持有,所以它不能被收回,發(fā)生內存泄露。

5.分析leaks details界面

?????? 里面引用MainActivity實例的都是系統(tǒng)對象,而且引用鏈條顯示是直接的引用,換句話就是說,如果在MainActivity里有一個Fragment,F(xiàn)ragment里面的ImageView引用了MainActivity,那么此leaks details界面的引用鏈不會把Fragment實例顯示出來,只會顯示ImageView實例。

?????? 這樣又增加了分析的難度,但是仔細分析,我們發(fā)現(xiàn)中間有一個

?????? references android.support.v7.widget.AppCompatImageView.mContext

?說明是某個ImageView持有MainActivity引用沒有被釋放,而又有一條

????? references android.animation.ValueAnimator$AnimationHandler.mAnimations

說明很可能是ImageView執(zhí)行了屬性動畫,導致了內存泄露。

???? 分析到此處,我們大概鎖定了內存泄露的點,由于代碼很多,去一個一個找有點麻煩,那么就在項目里用關鍵字“ValueAnimator”或“ObjectAnimator”進行全局搜索??

??? 終于在“CustomProgressDialog.java”查找到了動畫的使用,ivIcon剛好是一個ImageView實例

private void startPropertyAnim() {
// 第二個參數(shù)"rotation"表明要執(zhí)行旋轉
// 0f -> 360f,從旋轉360度,也可以是負值,負值即為逆時針旋轉,正值是順時針旋轉。
ObjectAnimator anim = ObjectAnimator.ofFloat(ivIcon, "rotation", 0f, 360f);
anim.setRepeatCount(INFINITE);
// 動畫的持續(xù)時間,執(zhí)行多久?
anim.setDuration(5000);
anim.setInterpolator(new LinearInterpolator());

// 正式開始啟動執(zhí)行動畫
anim.start();
}

6.分析定位出的代碼,修正

CustomProgressDialog是一個自定義對話框,對話框顯示時,ImageView會執(zhí)行旋轉動畫,但是對話框消失時,動畫并沒有被取消,導致了內存泄露。最后進行修正

private void startPropertyAnim() {
// 第二個參數(shù)"rotation"表明要執(zhí)行旋轉
// 0f -> 360f,從旋轉360度,也可以是負值,負值即為逆時針旋轉,正值是順時針旋轉。
if (anim != null){
anim.cancel();
}

anim = ObjectAnimator.ofFloat(ivIcon, "rotation", 0f, 360f);
anim.setRepeatCount(INFINITE);
// 動畫的持續(xù)時間,執(zhí)行多久?
anim.setDuration(5000);
anim.setInterpolator(new LinearInterpolator());

// 正式開始啟動執(zhí)行動畫
anim.start();
}

@Override
public void dismiss() {
super.dismiss();
if (anim != null){
anim.cancel();
}
}

最后重新運行修改的程序,測試,發(fā)現(xiàn)內存泄露成功解決。

總結:

?????? ?當項目比較小,代碼量不多時,可能人工檢查一下,就能解決內存泄露的問題,但是當項目越來越龐大,代碼量非常大時,就需要利用工具來幫助進行檢查。就像上面這個問題,在沒有利用工具的情況下,本人花了大量時間看代碼都沒有檢查出來,而利用工具,很好的進行了定位,查找起來方向性非常明確,最后順利解決隱藏很深的一個內存泄露點。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,324評論 25 708
  • 內存管理的目的就是讓我們在開發(fā)中怎么有效的避免我們的應用出現(xiàn)內存泄漏的問題。內存泄漏大家都不陌生了,簡單粗俗的講,...
    宇宙只有巴掌大閱讀 2,493評論 0 12
  • 被文同時發(fā)布在CSDN上,歡迎查看。 APP內存的使用,是評價一款應用性能高低的一個重要指標。雖然現(xiàn)在智能手機的內...
    大圣代閱讀 4,976評論 2 54
  • 讀牌1: 云端,紅色翅膀的大天使張開雙臂,仿佛一切盡在他掌握。他閉著眼睛,表情平靜詳和; 讀牌2: 一對男女裸裎相...
    宋佳Sabrina閱讀 282評論 0 1
  • 我期望著一場離去可是對不起生養(yǎng)我的大地我眷戀你因為你的呼吸殘留著我的愛意那我將走過許多地方之后變成一棵樹或者一朵花...
    三寸之命閱讀 247評論 0 1

友情鏈接更多精彩內容