本文介紹如何在系統應用中集成LeakCanary。由於很多系統應用不使用gradle,且通過Android.mk來進行編譯,因此無法使用類似gradle的方式添加依賴來完成對LeakCanary的集成和使用。
1. 源碼下載
LeakCanary的源碼鏈接 https://github.com/square/leakcanary,通過 git clone
或者 zip 的方式將源碼下載到本地,源碼目錄如下:
將 leakcanary-analyzer
,leakcanary-android
和 leakcanary-watcher
複製到系統應用源碼根目錄下。
2. 添加編譯支持(修改Android.mk)
將上述3個java代碼目錄添加到編譯樹中:
LOCAL_SRC_FILES += $(call all-java-files-under, leakcanary-analyzer/) \
$(call all-java-files-under, leakcanary-android/) \
$(call all-java-files-under, leakcanary-watcher/)
將 leakcanary-android 目錄下的 res 目錄添加到資源文件編譯:
LOCAL_RESOURCE_DIR += $(LOCAL_PATH)/leakcanary-android/src/main/res
添加 haha
和 trove
的 jar 包到系統應用源碼 libs 文件夾中,haha-2.0.3.jar 和 trove-3.0.2.jar 的下載鏈接:
https://pan.baidu.com/s/1dto2NcgvxEdiA3lGXVKX0A 提取碼: qjqy
將這兩個 jar 包加入編譯:
LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES += \
haha:libs/haha-2.0.3.jar \
trove:libs/trove-3.0.2.jar
LOCAL_STATIC_JAVA_LIBRARIES += \
haha \
trove
3. 修改 AndroidManifest.xml
需要將 leakcanary-android 中聲明的 Activity,Service,Provider 等組件以及權限申請加入到 AndroidManifest.xml 清單文件中:
<!-- LeakCanary -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<!-- LeakCanary -->
<service
android:name="com.squareup.leakcanary.internal.HeapAnalyzerService"
android:process=":leakcanary"
android:enabled="true" />
<service
android:name="com.squareup.leakcanary.DisplayLeakService"
android:process=":leakcanary"
android:enabled="true" />
<provider
android:name="com.squareup.leakcanary.internal.LeakCanaryFileProvider"
android:authorities="com.squareup.leakcanary.fileprovider.${applicationId}"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/leak_canary_file_paths"/>
</provider>
<activity
android:theme="@style/leak_canary_LeakCanary.Base"
android:name="com.squareup.leakcanary.internal.DisplayLeakActivity"
android:process=":leakcanary"
android:enabled="true"
android:label="@string/leak_canary_display_activity_label"
android:icon="@mipmap/leak_canary_icon"
android:taskAffinity="com.squareup.leakcanary.${applicationId}">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity
android:theme="@style/leak_canary_Theme.Transparent"
android:name="com.squareup.leakcanary.internal.RequestStoragePermissionActivity"
android:process=":leakcanary"
android:taskAffinity="com.squareup.leakcanary.${applicationId}"
android:enabled="true"
android:excludeFromRecents="true"
android:icon="@mipmap/leak_canary_icon"
android:roundIcon="@mipmap/leak_canary_icon"
android:label="@string/leak_canary_storage_permission_activity_label"/>
4. 初始化 LeakCanary
在應用 Application (如果沒有,需要手寫一個並在 AndroidManifest.xml 文件中註冊)的 onCreate 函數中初始化 LeakCanary。
@Override
public void onCreate() {
super.onCreate();
if (!LeakCanary.isInAnalyzerProcess(this)) {
sRefWatcher = LeakCanary.install(this);
}
}
5. 其他編譯錯誤解決
packages/apps/****/leakcanary-android/src/main/res/values/leak_canary_public.xml:19: error: No 'id' attribute supplied <public>, and no previous id defined in this file.
packages/apps/****/leakcanary-android/src/main/res/values/leak_canary_public.xml:20: error: No 'id' attribute supplied <public>, and no previous id defined in this file.
packages/apps/****/leakcanary-android/src/main/res/values/leak_canary_public.xml:21: error: No 'id' attribute supplied <public>, and no previous id defined in this file.
解決方法:直接刪除 leak_canary_public.xml。
packages/apps/****/leakcanary-analyzer/src/main/java/com/squareup/leakcanary/HeapAnalyzer.java:31: 錯誤: 找不到符號
import gnu.trove.THashMap;
packages/apps/****/leakcanary-analyzer/src/main/java/com/squareup/leakcanary/HeapAnalyzer.java:32: 錯誤: 找不到符號
import gnu.trove.TObjectProcedure;
解決方法:在 HeapAnalyzer.java 文件中將 “import gnu.trove.THashMap” 修改成 “import gnu.trove.map.hash.THashMap”,將 “import gnu.trove.TObjectProcedure” 修改成 “import gnu.trove.procedure.TObjectProcedure”。
packages/apps/****/leakcanary-analyzer/src/test/java/com/squareup/leakcanary/AsyncTaskLeakTest.java:22: 錯誤: 程序包org.junit不存在
import org.junit.Before;
^
packages/apps/****/leakcanary-analyzer/src/test/java/com/squareup/leakcanary/AsyncTaskLeakTest.java:23: 錯誤: 程序包org.junit不存在
import org.junit.Test;
^
packages/apps/****/leakcanary-analyzer/src/test/java/com/squareup/leakcanary/AsyncTaskLeakTest.java:24: 錯誤: 程序包org.junit.runner不存在
import org.junit.runner.RunWith;
^
packages/apps/****/leakcanary-analyzer/src/test/java/com/squareup/leakcanary/AsyncTaskLeakTest.java:25: 錯誤: 程序包org.junit.runners不存在
import org.junit.runners.Parameterized;
^
packages/apps/****/leakcanary-analyzer/src/test/java/com/squareup/leakcanary/AsyncTaskLeakTest.java:34: 錯誤: 程序包org.hamcrest.core不存在
import static org.hamcrest.core.StringContains.containsString;
^
packages/apps/****/leakcanary-analyzer/src/test/java/com/squareup/leakcanary/AsyncTaskLeakTest.java:34: 錯誤: 僅從類和接口靜態導入
import static org.hamcrest.core.StringContains.containsString;
^
packages/apps/****/leakcanary-analyzer/src/test/java/com/squareup/leakcanary/AsyncTaskLeakTest.java:35: 錯誤: 程序包org.junit不存在
import static org.junit.Assert.assertEquals;
解決方法:直接刪除所有的 Test 類文件
packages/apps/****/leakcanary-android/src/main/java/com/squareup/leakcanary/DisplayLeakService.java:57: 錯誤: 程序包R不存在
String contentTitle = getString(R.string.leak_canary_no_leak_title);
^
packages/apps/****/leakcanary-android/src/main/java/com/squareup/leakcanary/DisplayLeakService.java:58: 錯誤: 程序包R不存在
String contentText = getString(R.string.leak_canary_no_leak_text);
解決辦法:將 LeakCanary 中所有使用到 “R” 的地方都替換成 import 當前系統應用的 “R”。
import static com.squareup.leakcanary.BuildConfig.GIT_SHA;
^
符號: 類 BuildConfig
位置: 程序包 com.squareup.leakcanary
packages/apps/****/leakcanary-android/src/main/java/com/squareup/leakcanary/LeakCanary.java:30: 錯誤: 僅從類和接口靜態導入
import static com.squareup.leakcanary.BuildConfig.GIT_SHA;
^
packages/apps/****/leakcanary-android/src/main/java/com/squareup/leakcanary/LeakCanary.java:31: 錯誤: 找不到符號
import static com.squareup.leakcanary.BuildConfig.LIBRARY_VERSION;
^
符號: 類 BuildConfig
位置: 程序包 com.squareup.leakcanary
packages/apps/****/leakcanary-android/src/main/java/com/squareup/leakcanary/LeakCanary.java:31: 錯誤: 僅從類和接口靜態導入
import static com.squareup.leakcanary.BuildConfig.LIBRARY_VERSION;
^
解決方法:在 leakcanary-android/src/main/java/com/squareup/leakcanary/ 文件夾下創建一個 BuildConfig.java 文件,在其中添加如下內容:public static String GIT_SHA = "GIT_SHA"; public static String LIBRARY_VERSION = "LIBRARY_VERSION";
packages/apps/****/leakcanary-android/src/main/java/com/squareup/leakcanary/internal/AndroidOFragmentRefWatcher.java:22: 錯誤: 找不到符號
import android.support.annotation.RequiresApi;
^
符號: 類 RequiresApi
位置: 程序包 android.support.annotation
packages/apps/****/leakcanary-android/src/main/java/com/squareup/leakcanary/internal/AndroidOFragmentRefWatcher.java:27: 錯誤: 找不到符號
@RequiresApi(Build.VERSION_CODES.O) //
解決方法:去除 RequiresApi 註解及相關的 import。
packages/apps/****/leakcanary-android/src/main/java/com/squareup/leakcanary/AbstractAnalysisResultService.java:47: 錯誤: 找不到符號
ContextCompat.startForegroundService(context, intent);
^
符號: 方法 startForegroundService(Context,Intent)
位置: 類 ContextCompat
packages/apps/****/leakcanary-android/src/main/java/com/squareup/leakcanary/internal/HeapAnalyzerService.java:49: 錯誤: 找不到符號
ContextCompat.startForegroundService(context, intent);
^
符號: 方法 startForegroundService(Context,Intent)
位置: 類 ContextCompat
解決方法:將 “ContextCompat.startForegroundService(context, intent)” 修改成 context.startForegroundService(intent)
。