前言
LeakCanary是Square公司提供的用于Android檢測內(nèi)存的小工具,他能幫助我們快速定位代碼隱藏的BUG,減少OOM的機(jī)會(huì)抛寝。
此處為git地址鏈接:https://github.com/square/leakcanary
題外話:
Square真的是家良心公司,提供了很多有名的組件。后續(xù)會(huì)整理目前市面上有名的組件。比如Facebook的開源組件... 現(xiàn)在先介紹下Square有哪些開源組件
OKHttp 一個(gè)開源穩(wěn)定的Http的通信依賴庫蟆肆,感覺比HttpUrlConnection好用,
okhttp現(xiàn)在已經(jīng)得到Google官方的認(rèn)可了晦款。
okie OKHttp依賴這個(gè)庫
dagger 快速依賴注入框架⊙坠Γ現(xiàn)在已經(jīng)由google公司維護(hù)了,
現(xiàn)在應(yīng)該是dagger2.官網(wǎng)地址:https://google.github.io/dagger/
picasso 一個(gè)圖片緩存庫缓溅,可以實(shí)現(xiàn)圖片的下載和緩存功能
retrofit 是一個(gè)RESTFUL(自行百度)的Http網(wǎng)絡(luò)請求框架的封裝蛇损,基于OKHttp,retrofit在于對接口的封裝,實(shí)質(zhì)是使用OKHttp進(jìn)行網(wǎng)絡(luò)請求
leakcanary 一個(gè)檢測內(nèi)存的小工具淤齐,本文說的就是這個(gè)
otto Android事件總線股囊,降低代碼的耦合性,可以跟EventBus做比較
...
回到正文更啄,現(xiàn)在開始講解LeakCanary的使用
使用LeakCanary
其實(shí)可以參考leakcanary的Sample介紹
- 首先在build.gradle中引用
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:25.2.0'
compile 'com.android.support.constraint:constraint-layout:1.0.0-alpha9'
testCompile 'junit:junit:4.12'
// LeakCanary
debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5'
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5'
testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5'
}
- 在Application的onCreate添加方法
public class ExampleApp extends Application{
@Override
public void onCreate() {
super.onCreate();
// LeakCanary初始化
LeakCanary.install(this);
}
}
- 在App中添加內(nèi)存泄漏代碼稚疹,本文就參照sample中的的例子,寫了一個(gè)SystemClock.sleep(20000);
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button asynTaskBtn = (Button) this.findViewById(R.id.async_task);
asynTaskBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startAsyncTask();
}
});
}
private void startAsyncTask() {
// This async task is an anonymous class and therefore has a hidden reference to the outer
// class MainActivity. If the activity gets destroyed before the task finishes (e.g. rotation),
// the activity instance will leak.
new AsyncTask<Void, Void, Void>() {
@Override protected Void doInBackground(Void... params) {
// Do some slow work in background
SystemClock.sleep(20000);
return null;
}
}.execute();
}
}
-
運(yùn)行祭务,會(huì)發(fā)現(xiàn)出現(xiàn)一個(gè)LeakCanary的圖標(biāo)内狗。后續(xù)會(huì)介紹這個(gè)圖標(biāo)是如何出現(xiàn)的,當(dāng)出現(xiàn)內(nèi)存泄漏的時(shí)候义锥,會(huì)在通知欄顯示一條內(nèi)存泄漏通知柳沙,點(diǎn)擊通知會(huì)進(jìn)入內(nèi)存泄漏的具體問題。
LeakCanary圖標(biāo)
根據(jù)圖標(biāo)顯示我們能夠看出內(nèi)存泄漏在AsyncTask中拌倍,根據(jù)AsyncTask中進(jìn)行內(nèi)存修改
講解完如何使用赂鲤,現(xiàn)在開始講解LeakCanary。
LeakCanary詳解
代碼目錄結(jié)構(gòu)
.
├── AbstractAnalysisResultService.java
├── ActivityRefWatcher.java -- Activity監(jiān)控者柱恤,監(jiān)控其生命周期
├── AndroidDebuggerControl.java --Android Debug控制開關(guān)蛤袒,就是判斷Debug.isDebuggerConnected()
├── AndroidExcludedRefs.java -- 內(nèi)存泄漏基類
├── AndroidHeapDumper.java --生成.hrpof的類
├── AndroidWatchExecutor.java -- Android監(jiān)控線程,延遲5s執(zhí)行
├── DisplayLeakService.java -- 顯示通知欄的內(nèi)存泄漏膨更,實(shí)現(xiàn)了AbstractAnalysisResultService.java
├── LeakCanary.java --對外類,提供install(this)方法
├── ServiceHeapDumpListener.java
└── internal --這個(gè)文件夾用于顯示內(nèi)存泄漏的情況(界面相關(guān))
├── DisplayLeakActivity.java --內(nèi)存泄漏展示的Activity
├── DisplayLeakAdapter.java
├── DisplayLeakConnectorView.java
├── FutureResult.java
├── HeapAnalyzerService.java 在另一個(gè)進(jìn)程啟動(dòng)的Service,用于接收數(shù)據(jù)并發(fā)送數(shù)據(jù)到界面
├── LeakCanaryInternals.java
├── LeakCanaryUi.java
└── MoreDetailsView.java
對外方法LeakCanary.install(this)
實(shí)際上LeakCanary對外提供的方法只有
LeakCanary.install(this);
從這里開始切入缴允,對應(yīng)源碼
/**
* Creates a {@link RefWatcher} that works out of the box, and starts watching activity
* references (on ICS+).
*/
public static RefWatcher install(Application application) {
return install(application, DisplayLeakService.class,
AndroidExcludedRefs.createAppDefaults().build());
}
/**
* Creates a {@link RefWatcher} that reports results to the provided service, and starts watching
* activity references (on ICS+).
*/
public static RefWatcher install(Application application,
Class<? extends AbstractAnalysisResultService> listenerServiceClass,
ExcludedRefs excludedRefs) {
// 判斷是否在Analyzer進(jìn)程
if (isInAnalyzerProcess(application)) {
return RefWatcher.DISABLED;
}
// 允許顯示內(nèi)存泄漏情況Activity
enableDisplayLeakActivity(application);
HeapDump.Listener heapDumpListener =
new ServiceHeapDumpListener(application, listenerServiceClass);
RefWatcher refWatcher = androidWatcher(application, heapDumpListener, excludedRefs);
ActivityRefWatcher.installOnIcsPlus(application, refWatcher);
return refWatcher;
}
為什么LeakCanary要求在4.0以上
<mark>通過注釋能看出這個(gè)LeakCanary是用于4.0以上的方法
references (on ICS+).
為什么要使用在4.0以上呢荚守?
ActivityRefWatcher.installOnIcsPlus(application, refWatcher);
這句方法告訴我們這個(gè)是用在Ics+(即4.0版本以上),那這個(gè)類ActivityRefWatcher
具體使用來干什么的呢
@TargetApi(ICE_CREAM_SANDWICH) public final class ActivityRefWatcher {
public static void installOnIcsPlus(Application application, RefWatcher refWatcher) {
if (SDK_INT < ICE_CREAM_SANDWICH) {
// If you need to support Android < ICS, override onDestroy() in your base activity.
return;
}
ActivityRefWatcher activityRefWatcher = new ActivityRefWatcher(application, refWatcher);
activityRefWatcher.watchActivities();
}
private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
new Application.ActivityLifecycleCallbacks() {
@Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
}
@Override public void onActivityStarted(Activity activity) {
}
@Override public void onActivityResumed(Activity activity) {
}
@Override public void onActivityPaused(Activity activity) {
}
@Override public void onActivityStopped(Activity activity) {
}
@Override public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override public void onActivityDestroyed(Activity activity) {
ActivityRefWatcher.this.onActivityDestroyed(activity);
}
};
private final Application application;
private final RefWatcher refWatcher;
/**
* Constructs an {@link ActivityRefWatcher} that will make sure the activities are not leaking
* after they have been destroyed.
*/
public ActivityRefWatcher(Application application, final RefWatcher refWatcher) {
this.application = checkNotNull(application, "application");
this.refWatcher = checkNotNull(refWatcher, "refWatcher");
}
void onActivityDestroyed(Activity activity) {
refWatcher.watch(activity);
}
public void watchActivities() {
// Make sure you don't get installed twice.
stopWatchingActivities();
application.registerActivityLifecycleCallbacks(lifecycleCallbacks);
}
public void stopWatchingActivities() {
application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks);
}
}
application.registerActivityLifecycleCallbacks(lifecycleCallbacks);這個(gè)方法使用在Android4.0上的练般,用于觀察Activity的生命周期矗漾。從上面代碼看出,LeakCanary監(jiān)聽Activity的銷毀操作
ActivityRefWatcher.this.onActivityDestroyed(activity);
LeakCanary如何出現(xiàn)LeakCanry的圖標(biāo)
public static void setEnabled(Context context, final Class<?> componentClass,
final boolean enabled) {
final Context appContext = context.getApplicationContext();
// 耗時(shí)操作
executeOnFileIoThread(new Runnable() {
@Override
public void run() {
ComponentName component = new ComponentName(appContext, componentClass);
PackageManager packageManager = appContext.getPackageManager();
int newState = enabled ? COMPONENT_ENABLED_STATE_ENABLED : COMPONENT_ENABLED_STATE_DISABLED;
// Blocks on IPC.
packageManager.setComponentEnabledSetting(component, newState, DONT_KILL_APP);
}
});
}
在install的方法執(zhí)行的時(shí)候調(diào)用了
// 允許顯示內(nèi)存泄漏情況Activity
enableDisplayLeakActivity(application);
這個(gè)方法執(zhí)行了上面顯示的方法setEnable.最核心的方法是packageManager.setComponentEnabledSetting薄料。
這個(gè)方法可以用來隱藏/顯示應(yīng)用圖標(biāo)
具體可以參照android 禁用或開啟四大組件setComponentEnabledSetting
[重點(diǎn)]LeakCanary如何捕獲內(nèi)存泄漏
通過Debug.dumpHprofData()方法生成.hprof文件,然后利用開源庫HAHA
(開源地址:https://github.com/square/haha)解析.hprof文件敞贡,并發(fā)送給DisplayLeakActivity進(jìn)行展示
public final class AndroidHeapDumper implements HeapDumper {
private static final String TAG = "AndroidHeapDumper";
private final Context context;
private final Handler mainHandler;
public AndroidHeapDumper(Context context) {
this.context = context.getApplicationContext();
mainHandler = new Handler(Looper.getMainLooper());
}
@Override public File dumpHeap() {
if (!isExternalStorageWritable()) {
Log.d(TAG, "Could not dump heap, external storage not mounted.");
}
File heapDumpFile = getHeapDumpFile();
if (heapDumpFile.exists()) {
Log.d(TAG, "Could not dump heap, previous analysis still is in progress.");
// Heap analysis in progress, let's not put too much pressure on the device.
return NO_DUMP;
}
FutureResult<Toast> waitingForToast = new FutureResult<>();
showToast(waitingForToast);
if (!waitingForToast.wait(5, SECONDS)) {
Log.d(TAG, "Did not dump heap, too much time waiting for Toast.");
return NO_DUMP;
}
Toast toast = waitingForToast.get();
try {
Debug.dumpHprofData(heapDumpFile.getAbsolutePath());
cancelToast(toast);
return heapDumpFile;
} catch (IOException e) {
cleanup();
Log.e(TAG, "Could not perform heap dump", e);
// Abort heap dump
return NO_DUMP;
}
}
/**
* Call this on app startup to clean up all heap dump files that had not been handled yet when
* the app process was killed.
*/
public void cleanup() {
LeakCanaryInternals.executeOnFileIoThread(new Runnable() {
@Override public void run() {
if (isExternalStorageWritable()) {
Log.d(TAG, "Could not attempt cleanup, external storage not mounted.");
}
File heapDumpFile = getHeapDumpFile();
if (heapDumpFile.exists()) {
Log.d(TAG, "Previous analysis did not complete correctly, cleaning: " + heapDumpFile);
heapDumpFile.delete();
}
}
});
}
private File getHeapDumpFile() {
return new File(storageDirectory(), "suspected_leak_heapdump.hprof");
}
private void showToast(final FutureResult<Toast> waitingForToast) {
mainHandler.post(new Runnable() {
@Override public void run() {
final Toast toast = new Toast(context);
toast.setGravity(Gravity.CENTER_VERTICAL, 0, 0);
toast.setDuration(Toast.LENGTH_LONG);
LayoutInflater inflater = LayoutInflater.from(context);
toast.setView(inflater.inflate(R.layout.leak_canary_heap_dump_toast, null));
toast.show();
// Waiting for Idle to make sure Toast gets rendered.
Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
@Override public boolean queueIdle() {
waitingForToast.set(toast);
return false;
}
});
}
});
}
private void cancelToast(final Toast toast) {
mainHandler.post(new Runnable() {
@Override public void run() {
toast.cancel();
}
});
}
}
檢測時(shí)機(jī)
在Activity銷毀的時(shí)候會(huì)執(zhí)行RefWatch.watch方法,然后就去執(zhí)行內(nèi)存檢測
這里又看到一個(gè)比較少的用法摄职,IdleHandler誊役,IdleHandler的原理就是在messageQueue因?yàn)榭臻e等待消息時(shí)給使用者一個(gè)hook。那AndroidWatchExecutor會(huì)在主線程空閑的時(shí)候谷市,派發(fā)一個(gè)后臺(tái)任務(wù)蛔垢,這個(gè)后臺(tái)任務(wù)會(huì)在DELAY_MILLIS時(shí)間之后執(zhí)行。LeakCanary設(shè)置的是5秒迫悠。
public void watch(Object watchedReference, String referenceName) {
checkNotNull(watchedReference, "watchedReference");
checkNotNull(referenceName, "referenceName");
if (debuggerControl.isDebuggerAttached()) {
return;
}
final long watchStartNanoTime = System.nanoTime();
String key = UUID.randomUUID().toString();
retainedKeys.add(key);
final KeyedWeakReference reference =
new KeyedWeakReference(watchedReference, key, referenceName, queue);
watchExecutor.execute(new Runnable() {
@Override public void run() {
ensureGone(reference, watchStartNanoTime);
}
});
}
public final class AndroidWatchExecutor implements Executor {
static final String LEAK_CANARY_THREAD_NAME = "LeakCanary-Heap-Dump";
private static final int DELAY_MILLIS = 5000;
private final Handler mainHandler;
private final Handler backgroundHandler;
public AndroidWatchExecutor() {
mainHandler = new Handler(Looper.getMainLooper());
HandlerThread handlerThread = new HandlerThread(LEAK_CANARY_THREAD_NAME);
handlerThread.start();
backgroundHandler = new Handler(handlerThread.getLooper());
}
@Override public void execute(final Runnable command) {
if (isOnMainThread()) {
executeDelayedAfterIdleUnsafe(command);
} else {
mainHandler.post(new Runnable() {
@Override public void run() {
executeDelayedAfterIdleUnsafe(command);
}
});
}
}
private boolean isOnMainThread() {
return Looper.getMainLooper().getThread() == Thread.currentThread();
}
private void executeDelayedAfterIdleUnsafe(final Runnable runnable) {
// This needs to be called from the main thread.
Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
@Override public boolean queueIdle() {
backgroundHandler.postDelayed(runnable, DELAY_MILLIS);
return false;
}
});
}
}
Fragment如何使用LeakCanary
如果我們想檢測Fragment的內(nèi)存的話鹏漆,可以在Application中將返回的RefWatcher存下來,可以在Fragment的onDestroy中watch它。
public abstract class BaseFragment extends Fragment {
@Override public void onDestroy() {
super.onDestroy();
RefWatcher refWatcher = ExampleApplication.getRefWatcher(getActivity());
refWatcher.watch(this);
}
}