每日一问:说说你对 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 入口。但是 DisplayLeakActivity 的 enable 属性默认是 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
}
}
确实是真的骚,我们都知道 ContentProvider 的 onCreate() 的调用时机介于 Application 的 attachBaseContext() 和 onCreate() 之间,LeakCanary 这么做,把 init 的逻辑放到库内部,让调用方完全不需要在 Application 里去进行初始化了,十分方便。这样下来既可以避免开发者忘记初始化导致一些错误,也可以让我们庞大的 Application 代码更加简洁。
参考:https://www.jianshu.com/p/49239eac7a76
每日一问:说说你对 LeakCanary 的了解的更多相关文章
- 每日一问:Android 消息机制,我有必要再讲一次!
坚持原创日更,短平快的 Android 进阶系列,敬请直接在微信公众号搜索:nanchen,直接关注并设为星标,精彩不容错过. 我 17 年的 面试系列,曾写过一篇名为:Android 面试(五):探 ...
- 每日一问:谈谈 volatile 关键字
这是 wanAndroid 每日一问中的一道题,下面我们来尝试解答一下. 讲讲并发专题 volatile,synchronize,CAS,happens before, lost wake up 为了 ...
- 每日一问:讲讲 Java 虚拟机的垃圾回收
昨天我们用比较精简的文字讲了 Java 虚拟机结构,没看过的可以直接从这里查看: 每日一问:你了解 Java 虚拟机结构么? 今天我们必须来看看 Java 虚拟机的垃圾回收算法是怎样的.不过在开始之前 ...
- 每日一问:你了解 Java 虚拟机结构么?
对于从事 C/C++ 程序员开发的小伙伴来说,在内存管理领域非常头疼,因为他们总是需要对每一个 new 操作去写配对的 delete/free 代码.而对于我们 Android 乃至 Java 程序员 ...
- 每日一问:LayoutParams 你知道多少?
前面的文章中着重讲解了 View 的测量流程.其中我提到了一句非常重要的话:View 的测量匡高是由父控件的 MeasureSpec 和 View 自身的 `LayoutParams 共同决定的.我们 ...
- 每日一问:简述 View 的绘制流程
Android 开发中经常需要用一些自定义 View 去满足产品和设计的脑洞,所以 View 的绘制流程至关重要.网上目前有非常多这方面的资料,但最好的方式还是直接跟着源码进行解读,每日一问系列一直追 ...
- android 深入浅出 群内“每日一问” 问答总结
永远不变的就是变. 俗话说的好,环境改变人生. 常常面对的是一群积极奋进的人,那么你的心态和生活也会变的充满斗志.青春在于折腾,趁我们还年轻,拿出你的激情.踏着泪水载着梦,才干拥有自己的一片天空. 上 ...
- 每日一问:面试结束时面试官问"你有什么问题需要问我呢",该如何回答?
面试结束时面试官问"你有什么问题需要问我呢",该如何回答?
- Python【每日一问】15
问:简述with方法打开处理文件实际上做了哪些工作 答: filename= "test.txt" with open(filename, "w", encod ...
随机推荐
- golang学习笔记 ---slice
Go 语言中的slice类型可以理解为是数组array类型的描述符,包含了三个因素: 指向底层数组的指针 slice目前使用到的底层数组的元素个数,即长度 底层数组的最大长度,即容量 因此当我们定义一 ...
- 2019-11-29-WPF-非客户区的触摸和鼠标点击响应
原文:2019-11-29-WPF-非客户区的触摸和鼠标点击响应 title author date CreateTime categories WPF 非客户区的触摸和鼠标点击响应 lindexi ...
- EF Core 简单使用介绍
EF Core 是一个ORM(对象关系映射),它使 .NET 开发人员可以使用 .NET对象操作数据库,避免了像ADO.NET访问数据库的代码,开发者只需要编写对象即可. EF Core 支持多种数据 ...
- Winform中设置ZedGraph的曲线为折线、点折线、散点图
场景 Winform中设置ZedGraph的曲线为散点图: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/102465399 在上 ...
- PHP之面向对象(上)
PHP 1,定义:动态交互的计算机语言,弱类型语言 静态交互 html css js 凡是动态交互的都需要服务器phpstudy 2,使用集成服务器phpstudy apache 服务器 m ...
- Eureka应用注册与集群数据同步源码解析
在之前的EurekaClient自动装配及启动流程解析一文中我们提到过,在构造DiscoveryClient类时,会把自身注册到服务端,本文就来分析一下这个注册流程 客户端发起注册 boolean r ...
- echarts 更改tooltip提示框CSS样式
最近 做项目,用过echarts,发现tooltip提示z-index级别很高,想更改下,看了下文档:https://www.echartsjs.com/zh/option.html#tooltip. ...
- 为什么MES实施起来效果不佳?
原因一:我国制造业存在管理基础的先天不足 我国企业与发达国家企业在管理发展上,存在较大的差别.发达制造国家经历了管理探索.发展.成熟.再提高的全过程,从管理基础的奠定到思想认识的深刻程度,都是我国所无 ...
- python—字符串拼接三种方法
python—字符串拼接三种方法 1.使用加号(+)号进行拼接 字符串拼接直接进行相加就可以,比较容易理解,但是一定要记得,变量直接相加,不是变量就要用引号引起来,不然会出错,另外数字是要转换为字 ...
- ORACLE百分比分析函数RATIO_TO_REPORT() OVER()
有时候不用的指标的绝对值不能比,但是转转为百分比的形式就容易看出波动了,是数据分析的好用的一个分析函数 20:00:24 SYS@orcl> conn scott/tiger; Connecte ...