昨天的问题说到了关于 内存泄漏需要注意的点,在文章最后有说到 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

每日一问:说说你对 LeakCanary 的了解的更多相关文章

  1. 每日一问:Android 消息机制,我有必要再讲一次!

    坚持原创日更,短平快的 Android 进阶系列,敬请直接在微信公众号搜索:nanchen,直接关注并设为星标,精彩不容错过. 我 17 年的 面试系列,曾写过一篇名为:Android 面试(五):探 ...

  2. 每日一问:谈谈 volatile 关键字

    这是 wanAndroid 每日一问中的一道题,下面我们来尝试解答一下. 讲讲并发专题 volatile,synchronize,CAS,happens before, lost wake up 为了 ...

  3. 每日一问:讲讲 Java 虚拟机的垃圾回收

    昨天我们用比较精简的文字讲了 Java 虚拟机结构,没看过的可以直接从这里查看: 每日一问:你了解 Java 虚拟机结构么? 今天我们必须来看看 Java 虚拟机的垃圾回收算法是怎样的.不过在开始之前 ...

  4. 每日一问:你了解 Java 虚拟机结构么?

    对于从事 C/C++ 程序员开发的小伙伴来说,在内存管理领域非常头疼,因为他们总是需要对每一个 new 操作去写配对的 delete/free 代码.而对于我们 Android 乃至 Java 程序员 ...

  5. 每日一问:LayoutParams 你知道多少?

    前面的文章中着重讲解了 View 的测量流程.其中我提到了一句非常重要的话:View 的测量匡高是由父控件的 MeasureSpec 和 View 自身的 `LayoutParams 共同决定的.我们 ...

  6. 每日一问:简述 View 的绘制流程

    Android 开发中经常需要用一些自定义 View 去满足产品和设计的脑洞,所以 View 的绘制流程至关重要.网上目前有非常多这方面的资料,但最好的方式还是直接跟着源码进行解读,每日一问系列一直追 ...

  7. android 深入浅出 群内“每日一问” 问答总结

    永远不变的就是变. 俗话说的好,环境改变人生. 常常面对的是一群积极奋进的人,那么你的心态和生活也会变的充满斗志.青春在于折腾,趁我们还年轻,拿出你的激情.踏着泪水载着梦,才干拥有自己的一片天空. 上 ...

  8. 每日一问:面试结束时面试官问"你有什么问题需要问我呢",该如何回答?

    面试结束时面试官问"你有什么问题需要问我呢",该如何回答?

  9. Python【每日一问】15

    问:简述with方法打开处理文件实际上做了哪些工作 答: filename= "test.txt" with open(filename, "w", encod ...

随机推荐

  1. 详解XOR(异或)运算加密

    逻辑运算之中,除了 AND 和 OR,还有一种 XOR 运算,中文称为"异或运算".它的定义是:两个值相同时,返回false,否则返回true.也就是说,XOR可以用来判断两个值是 ...

  2. 图解微信小程序---添加tabBar底部菜单,添加已做好轮播图操作

    图解微信小程序---添加tabBar底部菜单,添加已做好轮播图操作 什么是tabBar? 顶部或者底部tab栏如果小程序是一个多 tab 应用(客户端窗口的底部或顶部有 tab 栏可以切换页面),可以 ...

  3. 「vue基础」一篇浅显易懂的 Vue 路由使用指南( Vue Router 上)

    大家好,今天的内容,我将和大家一起聊聊 Vue 路由相关的知识,如果你以前做过服务端相关的开发,那你一定会对程序的URL结构有所了解,我没记错的话也是路由映射的概念,需要进行配置. 其实前端这些框架的 ...

  4. 剑指offer 栈的压入和弹出

    题目描述输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序.假设压入栈的所有数字均不相等.例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈 ...

  5. C#实现高性能高并发Socket服务器

    1.高并发服务器实现一 本文转载 转载地址 2.高并发服务器实现二 本文转载 转载内容在于学习C#实现的高并发服务器 以下个人观点 1 需要注意SocketAsyncEventArgs的使用 2 做到 ...

  6. python 函数式编程 闭包,返回一个函数

    参考链接:https://www.liaoxuefeng.com/wiki/1016959663602400/1017434209254976 作业 #使用生成器 def createCounter( ...

  7. 基于JDK1.8,Java容器源码分析

    容器源码分析 如果没有特别说明,以下源码分析基于 JDK 1.8. 在 IDEA 中 double shift 调出 Search EveryWhere,查找源码文件,找到之后就可以阅读源码. Lis ...

  8. 将Python源程序打包成可独立执行的文件

    有时候需要将编写好的脚本发送给别人,但是在没有安装运行环境或依赖库的情况下,Python脚本程序无法执行.PyInstaller工具可以快速的将python脚本打包成一个二进制可执行的exe程序,并且 ...

  9. vue中通过hls.js播放m3u8格式的视频

    近期做了一个功能,是接入一个海康的摄像头的监控视频,怎么获取m3u8的视频这里就不在叙述了,只说一下怎么将m3u8格式的视频成功播放 一.m3u8和HLS介绍 1.M3U8文件是指UTF-8编码格式的 ...

  10. 小程序动态设置style,使用内部数据