系統應用集成 LeakCanary

本文介紹如何在系統應用中集成LeakCanary。由於很多系統應用不使用gradle,且通過Android.mk來進行編譯,因此無法使用類似gradle的方式添加依賴來完成對LeakCanary的集成和使用。

1. 源碼下載

LeakCanary的源碼鏈接 https://github.com/square/leakcanary,通過 git clone 或者 zip 的方式將源碼下載到本地,源碼目錄如下:
LeakCanary源碼目錄樹
leakcanary-analyzerleakcanary-androidleakcanary-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

添加 hahatrove 的 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)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章