昨天的问题说到了关于 内存泄漏需要注意的点,在文章最后有说到 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 中,我们可以看到相关配置:

  1. <!--leakcanary-sample/src/main/AndroidManifest.xml-->
  2. <service
  3. android:name=".internal.HeapAnalyzerService"
  4. android:process=":leakcanary"
  5. android:enabled="false"
  6. />
  7. <service
  8. android:name=".DisplayLeakService"
  9. android:process=":leakcanary"
  10. android:enabled="false"
  11. />
  12. <activity
  13. android:theme="@style/leak_canary_LeakCanary.Base"
  14. android:name=".internal.DisplayLeakActivity"
  15. android:process=":leakcanary"
  16. android:enabled="false"
  17. android:label="@string/leak_canary_display_activity_label"
  18. android:icon="@mipmap/leak_canary_icon"
  19. android:taskAffinity="com.squareup.leakcanary.${applicationId}"
  20. >
  21. <intent-filter>
  22. <action android:name="android.intent.action.MAIN"/>
  23. <category android:name="android.intent.category.LAUNCHER"/>
  24. </intent-filter>
  25. </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 里面做类似下面的代码。

  1. //com.example.leakcanary.ExampleApplication
  2. @Override
  3. public void onCreate() {
  4. super.onCreate();
  5. if (LeakCanary.isInAnalyzerProcess(this)) {
  6. // This process is dedicated to LeakCanary for heap analysis.
  7. // You should not init your app in this process.
  8. return;
  9. }
  10. LeakCanary.install(this);
  11. }

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

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

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

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

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <manifest
  3. xmlns:android="http://schemas.android.com/apk/res/android"
  4. package="com.squareup.leakcanary.leaksentry"
  5. >
  6. <application>
  7. <provider
  8. android:name="leakcanary.internal.LeakSentryInstaller"
  9. android:authorities="${applicationId}.leak-sentry-installer"
  10. android:exported="false"/>
  11. </application>
  12. </manifest>

再经过查看 LeakSentryInstaller 可以看到:

  1. package leakcanary.internal
  2. import android.app.Application
  3. import android.content.ContentProvider
  4. import android.content.ContentValues
  5. import android.database.Cursor
  6. import android.net.Uri
  7. import leakcanary.CanaryLog
  8. /**
  9. * Content providers are loaded before the application class is created. [LeakSentryInstaller] is
  10. * used to install [leaksentry.LeakSentry] on application start.
  11. */
  12. internal class LeakSentryInstaller : ContentProvider() {
  13. override fun onCreate(): Boolean {
  14. CanaryLog.logger = DefaultCanaryLog()
  15. val application = context!!.applicationContext as Application
  16. InternalLeakSentry.install(application)
  17. return true
  18. }
  19. override fun query(
  20. uri: Uri,
  21. strings: Array<String>?,
  22. s: String?,
  23. strings1: Array<String>?,
  24. s1: String?
  25. ): Cursor? {
  26. return null
  27. }
  28. override fun getType(uri: Uri): String? {
  29. return null
  30. }
  31. override fun insert(
  32. uri: Uri,
  33. contentValues: ContentValues?
  34. ): Uri? {
  35. return null
  36. }
  37. override fun delete(
  38. uri: Uri,
  39. s: String?,
  40. strings: Array<String>?
  41. ): Int {
  42. return 0
  43. }
  44. override fun update(
  45. uri: Uri,
  46. contentValues: ContentValues?,
  47. s: String?,
  48. strings: Array<String>?
  49. ): Int {
  50. return 0
  51. }
  52. }

确实是真的骚,我们都知道 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. jmeter 如何获取一小时之前的时间戳

    正确答案: ${__intSum(${__time(/1000,)},-3600,)} 如果还要显示毫秒 ${__longSum(${__time},-3600000,)}

  2. Consider the following: If you want an embedded database (H2, HSQL or Der...

    这个坑把java进程干掉就可以了,因为占用了 Description: Failed to configure a DataSource: 'url' attribute is not specifi ...

  3. Kafka学习笔记之Kafka背景及架构介绍

    0x00 概述 本文介绍了Kafka的创建背景,设计目标,使用消息系统的优势以及目前流行的消息系统对比.并介绍了Kafka的架构,Producer消息路由,Consumer Group以及由其实现的不 ...

  4. mongodb 更新数据时int32变为double的解决办法

       场景: 在命令手动的修改签到表的整型字段synState,multi参数是可以更新多条,如果是false则更新一条. db.getCollection("ClassRecordOneD ...

  5. C#左移运算符

     << 左移操作符.简单理解是对变量进行与2的n次乘方的运算.比如x<<1=x*2x<<2=x*4x<<3=x*8x<<4=x*16依此类推 ...

  6. 【开发工具】- Xshell过期了怎么办?

    点击下边链接下载免费版 http://www.netsarang.com/download/free_license.html

  7. (原)Ubuntu安装TensorRT

    转载请注明出处: https://www.cnblogs.com/darkknightzh/p/11129472.html 参考网址: https://docs.nvidia.com/deeplear ...

  8. 随笔记录--RegExp类型

    阅读Javascript高级程序设计第五章 -- RegExp类型总结 对于基础教程部分, 有小伙伴不熟悉的,可以参考 正则表达式 - 教程 1. 基础部分回顾: ECMASript通过RegExp类 ...

  9. How to: Create a C/C++ Union by Using Attributes (C#)

    [How to: Create a C/C++ Union by Using Attributes (C#)] 1.you can create what is known as a union in ...

  10. pansas 绘制定制的箱线图

    1  普通风格 代码 import numpy as np import pandas as pd import matplotlib.pyplot as plt plt.rcParams['font ...