使用LeakCanary遇到的问题 就是不弹出来
今天樓主遇到引用LeakCanary時(shí)代碼跟官網(wǎng)一樣但是就不彈出來(lái)。樓主新建項(xiàng)目就可以正常使用。樓主郁悶半天,現(xiàn)在終于整出來(lái)了。
樓主主工程app引用module為thirdParty,本想為了整潔三方的都扔進(jìn)這個(gè)thirdParty 結(jié)果導(dǎo)致了這個(gè)沒(méi)弄出來(lái)。
1.寫一個(gè)application :
[java] view plaincopy print?2.寫一段讓程序泄露的代碼:
[java] view plaincopy print?
3.添加build.gradle 由于我把所有的依賴都寫在的thirdParty這個(gè)lib中
仔細(xì)觀察一下這個(gè)引用不太一樣:
我們平時(shí)引用都是:
[java] view plaincopy print?而LeakCanary的引用是:
[java] view plaincopy print??
后來(lái)我又寫到了主app中 結(jié)果可以使用了。。。
查了一下他們的含義:
Compile
compile是對(duì)所有的build type以及favlors都會(huì)參與編譯并且打包到最終的apk文件中。
Provided
Provided是對(duì)所有的build type以及favlors只在編譯時(shí)使用,類似eclipse中的external-libs,只參與編譯,不打包到最終apk。
APK
只會(huì)打包到apk文件中,而不參與編譯,所以不能再代碼中直接調(diào)用jar中的類或方法,否則在編譯時(shí)會(huì)報(bào)錯(cuò)
Test compile
Test compile 僅僅是針對(duì)單元測(cè)試代碼的編譯編譯以及最終打包測(cè)試apk時(shí)有效,而對(duì)正常的debug或者release apk包不起作用。
Debug compile
Debug compile 僅僅針對(duì)debug模式的編譯和最終的debug apk打包。
Release compile
Release compile 僅僅針對(duì)Release?模式的編譯和最終的Release?apk打包。
還不太清楚為啥。。。但是給大家提個(gè)醒。
下面轉(zhuǎn)一個(gè)非常好的文章:http://droidyue.com/blog/2016/03/28/android-leakcanary/
懶人可以看我下面的轉(zhuǎn)載
?
Android內(nèi)存泄漏檢測(cè)利器:LeakCanary
是什么?一言以蔽之:LeakCanary是一個(gè)傻瓜化并且可視化的內(nèi)存泄露分析工具
為什么需要LeakCanary?
因?yàn)樗?jiǎn)單,易于發(fā)現(xiàn)問(wèn)題,人人可參與。
- 簡(jiǎn)單:只需設(shè)置一段代碼即可,打開應(yīng)用運(yùn)行一下就能夠發(fā)現(xiàn)內(nèi)存泄露。而MAT分析需要Heap Dump,獲取文件,手動(dòng)分析等多個(gè)步驟。
- 易于發(fā)現(xiàn)問(wèn)題:在手機(jī)端即可查看問(wèn)題即引用關(guān)系,而MAT則需要你分析,找到Path To GC Roots等關(guān)系。
- 人人可參與:開發(fā)人員,測(cè)試測(cè)試,產(chǎn)品經(jīng)理基本上只要會(huì)用App就有可能發(fā)現(xiàn)問(wèn)題。而傳統(tǒng)的MAT方式,只有部分開發(fā)者才有精力和能力實(shí)施。
如何集成
盡量在app下的build.gradle中加入以下依賴
| 1 2 3 4 5 | dependencies { debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3.1' // or 1.4-beta1 releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3.1' // or 1.4-beta1 testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3.1' // or 1.4-beta1 } |
在Application中加入類似如下的代碼
| 1 2 3 4 5 6 7 | public class ExampleApplication extends Application { @Override public void onCreate() { super.onCreate(); LeakCanary.install(this); } } |
到這里你就可以檢測(cè)到Activity的內(nèi)容泄露了。其實(shí)現(xiàn)原理是設(shè)置Application的ActivityLifecycleCallbacks方法監(jiān)控所有Activity的生命周期回調(diào)。內(nèi)部實(shí)現(xiàn)代碼為
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | public final class ActivityRefWatcher { private final ActivityLifecycleCallbacks lifecycleCallbacks = new ActivityLifecycleCallbacks() { public void onActivityCreated(Activity activity, Bundle savedInstanceState) { } public void onActivityStarted(Activity activity) { } public void onActivityResumed(Activity activity) { } public void onActivityPaused(Activity activity) { } public void onActivityStopped(Activity activity) { } public void onActivitySaveInstanceState(Activity activity, Bundle outState) { } public void onActivityDestroyed(Activity activity) { ActivityRefWatcher.this.onActivityDestroyed(activity); } }; private final Application application; private final RefWatcher refWatcher; public static void installOnIcsPlus(Application application, RefWatcher refWatcher) { if(VERSION.SDK_INT >= 14) { ActivityRefWatcher activityRefWatcher = new ActivityRefWatcher(application, refWatcher); activityRefWatcher.watchActivities(); } } .... } |
想要檢測(cè)更多?
首先我們需要獲得一個(gè)RefWatcher,用來(lái)后續(xù)監(jiān)控可能發(fā)生泄漏的對(duì)象
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | public class MyApplication extends Application { private static RefWatcher sRefWatcher; @Override public void onCreate() { super.onCreate(); sRefWatcher = LeakCanary.install(this); } public static RefWatcher getRefWatcher() { return sRefWatcher; } } |
監(jiān)控某個(gè)可能存在內(nèi)存泄露的對(duì)象
| 1 | MyApplication.getRefWatcher().watch(sLeaky); |
哪些需要進(jìn)行監(jiān)控
默認(rèn)情況下,是對(duì)Activity進(jìn)行了檢測(cè)。另一個(gè)需要監(jiān)控的重要對(duì)象就是Fragment實(shí)例。因?yàn)樗虯ctivity實(shí)例一樣可能持有大量的視圖以及視圖需要的資源(比如Bitmap)即在Fragment onDestroy方法中加入如下實(shí)現(xiàn)
| 1 2 3 4 5 6 7 | public class MainFragment extends Fragment { @Override public void onDestroy() { super.onDestroy(); MyApplication.getRefWatcher().watch(this); } } |
其他也可以監(jiān)控的對(duì)象
- BroadcastReceiver
- Service
- 其他有生命周期的對(duì)象
- 直接間接持有大內(nèi)存占用的對(duì)象(即Retained Heap值比較大的對(duì)象)
何時(shí)進(jìn)行監(jiān)控
首先,我們需要明確什么是內(nèi)存泄露,簡(jiǎn)而言之,某個(gè)對(duì)象在該釋放的時(shí)候由于被其他對(duì)象持有沒(méi)有被釋放,因而造成了內(nèi)存泄露。
因此,我們監(jiān)控也需要設(shè)置在對(duì)象(很快)被釋放的時(shí)候,如Activity和Fragment的onDestroy方法。
一個(gè)錯(cuò)誤示例,比如監(jiān)控一個(gè)Activity,放在onCreate就會(huì)大錯(cuò)特錯(cuò)了,那么你每次都會(huì)收到Activity的泄露通知。
如何解決
常用的解決方法思路如下
- 盡量使用Application的Context而不是Activity的
- 使用弱引用或者軟引用
- 手動(dòng)設(shè)置null,解除引用關(guān)系
- 將內(nèi)部類設(shè)置為static,不隱式持有外部的實(shí)例
- 注冊(cè)與反注冊(cè)成對(duì)出現(xiàn),在對(duì)象合適的生命周期進(jìn)行反注冊(cè)操作。
- 如果沒(méi)有修改的權(quán)限,比如系統(tǒng)或者第三方SDK,可以使用反射進(jìn)行解決持有關(guān)系
加入例外
有些特殊情況,我們需要忽略一些問(wèn)題,這時(shí)候就需要添加例外規(guī)則。比如ExampleClass.exampleField會(huì)導(dǎo)致內(nèi)存泄漏,我們想要忽略,如下操作即可。
| 1 2 3 4 5 6 7 8 9 | // ExampleApplication is defined in "Customizing and using the no-op dependency" public class DebugExampleApplication extends ExampleApplication { protected RefWatcher installLeakCanary() { ExcludedRefs excludedRefs = AndroidExcludedRefs.createAppDefaults() .instanceField("com.example.ExampleClass", "exampleField") .build(); return LeakCanary.install(this, DisplayLeakService.class, excludedRefs); } } |
如何實(shí)現(xiàn)的
LeakCanary實(shí)際上就是在本機(jī)上自動(dòng)做了Heap dump,然后對(duì)生成的hprof文件分析,進(jìn)行結(jié)果展示。和手工進(jìn)行MAT分析步驟基本一致。
如何不影響對(duì)外版APK
是的,這個(gè)問(wèn)題確實(shí)值得關(guān)注,因?yàn)長(zhǎng)eakCanary確實(shí)是影響程序運(yùn)行的,尤其是heap dump操作,不過(guò)好在這件事Square已經(jīng)考慮了,即在我們?cè)黾右蕾嚂r(shí)
| 1 2 3 | debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3.1' // or 1.4-beta1 releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3.1' // or 1.4-beta1 testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3.1' // or 1.4-beta1 |
其中releaseCompile和testCompile這兩個(gè)的依賴明顯不同于debugCompile的依賴。它們的依賴屬于NOOP操作。
NOOP,即No Operation Performed,無(wú)操作指令。常用的編譯器技術(shù)會(huì)檢測(cè)無(wú)操作指令并出于優(yōu)化的目的將無(wú)操作指令剔除。
因而,只要配置好releaseCompile和testCompile的依賴,就無(wú)需擔(dān)心對(duì)外版本的性能問(wèn)題了。
實(shí)踐中的問(wèn)題
- 如果targetSdkVersion為23,在6.0的機(jī)器上會(huì)存在問(wèn)題,卡死,因?yàn)長(zhǎng)eakCanary并沒(méi)有很好支持Marshmallow運(yùn)行時(shí)權(quán)限,所以始終得不到sd卡權(quán)限,進(jìn)而導(dǎo)致卡死。
注意
- 目前LeakCanary一次只能報(bào)一個(gè)泄漏問(wèn)題,如果存在內(nèi)存泄漏但不是你的模塊,并不能說(shuō)明這個(gè)模塊沒(méi)有問(wèn)題。建議建議將非本模塊的泄漏解決之后,再進(jìn)行檢測(cè)。
Anroid中內(nèi)存泄漏相關(guān)文章
- 避免Android中Context引起的內(nèi)存泄露
- Android中Handler引起的內(nèi)存泄露
- Google為何這樣設(shè)計(jì)OnSharedPreferenceChangeListener
- Google IO:Android內(nèi)存管理主題演講記錄
- 譯文:理解Java中的弱引用
- 細(xì)話Java:”失效”的private修飾符
總結(jié)
以上是生活随笔為你收集整理的使用LeakCanary遇到的问题 就是不弹出来的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 数组(全部子集)一
- 下一篇: Thinkphp整合各个功能