LeakCanary接入MK编译App过程记录

2 篇文章 0 订阅
2 篇文章 0 订阅

因为工作需要,需要对固件中通过MK编译的系统应用接入LeakCanary以检查内存泄露。

几经折腾,终于成功。

接入的LeakCanary 最新版本 2.2版本,免写版本。

环境准备:使用的Android10 AOSP代码(tag: android-10.0.0_r10),接入leakcanary前已经全编译通过。

接入的APP路径:packages/apps/Gallery2

对比的独立应用接入LeakCanary: GcTest

 

来看看遇到的问题,及解决过程:

1、在MK中加入leakcanary2.2依赖编译不过

解决方法:修改Android.mk

--- a/Android.mk
+++ b/Android.mk
@@ -2,6 +2,8 @@ LOCAL_PATH:= $(call my-dir)

 include $(CLEAR_VARS)

+COMMON_LIBS_PATH := ../../../prebuilts/tools/common/m2/repository
+
 LOCAL_MODULE_TAGS := optional

 LOCAL_STATIC_ANDROID_LIBRARIES := \
@@ -13,7 +15,22 @@ LOCAL_STATIC_ANDROID_LIBRARIES := \
 LOCAL_STATIC_JAVA_LIBRARIES := \
     com.android.gallery3d.common2 \
     xmp_toolkit \
-    mp4parser
+    mp4parser \
+    leakcanary-android \
+    leakcanary-android-core \
+    leakcanary-object-watcher \
+    leakcanary-object-watcher-android \
+    shark \
+    shark-android \
+    shark-graph \
+    shark-hprof \
+    shark-log \
+    leakcanary-object-watcher-android-androidx \
+    kotlin-stdlib-1.2.50
+
+LOCAL_AAPT_FLAGS := \
+        --auto-add-overlay \
+        --extra-packages com.squareup.leakcanary \

 LOCAL_SRC_FILES := \
     $(call all-java-files-under, src) \
@@ -30,7 +47,8 @@ LOCAL_PRODUCT_MODULE := true

 LOCAL_OVERRIDES_PACKAGES := Gallery Gallery3D GalleryNew3D

-LOCAL_SDK_VERSION := current
+#LOCAL_SDK_VERSION := current
+LOCAL_PRIVATE_PLATFORM_APIS := true

 LOCAL_JNI_SHARED_LIBRARIES := \
     libjni_eglfence \
@@ -44,6 +62,21 @@ LOCAL_JAVA_LIBRARIES += org.apache.http.legacy
 LOCAL_JARJAR_RULES := $(LOCAL_PATH)/jarjar-rules.txt

 include $(BUILD_PACKAGE)
+#Added for source code of leakcannry compile start
+include $(CLEAR_VARS)
+LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES := \
+	leakcanary-android:libs/leakcanary-android-2.2.aar \
+	leakcanary-android-core:libs/leakcanary-android-core-2.2.aar \
+	leakcanary-object-watcher:libs/leakcanary-object-watcher-2.2.jar \
+	leakcanary-object-watcher-android:libs/leakcanary-object-watcher-android-2.2.aar \
+	leakcanary-object-watcher-android-androidx:libs/leakcanary-object-watcher-android-androidx-2.2.aar \
+	shark:libs/shark-2.2.jar \
+	shark-android:libs/shark-android-2.2.jar \
+	shark-graph:libs/shark-graph-2.2.jar \
+	shark-hprof:libs/shark-hprof-2.2.jar \
+	shark-log:libs/shark-log-2.2.jar \
+        kotlin-stdlib-1.2.50:$(COMMON_LIBS_PATH)/org/jetbrains/kotlin/kotlin-stdlib/1.2.50/kotlin-stdlib-1.2.50.jar
+include $(BUILD_MULTI_PREBUILT)

 ifeq ($(strip $(LOCAL_PACKAGE_OVERRIDES)),)

  怎么知道要依赖这些包?

  通过在独立应用上去引用LeakCanary来获取到的。

  独立应用引用LeakCanary参见 https://square.github.io/leakcanary/upgrading-to-leakcanary-2.0/  

  特别的 LeakCanary使用kotlin编写,这里需要依赖kotlin-stdlib库,使用AOSP自带的kotlin-stdlib库声明编译依然有问题,自己定义了一个本地的kotlin-stdlib-1.2.50版本引用后编译通过。

 2、安装应用后发现并没有Leaks图标显示

  2.2版本LeakCanary打入应用,安装后会显示一个与应用相同的图标到桌面,名称为Leaks。

  解压apk后查看AndroidManifest.xml,发现依赖的aar里面AndroidManifest.xml的内容没有合并进来。

  原来aar是需要声明到LOCAL_STATIC_JAVA_AAR_LIBRARIES下面,而不LOCAL_STATIC_JAVA_LIBRARIES下面

  另外,还有一个缺失的okio依赖也添加上了。

  

+LOCAL_STATIC_JAVA_AAR_LIBRARIES := \
+    leakcanary-android \
+    leakcanary-android-core \
+    leakcanary-object-watcher-android
+    leakcanary-object-watcher-android-androidx
+
 LOCAL_STATIC_JAVA_LIBRARIES := \
     com.android.gallery3d.common2 \
     xmp_toolkit \
     mp4parser \
-    leakcanary-android \
-    leakcanary-android-core \
     leakcanary-object-watcher \
-    leakcanary-object-watcher-android \
     shark \
     shark-android \
     shark-graph \
     shark-hprof \
     shark-log \
-    leakcanary-object-watcher-android-androidx \
-    kotlin-stdlib-1.2.50
+    kotlin-stdlib-1.2.50 \
+    okio
+
@@ -69,15 +69,20 @@ LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES := \
        leakcanary-android-core:libs/leakcanary-android-core-2.2.aar \
        leakcanary-object-watcher:libs/leakcanary-object-watcher-2.2.jar \
        leakcanary-object-watcher-android:libs/leakcanary-object-watcher-android-2.2.aar \
        leakcanary-object-watcher-android-androidx:libs/leakcanary-object-watcher-android-androidx-2.2.aar \
        shark:libs/shark-2.2.jar \
        shark-android:libs/shark-android-2.2.jar \
        shark-graph:libs/shark-graph-2.2.jar \
        shark-hprof:libs/shark-hprof-2.2.jar \
        shark-log:libs/shark-log-2.2.jar \
-        kotlin-stdlib-1.2.50:$(COMMON_LIBS_PATH)/org/jetbrains/kotlin/kotlin-stdlib/1.2.50/kotlin-stdlib-1.2.50.jar
+        kotlin-stdlib-1.2.50:$(COMMON_LIBS_PATH)/org/jetbrains/kotlin/kotlin-stdlib/1.2.50/kotlin-stdlib-1.2.50.jar \
+       okio:libs/okio-2.2.2.jar
 include $(BUILD_MULTI_PREBUILT)

   编译后,安装上 Leaks 图标显示到桌面了。

   开始写一下内存泄露的demo进去,然后测试。

   问题来了:内存泄露的Activity销毁后并未触发自动的检查,即使手动的去点击 Dump Heap Now,结果还是显示Found 0 retained Objects。就是没检查到内存泄露。

   3、检测不到内存泄露

    开始解压Gallery2.apk与GcTest.apk对比里面的打入的leakcanary的class、AndroidManifest里面关于leakcanary的声明的区别。

    没有发现什么特别的异常。

    心里隐隐的觉得是Gallery2.apk是没有去做打入WeakReference的操作,至于为什么Gallery2.apk没有做,倒没有认真的想。

    通过Gallery2.apk与GcTest.apk的Leaks分别去检查了对方导出的hprof,结果是Gallery2能够分析出GcTest hprof中的内存泄露,GcTest分析Gallery2 hprof的结果同样是未发现内存泄露。这也证明了我的猜测,Gallery2里是没有做WeakReference的。

    好吧,只能去进一步看下LeakCanary的文档、源码。

    为了看这kotlin的代码,还专门去瞄了下kotlin的语法。

    参考leakcanary官方说明: https://square.github.io/leakcanary/recipes/#watching-objects-with-a-lifecycle

     然后再去对比 Gallery2.apk与GcTest.apk 销毁内存泄露的Activity之后的日志( tag:LeakCanary),发现GcTest会有如下的日志:

04-02 06:57:49.874 13112 13112 D LeakCanary: Watching instance of com.example.gctest.LeakActivity (com.example.gctest.LeakActivity received Activity#onDestroy() callback) with key e63419f1-7884-4f1a-9abd-d656ebd98466

 接着就开始dump、分析,提示泄露。

  去看LeakCanary源码里面的这个日志的出自,然后自己再增加了一些日志。

  分别更新到Gallery2.apk与GcTest.apk 下编译后运行。

  发现下面问题点在于ObjectWatcher 里面的  if (!isEnabled()) 判定这里跳出了,所以后续的逻辑都没有走到。

@Synchronized fun watch(
    watchedObject: Any,
    description: String
  ) {
    if (!isEnabled()) {
      SharkLog.d { "not isEnabled()" }
      return
    }
    removeWeaklyReachableObjects()
    SharkLog.d { "after removeWeaklyReachableObjects" }
    val key = UUID.randomUUID()
        .toString()
    val watchUptimeMillis = clock.uptimeMillis()
    val reference =
      KeyedWeakReference(watchedObject, key, description, watchUptimeMillis, queue)
    SharkLog.d{ "after KeyedWeakReference" }
    SharkLog.d {
      "Watching " +
          (if (watchedObject is Class<*>) watchedObject.toString() else "instance of ${watchedObject.javaClass.name}") +
          (if (description.isNotEmpty()) " ($description)" else "") +
          " with key $key"
    }

    watchedObjects[key] = reference
    checkRetainedExecutor.execute {
      moveToRetained(key)
    }
  }

  进一步去看这个isEnabled()的判定,来自 AppWatcher.config.enabled,代码里面来如下面的判断

  val isDebuggableBuild by lazy {
    (application.applicationInfo.flags and ApplicationInfo.FLAG_DEBUGGABLE) != 0
  }
这表示该应用为Debug编译则enable为true, 反之则为false。

   恍然大悟,想起来官方文档里面有一段,当时也没多想
  

   好吧,修改了代码,强制赋值 isDebuggableBuild 为true

  val isDebuggableBuild = true

  再重新编译了一个leakcanary-object-watcher-android.aar 替换到Gallery2下面,编译后验证。

  终于能够正常的自动触发、并分析出内存泄露了。

  2020-04-03

    

 

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值