當你的才華還撐不起你的夢想時,你只能一直前進!

每日一問:說說你對 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="//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

posted @ 2019-06-06 16:57 南塵 閱讀(...) 評論(...) 編輯 收藏

寫不完的矯情,做不完的開源

點擊進入我的GitHub頁
- 韩国快乐8开奖结果查询
韩国快乐8开奖结果查询
優美鋼琴曲合集-南塵.mp3                    感謝您閱讀我的博客,如果您現在工作、學習累了或者疲憊了,不妨聆聽一下音樂,它能夠減輕你的疲勞,還能夠帶給您一種舒適愉悅的心情。(樣式取自博客園-欲淚成雪)