亚洲欧美日韩熟女|做爱高潮视频网址|国产一区二区三级片|国产Av中文字幕www.性色av|亚洲婷婷永久免费|国产高清中文字幕|欧美变态网站久re视频精品|人妻AV鲁丝第一页|天堂AV一区二区在线观看|综合 91在线精品

Android 性能優(yōu)化之內(nèi)存優(yōu)化與泄漏分析工具LeakCanary

2023-04-12


一、背景


在Android應(yīng)用中,除了正常的業(yè)務(wù)開(kāi)發(fā),我們也要關(guān)注性能問(wèn)題。卡頓、內(nèi)存溢出、內(nèi)存泄漏等問(wèn)題,直接的表現(xiàn)會(huì)反饋到用戶(hù)體驗(yàn)上,用戶(hù)體驗(yàn)不好導(dǎo)致應(yīng)用被卸載或者換到其他平臺(tái)。


在性能優(yōu)化,各大公司都會(huì)付出一些代價(jià),或者安排專(zhuān)人負(fù)責(zé)。有些新手也想做,但是無(wú)從下手,對(duì)專(zhuān)業(yè)工具和專(zhuān)業(yè)代碼使用以及分析比較吃力,排查起來(lái)也比較費(fèi)勁。如果有專(zhuān)業(yè)的工具能夠只管的把這些記錄并標(biāo)記好。這樣新手也可以通過(guò)詳情的問(wèn)題去排查,那么LeaksCanary就是這款工具了。


二、性能的簡(jiǎn)單介紹(在面試的時(shí)候,也經(jīng)常會(huì)被問(wèn)起)


1、卡頓:卡頓的主要涉及到線(xiàn)程的使用,在LeaksCanary中,會(huì)給出當(dāng)前線(xiàn)程的使用信息


2、內(nèi)存溢出:是程序在申請(qǐng)內(nèi)存時(shí),沒(méi)有足夠的內(nèi)存空間供其使用


3、內(nèi)存泄漏:是程序在申請(qǐng)內(nèi)存后,無(wú)法釋放已申請(qǐng)的內(nèi)存空間


三、LeaksCanary


1、介紹

LeakCanary是Square公司為Android開(kāi)發(fā)者提供的一個(gè)自動(dòng)檢測(cè)內(nèi)存泄漏的工具,


LeakCanary本質(zhì)上是一個(gè)基于MAT進(jìn)行Android應(yīng)用程序內(nèi)存泄漏自動(dòng)化檢測(cè)的的開(kāi)源工具,我們可以通過(guò)集成LeakCanary提供的jar包到自己的工程中,一旦檢測(cè)到內(nèi)存泄漏,LeakCanary就會(huì)dump Memory信息,并通過(guò)另一個(gè)進(jìn)程分析內(nèi)存泄漏的信息并展示出來(lái),隨時(shí)發(fā)現(xiàn)和定位內(nèi)存泄漏問(wèn)題,而不用每次在開(kāi)發(fā)流程中都抽出專(zhuān)人來(lái)進(jìn)行內(nèi)存泄漏問(wèn)題檢測(cè),極大地方便了Android應(yīng)用程序的開(kāi)發(fā)。


2、github地址:

GitHub - square/leakcanary: A memory leak detection library for Android.https://www.baidu.com/link?url=bcGyW7ySy9cW2azMXz5h-je6EtkwbMggSIlaEiFfWbtIqf3Yx3rak5qcn3Om3WJp&wd=&eqid=87caa544001114160000000463e1f198



3、接入


3.1依賴(lài)庫(kù):



debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.9.1' releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:2.9.1'




3.2、代碼引入

2.9.1的庫(kù)已kotlin開(kāi)發(fā)語(yǔ)言為核心,網(wǎng)絡(luò)上大多數(shù)還是1.6.1。這就導(dǎo)致在接入的時(shí)候無(wú)法方式不同。


1.6.1版本,直接在application中,通過(guò)LeakCanary.install(application),來(lái)完成。


2.9.1接入:


在androidManifest中引入







leak_canary_watcher_auto_install:



true




是否自動(dòng)安裝,如果你不想自動(dòng)安裝,可以自行社會(huì)一個(gè)變量引入,默認(rèn)不自動(dòng)安裝



介紹:MainProcessAppWatcherInstaller

internal class MainProcessAppWatcherInstaller : ContentProvider() {

  override fun onCreate(): Boolean {
    val application = context!!.applicationContext as Application
    AppWatcher.manualInstall(application)
    return true
  }

MainProcessAppWatcherInstaller安裝是ContentProvider的一個(gè)派生類(lèi)。安裝也是通過(guò)




AppWatcher.manualInstall(application)






安裝方法manualInstall會(huì)做安裝前的檢查

如果不想通過(guò)自動(dòng)安裝,可以通過(guò)


Java:AppWatcher.INSTANCE.manualInstall(application);


kotlin:AppWatcher.manualInstall(application);


如果不想自己控制,可以在xml資源文件,設(shè)置為true。


注意:

如果手動(dòng)安裝,最好判斷一下是否已安裝AppWatcher.isInstalled()否則會(huì)報(bào)錯(cuò)


四、卡頓問(wèn)題優(yōu)化:


LeakCanary 造成卡頓的原因就是在主進(jìn)程中 dump hprof 文件,文件就會(huì)涉及到IO操作,在讀寫(xiě)時(shí),占用大量線(xiàn)程,導(dǎo)致頁(yè)面會(huì)出現(xiàn)卡頓情況。針對(duì)這個(gè)問(wèn)題,可以通過(guò)引入多進(jìn)程方案。避免在IO時(shí),影響主進(jìn)程


leakcanary-android-process

依賴(lài)庫(kù):


debugImplementation 'com.squareup.leakcanary:leakcanary-android-process:2.9.1'

該庫(kù)的核心是RemoteLeakCanaryWorkerService,這個(gè)服務(wù)就是多進(jìn)程的核心。我們使用的時(shí)候,需要提前注冊(cè)這個(gè)service。


在該service中:

override fun onCreate() {
    // Ideally we wouldn't need to install AppWatcher at all here, however
    // the installation triggers InternalsLeakCanary to store the application instance
    // which is then used by the event listeners that respond to analysis progress.
    if (!AppWatcher.isInstalled) {
      val application = super.getApplicationContext() as Application
      AppWatcher.manualInstall(
        application,
        // Nothing to watch in the :leakcanary process.
        watchersToInstall = emptyList()
      )
    }
    super.onCreate()
  }

在oncreate中,執(zhí)行安裝。

其實(shí),在核心庫(kù)中,已加入了該service。我們只需要通過(guò)



HeapDump:核心




RemoteWorkManagerHeapAnalyzer


WorkManger也是一款開(kāi)源工具,用于后臺(tái)工作的架構(gòu)組件,需要兼顧機(jī)會(huì)和有保證的執(zhí)行。機(jī)會(huì)性執(zhí)行意味著WorkManager將盡快完成您的后臺(tái)工作。


依賴(lài)庫(kù):



def versions_work = "2.3.3"implementation "androidx.work:work-runtime:$versions_work"




五、線(xiàn)上接入:


目前很多調(diào)試都是在debug下進(jìn)行,通過(guò)專(zhuān)業(yè)工具去處理。但是我們一旦打包以后會(huì)移除debug這些模塊。如果線(xiàn)上發(fā)生了這些,如何獲取?


很多開(kāi)發(fā)人員通過(guò)獲取句柄文件來(lái)分析,但是如果我們沒(méi)有問(wèn)題設(shè)備,就無(wú)法獲取到dump的句柄文件。這時(shí),如果能把句柄文件傳回到日志服務(wù)器,這樣就方便開(kāi)發(fā)人員定位信息。


線(xiàn)上使用 LeakCanary,首要要確定以下問(wèn)題:

  1. 如何獲取 LeakCanary 分析的結(jié)果?
  2. 結(jié)果以何種形式上報(bào)到質(zhì)量平臺(tái)上?
  3. 如何確定合理的監(jiān)控采集時(shí)機(jī),盡可能小的影響用戶(hù)體驗(yàn)?

定義自己的監(jiān)聽(tīng):

上面我們介紹了workmanager這個(gè)工具,他就是通過(guò)定義一個(gè)監(jiān)聽(tīng),來(lái)完成后臺(tái)的處理,減少主進(jìn)程的線(xiàn)程操作,降低卡頓。同樣我們也可以定義自己的EventListener。


目前LeakCanary的方法中已提供了一些監(jiān)聽(tīng):


val eventListeners: List = listOf(
      LogcatEventListener,
      ToastEventListener,
      LazyForwardingEventListener {
        if (InternalLeakCanary.formFactor == TV) TvEventListener else NotificationEventListener
      },
      when {
          RemoteWorkManagerHeapAnalyzer.remoteLeakCanaryServiceInClasspath ->
            RemoteWorkManagerHeapAnalyzer
          WorkManagerHeapAnalyzer.validWorkManagerInClasspath -> WorkManagerHeapAnalyzer
          else -> BackgroundThreadHeapAnalyzer
      }
    ),

小試牛刀:

import leakcanary.EventListener

class MyLeakCanaryEventListener:EventListener {

    override fun onEvent(event: EventListener.Event) {


    }


}

Event:

目前已提供了如下Event


1、class DumpingHeap(uniqueId: String) : Event(uniqueId)

從“LeakCanary堆轉(zhuǎn)儲(chǔ)”HandlerThread發(fā)送


2、class HeapDump( uniqueId: String, val file: File, val durationMillis: Long, val reason: String) : Event(uniqueId)

從“LeakCanary堆轉(zhuǎn)儲(chǔ)”HandlerThread發(fā)送


3、class HeapDumpFailed( uniqueId: String, val exception: Throwable, val willRetryLater: Boolean) : Event(uniqueId)

從“LeakCanary堆轉(zhuǎn)儲(chǔ)”HandlerThread發(fā)送。失敗


4、class HeapAnalysisProgress( uniqueId: String, val step: Step, val progressPercent: Double) : Event(uniqueId)

從執(zhí)行分析的線(xiàn)程發(fā)送。


5、sealed class HeapAnalysisDone ( uniqueId: String, val heapAnalysis: T, showIntent: Intent) : Event(uniqueId)

5.1、class HeapAnalysisSucceeded( uniqueId: String, heapAnalysis: HeapAnalysisSuccess, val unreadLeakSignatures: Set , showIntent: Intent)

分析成功,


5.2、class HeapAnalysisFailed( uniqueId: String, heapAnalysis: HeapAnalysisFailure, showIntent: Intent)

分析失敗


這些類(lèi)都是繼承了class Event( val uniqueId: String),且都是內(nèi)部類(lèi)。


分析結(jié)果:

if (event is EventListener.Event.HeapAnalysisDone.HeapAnalysisSucceeded) {
            //分析成功

            val successEvent = event as EventListener.Event.HeapAnalysisDone.HeapAnalysisSucceeded
            val list = successEvent.heapAnalysis.allLeaks
            list?.let {
                for (leak in it) {
                    leak.leakTraces
                }
            }
        }

這樣我們就可以得到了分析結(jié)果。


六、注冊(cè)監(jiān)聽(tīng):


注冊(cè)監(jiān)聽(tīng)涉及到LeakCanary的配置,這邊我們先講解一下LeakCanary.Config配置參數(shù)


data class Config(
    /**
     * Whether LeakCanary should dump the heap when enough retained instances are found. This needs
     * to be true for LeakCanary to work, but sometimes you may want to temporarily disable
     * LeakCanary (e.g. for a product demo).
     *
     * Defaults to true.
     */
    val dumpHeap: Boolean = true,
    /**
     * If [dumpHeapWhenDebugging] is false then LeakCanary will not dump the heap
     * when the debugger is attached. The debugger can create temporary memory leaks (for instance
     * if a thread is blocked on a breakpoint).
     *
     * Defaults to false.
     */
    val dumpHeapWhenDebugging: Boolean = false,
    /**
     * When the app is visible, LeakCanary will wait for at least
     * [retainedVisibleThreshold] retained instances before dumping the heap. Dumping the heap
     * freezes the UI and can be frustrating for developers who are trying to work. This is
     * especially frustrating as the Android Framework has a number of leaks that cannot easily
     * be fixed.
     *
     * When the app becomes invisible, LeakCanary dumps the heap after
     * [AppWatcher.retainedDelayMillis] ms.
     *
     * The app is considered visible if it has at least one activity in started state.
     *
     * A higher threshold means LeakCanary will dump the heap less often, therefore it won't be
     * bothering developers as much but it could miss some leaks.
     *
     * Defaults to 5.
     */
    val retainedVisibleThreshold: Int = 5,

    /**
     * Known patterns of references in the heap, added here either to ignore them
     * ([IgnoredReferenceMatcher]) or to mark them as library leaks ([LibraryLeakReferenceMatcher]).
     *
     * When adding your own custom [LibraryLeakReferenceMatcher] instances, you'll most
     * likely want to set [LibraryLeakReferenceMatcher.patternApplies] with a filter that checks
     * for the Android OS version and manufacturer. The build information can be obtained by calling
     * [shark.AndroidBuildMirror.fromHeapGraph].
     *
     * Defaults to [AndroidReferenceMatchers.appDefaults]
     */
    val referenceMatchers: List = AndroidReferenceMatchers.appDefaults,

    /**
     * List of [ObjectInspector] that provide LeakCanary with insights about objects found in the
     * heap. You can create your own [ObjectInspector] implementations, and also add
     * a [shark.AppSingletonInspector] instance created with the list of internal singletons.
     *
     * Defaults to [AndroidObjectInspectors.appDefaults]
     */
    val objectInspectors: List = AndroidObjectInspectors.appDefaults,

    /**
     * Deprecated, add to LeakCanary.config.eventListeners instead.
     * Called on a background thread when the heap analysis is complete.
     * If you want leaks to be added to the activity that lists leaks, make sure to delegate
     * calls to a [DefaultOnHeapAnalyzedListener].
     *
     * Defaults to [DefaultOnHeapAnalyzedListener]
     */
    @Deprecated(message = "Add to LeakCanary.config.eventListeners instead")
    val onHeapAnalyzedListener: OnHeapAnalyzedListener = DefaultOnHeapAnalyzedListener.create(),

    /**
     * Extracts metadata from a hprof to be reported in [HeapAnalysisSuccess.metadata].
     * Called on a background thread during heap analysis.
     *
     * Defaults to [AndroidMetadataExtractor]
     */
    val metadataExtractor: MetadataExtractor = AndroidMetadataExtractor,

    /**
     * Whether to compute the retained heap size, which is the total number of bytes in memory that
     * would be reclaimed if the detected leaks didn't happen. This includes native memory
     * associated to Java objects (e.g. Android bitmaps).
     *
     * Computing the retained heap size can slow down the analysis because it requires navigating
     * from GC roots through the entire object graph, whereas [shark.HeapAnalyzer] would otherwise
     * stop as soon as all leaking instances are found.
     *
     * Defaults to true.
     */
    val computeRetainedHeapSize: Boolean = true,

    /**
     * How many heap dumps are kept on the Android device for this app package. When this threshold
     * is reached LeakCanary deletes the older heap dumps. As several heap dumps may be enqueued
     * you should avoid going down to 1 or 2.
     *
     * Defaults to 7.
     */
    val maxStoredHeapDumps: Int = 7,

    /**
     * LeakCanary always attempts to store heap dumps on the external storage if the
     * WRITE_EXTERNAL_STORAGE is already granted, and otherwise uses the app storage.
     * If the WRITE_EXTERNAL_STORAGE permission is not granted and
     * [requestWriteExternalStoragePermission] is true, then LeakCanary will display a notification
     * to ask for that permission.
     *
     * Defaults to false because that permission notification can be annoying.
     */
    val requestWriteExternalStoragePermission: Boolean = false,

    /**
     * Finds the objects that are leaking, for which LeakCanary will compute leak traces.
     *
     * Defaults to [KeyedWeakReferenceFinder] which finds all objects tracked by a
     * [KeyedWeakReference], ie all objects that were passed to
     * [ObjectWatcher.expectWeaklyReachable].
     *
     * You could instead replace it with a [FilteringLeakingObjectFinder], which scans all objects
     * in the heap dump and delegates the decision to a list of
     * [FilteringLeakingObjectFinder.LeakingObjectFilter]. This can lead to finding more leaks
     * than the default and shorter leak traces. This also means that every analysis during a
     * given process life will bring up the same leaking objects over and over again, unlike
     * when using [KeyedWeakReferenceFinder] (because [KeyedWeakReference] instances are cleared
     * after each heap dump).
     *
     * The list of filters can be built from [AndroidObjectInspectors]:
     *
     * ```kotlin
     * LeakCanary.config = LeakCanary.config.copy(
     *     leakingObjectFinder = FilteringLeakingObjectFinder(
     *         AndroidObjectInspectors.appLeakingObjectFilters
     *     )
     * )
     * ```
     */
    val leakingObjectFinder: LeakingObjectFinder = KeyedWeakReferenceFinder,

    /**
     * Dumps the Java heap. You may replace this with your own implementation if you wish to
     * change the core heap dumping implementation.
     */
    val heapDumper: HeapDumper = AndroidDebugHeapDumper,

    /**
     * Listeners for LeakCanary events. See [EventListener.Event] for the list of events and
     * which thread they're sent from. You most likely want to keep this list and add to it, or
     * remove a few entries but not all entries. Each listener is independent and provides
     * additional behavior which you can disable by not excluding it:
     *
     * ```kotlin
     * // No cute canary toast (very sad!)
     * LeakCanary.config = LeakCanary.config.run {
     *   copy(
     *     eventListeners = eventListeners.filter {
     *       it !is ToastEventListener
     *     }
     *   )
     * }
     * ```
     */
    val eventListeners: List = listOf(
      LogcatEventListener,
      ToastEventListener,
      LazyForwardingEventListener {
        if (InternalLeakCanary.formFactor == TV) TvEventListener else NotificationEventListener
      },
      when {
          RemoteWorkManagerHeapAnalyzer.remoteLeakCanaryServiceInClasspath ->
            RemoteWorkManagerHeapAnalyzer
          WorkManagerHeapAnalyzer.validWorkManagerInClasspath -> WorkManagerHeapAnalyzer
          else -> BackgroundThreadHeapAnalyzer
      }
    ),

    /**
     * Deprecated: This is a no-op, set a custom [leakingObjectFinder] instead.
     */
    @Deprecated("This is a no-op, set a custom leakingObjectFinder instead")
    val useExperimentalLeakFinders: Boolean = false
  )

默認(rèn)都是有值,對(duì)應(yīng)參數(shù)如下:

private var dumpHeap = config.dumpHeap
      private var dumpHeapWhenDebugging = config.dumpHeapWhenDebugging
      private var retainedVisibleThreshold = config.retainedVisibleThreshold
      private var referenceMatchers = config.referenceMatchers
      private var objectInspectors = config.objectInspectors
      private var onHeapAnalyzedListener = config.onHeapAnalyzedListener
      private var metadataExtractor = config.metadataExtractor
      private var computeRetainedHeapSize = config.computeRetainedHeapSize
      private var maxStoredHeapDumps = config.maxStoredHeapDumps
      private var requestWriteExternalStoragePermission =
        config.requestWriteExternalStoragePermission
      private var leakingObjectFinder = config.leakingObjectFinder
      private var heapDumper = config.heapDumper
      private var eventListeners = config.eventListeners
      private var useExperimentalLeakFinders = config.useExperimentalLeakFinders

新增我們的監(jiān)聽(tīng):

val eventListeners = LeakCanary.config.eventListeners.toMutableList().apply {
            add(MyLeakCanaryEventListener())
        }

        LeakCanary.config=LeakCanary.config.copy(
            eventListeners=eventListeners
        )

這樣整個(gè)線(xiàn)上監(jiān)聽(tīng)基本完成。


我們只要對(duì)LeakTrace進(jìn)行分析,把結(jié)果存儲(chǔ)后,在一個(gè)合適的時(shí)機(jī)上報(bào)給服務(wù)器即可


七、AppWatcher的看守配置:


知道如何去分析這些問(wèn)題,LeakCanary還提供了看門(mén)機(jī)制,監(jiān)聽(tīng)哪些也是可以通過(guò)配置的。


在AppWatcher中,也提供了Config


data class Config(
    @Deprecated("Call AppWatcher.manualInstall() with a custom watcher list")
    val watchActivities: Boolean = true,

    @Deprecated("Call AppWatcher.manualInstall() with a custom watcher list")
    val watchFragments: Boolean = true,

    @Deprecated("Call AppWatcher.manualInstall() with a custom watcher list")
    val watchFragmentViews: Boolean = true,

    @Deprecated("Call AppWatcher.manualInstall() with a custom watcher list")
    val watchViewModels: Boolean = true,

    @Deprecated("Call AppWatcher.manualInstall() with a custom retainedDelayMillis value")
    val watchDurationMillis: Long = TimeUnit.SECONDS.toMillis(5),

    @Deprecated("Call AppWatcher.appDefaultWatchers() with a custom ReachabilityWatcher")
    val enabled: Boolean = true
  )

可以看守的有以下,默認(rèn)都是開(kāi)啟,如果你想關(guān)閉哪個(gè),默認(rèn)設(shè)為false即可




AppWatcher.config= AppWatcher.config.copy(watchActivities = false, enabled = false)




這樣,我們就可以很好的看守我們想要的結(jié)果。


八、ANR日志收集

ANR或者crash的創(chuàng)建,也意味著ActivityThread的線(xiàn)程已經(jīng)掛了,這時(shí)候我們?nèi)魏蔚淖龇ǘ疾荒芤蕾?lài)主線(xiàn)程來(lái)完成,這樣會(huì)導(dǎo)致抓取不到日志。


我們知道,虛擬機(jī)android的跨線(xiàn)程通訊都是binder,雖然ActivityThread掛了,但是AMS沒(méi)有掛,AMS還存活在SystemManager中,又因?yàn)槊總€(gè)進(jìn)程在fork出來(lái)的時(shí)候都有自己的binder。所以這個(gè)時(shí)候我們可以通過(guò)AMS來(lái)獲取。


日志收集核心:獲取、存儲(chǔ)、上報(bào)。獲取到存儲(chǔ)是核心流程,如何將獲取的日志進(jìn)行存儲(chǔ),IO機(jī)制顯然無(wú)法滿(mǎn)足這么短時(shí)間的操作。這個(gè)時(shí)候我們可以參考內(nèi)存映射mmp機(jī)制。目前包括一些開(kāi)源的都是采用mmap,常見(jiàn)的框架如微信的mmkv,mmkv是采用了mmap,也都是c在處理,利用內(nèi)存映射來(lái)獲取日志文檔,這樣就很容易獲取到日志信息。


九、總結(jié)


通過(guò)接入、卡頓優(yōu)化、攔截、配置等,能夠很好的滿(mǎn)足我們從線(xiàn)上到線(xiàn)下的各種把控??梢院芎玫耐晟茟?yīng)用的監(jiān)控與優(yōu)化機(jī)制。


Debug模式,直接在LeaksApp中可以很好的看到問(wèn)題列表。其他更多的可以參考github的開(kāi)源信息,或者自己接入調(diào)試。


本文僅代表作者觀點(diǎn),版權(quán)歸原創(chuàng)者所有,如需轉(zhuǎn)載請(qǐng)?jiān)谖闹凶⒚鱽?lái)源及作者名字。

免責(zé)聲明:本文系轉(zhuǎn)載編輯文章,僅作分享之用。如分享內(nèi)容、圖片侵犯到您的版權(quán)或非授權(quán)發(fā)布,請(qǐng)及時(shí)與我們聯(lián)系進(jìn)行審核處理或刪除,您可以發(fā)送材料至郵箱:service@tojoy.com