每日一问:说说你对 LeakCanary 的了解

昨天的问题说到了关于 内存泄漏需要注意的点,在文章最后有说到 LeakCanary 检测内存泄漏。实际上,我相信绝大多数人也知道甚至使用过这个库。

这个系列通常来说如果发现了不错的资源,会选择直接截取部分拿过来,所以对于文章底部的参考链接一般都是非常不错的,可以直接去看哟~

LeakCanary 的基本工作流程是怎样的?

LeakCanary 的使用方式非常简单,只需要在 build.gradle 里面直接写上依赖,并且在 Application 类里面做注册就可以了。

当然,需要在 Application 里面注册这样的操作仅在大多数人接触的 1.x 版本,实际上 LeakCanary 现在已经升级到了 2.x 版本,代码侵入性更低,而且纯 Kotlin 写法。从 Google 各种 Demo 主推 Kotlin 以及各种主流库都在使用 Kotlin 编写来看可见 Kotlin 确实在 Android 开发中愈发重要,没使用的小伙伴必须得去学习一波了,目前我也是纯 Kotlin 做开发的。

对于工作原理我相信大家应该也是或多或少有一定了解,这里刚好有一张非常不错的流程图就直接借用过来了,另外他从源码角度理解 LeakCanary 的这篇文章也写的非常不错,感兴趣的点击文章底部的链接直达。

初次使用 LeakCanary 为什么没有 Icon 入口

我们常常在使用 LeakCanary 的时候会发现这样一个问题:最开始并没有出现 LeakCanary 的 Launcher icon,但当出现了内存泄漏警告的时候系统桌面就多了这么一个图标,一般情况下都是会非常好奇的。

从 1.x 的源码中就可以看出端倪。在 leakcanary-android 的 manifast 中,我们可以看到相关配置:

<!--leakcanary-sample/src/main/AndroidManifest.xml-->
<service
    android:name=".internal.HeapAnalyzerService"
    android:process=":leakcanary"
    android:enabled="false"
    />
<service
    android:name=".DisplayLeakService"
    android:process=":leakcanary"
    android:enabled="false"
    />
<activity
    android:theme="@style/leak_canary_LeakCanary.Base"
    android:name=".internal.DisplayLeakActivity"
    android:process=":leakcanary"
    android:enabled="false"
    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>

我们可以看到 DisplayLeakActivity 被设置为了 Launcher,并设置上了对应的图标,所以我们使用 LeakCanary 会在系统桌面上生成 Icon 入口。但是 DisplayLeakActivityenable 属性默认是 false,所以在桌面上是不会显示入口的。而在发生内存泄漏的时候,LeakCanary 会主动将 enable 属性置为 true。

LeakCanary 2 都做了些什么

最近 LeakCanary 升级到了 2.x 版本,这是一次完全的重构,去除了 1.x release 环境下引用的空包 leakcanary-android-no-op。并且 Kotlin 语言覆盖高达 99.8%,也再也不需要在 Application 里面做类似下面的代码。

//com.example.leakcanary.ExampleApplication
@Override
public void onCreate() {
    super.onCreate();
    if (LeakCanary.isInAnalyzerProcess(this)) {
        // This process is dedicated to LeakCanary for heap analysis.
        // You should not init your app in this process.
        return;
    }
    LeakCanary.install(this);
}

只需要在依赖里面添加这样的代码就可以了。

dependencies {
  debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.0-alpha-2'
}

初次看到这样的操作,会觉得非常神奇,仔细阅读源码才回发现它竟然使用了一个骚操作:ContentProvider

leakcanary-leaksentry 模块的 AndroidManifest.xml文件中可以看到:

<?xml version="1.0" encoding="utf-8"?>
<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.squareup.leakcanary.leaksentry"
    >

  <application>
    <provider
        android:name="leakcanary.internal.LeakSentryInstaller"
        android:authorities="${applicationId}.leak-sentry-installer"
        android:exported="false"/>
  </application>
</manifest>

再经过查看 LeakSentryInstaller 可以看到:

package leakcanary.internal

import android.app.Application
import android.content.ContentProvider
import android.content.ContentValues
import android.database.Cursor
import android.net.Uri
import leakcanary.CanaryLog

/**
 * Content providers are loaded before the application class is created. [LeakSentryInstaller] is
 * used to install [leaksentry.LeakSentry] on application start.
 */
internal class LeakSentryInstaller : ContentProvider() {

  override fun onCreate(): Boolean {
    CanaryLog.logger = DefaultCanaryLog()
    val application = context!!.applicationContext as Application
    InternalLeakSentry.install(application)
    return true
  }

  override fun query(
    uri: Uri,
    strings: Array<String>?,
    s: String?,
    strings1: Array<String>?,
    s1: String?
  ): Cursor? {
    return null
  }

  override fun getType(uri: Uri): String? {
    return null
  }

  override fun insert(
    uri: Uri,
    contentValues: ContentValues?
  ): Uri? {
    return null
  }

  override fun delete(
    uri: Uri,
    s: String?,
    strings: Array<String>?
  ): Int {
    return 0
  }

  override fun update(
    uri: Uri,
    contentValues: ContentValues?,
    s: String?,
    strings: Array<String>?
  ): Int {
    return 0
  }
}

确实是真的骚,我们都知道 ContentProvideronCreate() 的调用时机介于 ApplicationattachBaseContext()onCreate() 之间,LeakCanary 这么做,把 init 的逻辑放到库内部,让调用方完全不需要在 Application 里去进行初始化了,十分方便。这样下来既可以避免开发者忘记初始化导致一些错误,也可以让我们庞大的 Application 代码更加简洁。

参考:https://www.jianshu.com/p/49239eac7a76

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,366评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,521评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,689评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,925评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,942评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,727评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,447评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,349评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,820评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,990评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,127评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,812评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,471评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,017评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,142评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,388评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,066评论 2 355

推荐阅读更多精彩内容