Android內(nèi)存泄漏的原因及解決技巧
正確的生命周期管理如何防止Android內(nèi)存泄漏
OutOfMemoryException是一個(gè)常見(jiàn)的令人沮喪的錯(cuò)誤,也是導(dǎo)致應(yīng)用程序意外關(guān)閉的主要原因之一。
“如果應(yīng)用程序昨天運(yùn)行良好,為什么現(xiàn)在會(huì)發(fā)生這種情況?這個(gè)問(wèn)題讓Android的開(kāi)發(fā)者和新手都感到困惑。
導(dǎo)致OutOfMemory異常的潛在原因有很多種,但其中最常見(jiàn)的是內(nèi)存泄漏—應(yīng)用程序中的內(nèi)存分配從未釋放。本文將解釋如何通過(guò)有效的生命周期管理(開(kāi)發(fā)過(guò)程中一個(gè)重要但經(jīng)常被忽視的部分)來(lái)最小化這種風(fēng)險(xiǎn)。
為什么安卓系統(tǒng)會(huì)發(fā)生內(nèi)存泄漏?
問(wèn)題很簡(jiǎn)單。某些對(duì)象應(yīng)該只有一個(gè)固定的壽命,當(dāng)它們的使用壽命結(jié)束時(shí),它們需要被刪除。
理論上,當(dāng)進(jìn)程使用onStop或onDestroy終止時(shí),應(yīng)該處理該內(nèi)存。但是,濫用對(duì)象引用可能會(huì)阻止垃圾收集器釋放未使用的對(duì)象。例如:如果未使用的對(duì)象A引用了未使用的對(duì)象B,那么您將得到兩個(gè)不必要的對(duì)象,垃圾回收器將永遠(yuǎn)不會(huì)釋放它們,因?yàn)樗鼈冋谙嗷ヒ谩?/p>
阻止內(nèi)存泄漏這種情況發(fā)生的常見(jiàn)技巧
開(kāi)發(fā)人員可以采取許多步驟來(lái)阻止死的活動(dòng)被困在內(nèi)存中。
- 在onResume()/onPause()或onStart()/onStop()中注冊(cè)/注銷廣播接收器
- 不要對(duì)視圖/活動(dòng)/上下文使用靜態(tài)變量
- 需要保存對(duì)上下文的引用的singleton應(yīng)該使用applicationContext()或?qū)⑵浒b到WeakReference中
- 注意匿名和非靜態(tài)內(nèi)部類,因?yàn)樗鼈儼瑢?duì)其封閉類的隱式引用。
- 如果要比父類(如處理程序)更長(zhǎng)壽,請(qǐng)使用靜態(tài)內(nèi)部類而不是匿名類。
- 如果內(nèi)部或匿名類是可取消的(如AsyncTask、Thread、RxSubscriptions),則在銷毀活動(dòng)時(shí)取消它。
Android生命周期感知組件
一旦你完成了上面的基本步驟,現(xiàn)在是時(shí)候做一些更重要的事情了:應(yīng)用程序活動(dòng)的生命周期。如果我們不能正確地管理生命周期,我們最終會(huì)在不再需要內(nèi)存的時(shí)候掛掉它。
這涉及到許多不同的任務(wù)。對(duì)于每個(gè)活動(dòng),我們需要中斷線程,去掉RxJava中的訂閱,取消AsyncTask引用,并確保正確刪除該活動(dòng)的引用(以及與之相關(guān)的任何其他活動(dòng))。所有這些任務(wù)都會(huì)消耗開(kāi)發(fā)人員的大量時(shí)間。
模型視圖呈現(xiàn)器(MVP)使事情變得更加復(fù)雜,MVP是Android中構(gòu)建用戶界面的常用架構(gòu)模式。然而,MVP對(duì)于從視圖中分離業(yè)務(wù)邏輯非常有用。
在MVP模式中,View和Presenter都是它們之間行為契約的抽象實(shí)現(xiàn)。實(shí)現(xiàn)MVP最常見(jiàn)的方法是使用活動(dòng)/片段作為視圖的實(shí)現(xiàn),并為習(xí)慣于引用視圖的演示者使用簡(jiǎn)單的實(shí)現(xiàn)。
所以我們最終得到了一個(gè)帶有Presenter引用的視圖和一個(gè)帶有視圖引用的Presenter(提示:這里有一個(gè)潛在的漏洞)。
考慮到這些潛在的困難,我們有必要建立一個(gè)適當(dāng)?shù)墓芾斫Y(jié)構(gòu)來(lái)移除在生命周期中創(chuàng)建的多余內(nèi)存。有幾種行之有效的方法可以做到這一點(diǎn):
1. 在Android Studio上使用Android Arch Lifecycle創(chuàng)建支持生命周期的組件
生命周期感知組件是智能的。例如,它們可以通過(guò)除去內(nèi)存來(lái)對(duì)另一個(gè)組件(如活動(dòng)或片段)的生命周期狀態(tài)的更改作出反應(yīng)。這意味著代碼更輕,內(nèi)存效率更高。
archlifecycle是Android的一個(gè)新庫(kù),它提供了一組工具來(lái)構(gòu)建支持生命周期的組件。庫(kù)以抽象的方式工作,這意味著生命周期所有者不再需要擔(dān)心管理特定任務(wù)和活動(dòng)的生命周期。
Arch生命周期的關(guān)鍵工具和定義如下:
- 生命周期:一個(gè)排序系統(tǒng),它定義了哪些對(duì)象具有Android生命周期,并允許對(duì)它們進(jìn)行監(jiān)視。
- LifecycleObserver:一個(gè)常規(guī)接口,它監(jiān)視每個(gè)被標(biāo)識(shí)為具有Android生命周期的對(duì)象,使用一個(gè)簡(jiǎn)單的公式來(lái)處理每個(gè)密鑰生命周期事件。
- @OnLifecycleEvent:可以在實(shí)現(xiàn)LifecycleObserver接口的類中使用的注釋。它允許我們?cè)O(shè)置關(guān)鍵生命周期事件,這些事件將在每次啟動(dòng)時(shí)觸發(fā)帶注釋的方法。以下是可設(shè)置的所有事件的列表:ON_ANY、ON_CREATE、ON_DESTROY、ON_PAUSE、ON_RESUME、ON_START、ON_STOP
- LifecycleOwner默認(rèn)為每個(gè)可以管理其生命周期的Android組件實(shí)現(xiàn),并讓開(kāi)發(fā)人員控制每個(gè)事件。
使用這些工具,我們可以將所有干凈的任務(wù)發(fā)送給它們的所有者(在我們的例子中是演示者),這樣我們就有了一個(gè)干凈的、無(wú)泄漏的解耦代碼(至少在演示者層是這樣)。
下面是一個(gè)超級(jí)基本的實(shí)現(xiàn),向您展示我們所說(shuō)的:
interface View: MVPView, LifecycleOwner class RandomPresenter : Presenter<View>, LifecycleObserver { private lateinit var view: View override fun attachView(view: View) { this.view = view view.lifecycle.addObserver(this) } @OnLifecycleEvent(Lifecycle.Event.On_DESTROY) fun onClear() { //TODO: clean }
2. 使用Android架構(gòu)視圖模型作為演示者和LiveData
另一種方法是通過(guò)使用新的生命周期組件來(lái)避免視圖模型的內(nèi)存泄漏。
ViewModel是一個(gè)抽象類,它實(shí)現(xiàn)一個(gè)稱為onClear的函數(shù),當(dāng)必須刪除某個(gè)特定對(duì)象時(shí),該函數(shù)會(huì)自動(dòng)調(diào)用。ViewModel是由框架生成的,它附加到創(chuàng)建者的生命周期中(作為一個(gè)額外的好處,使用Dagger注入非常容易)
除了使用ViewModel,LiveData還提供了一個(gè)重要的通信渠道。這意味著創(chuàng)造了一個(gè)容易觀察到的反應(yīng)性產(chǎn)物。
這里最重要的一點(diǎn)是,生命周期所有者可以觀察到LiveData,因此數(shù)據(jù)傳輸總是由生命周期管理的,而且我們可以確保在使用它們時(shí)保留任何引用。
3. 使用LeakCanary和Bugfender
除了上述步驟之外,我們還想推薦兩個(gè)重要的工具包:LeakCanary,一個(gè)用于監(jiān)視泄漏的流行工具,以及我們自己的Bugfender。
LeakCanary是一個(gè)用于Android和Java的內(nèi)存檢測(cè)庫(kù)。它是開(kāi)源的,所以有一個(gè)龐大的社區(qū)支持它,它不僅僅告訴你一個(gè)漏洞,它還告訴你可能的原因。
我們的遠(yuǎn)程日志工具Bugfender允許您調(diào)試單個(gè)泄漏跟蹤,并擴(kuò)展一個(gè)名為DisplayLeakService的類,它讓我們知道何時(shí)發(fā)生泄漏。然后我們就可以用Bugfender輕松登錄了。
public class LeakUploadService extends DisplayLeakService { override fun afterDefaultHandling(heapDump: HeapDump, result: AnalysisResult, leakInfo: String) { if (result.leakFound) { Bugfender.d(“LeakCanary”, result.toString()) } } }
此外,用戶還可以獲得Bugfender的所有其他好處,包括全天候記錄日志(即使設(shè)備離線)、內(nèi)置故障報(bào)告和易于使用的web控制臺(tái)。
以上就是Android內(nèi)存泄漏的原因及解決技巧的詳細(xì)內(nèi)容,更多關(guān)于Android內(nèi)存泄漏的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android 源碼淺析RecyclerView ItemAnimator
這篇文章主要為大家介紹了Android 源碼淺析RecyclerView ItemAnimator,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12Android的Service應(yīng)用程序組件基本編寫方法
Service是一個(gè)android 系統(tǒng)中的應(yīng)用程序組件,它跟Activity的級(jí)別差不多,但是他沒(méi)有圖形化界面,不能自己運(yùn)行,只能后臺(tái)運(yùn)行,Service通常用來(lái)處理一些耗時(shí)比較長(zhǎng)的操作2012-12-12談?wù)凙ndroid6.0運(yùn)行時(shí)的權(quán)限處理
之前有人在 Android 6.0 的機(jī)型上運(yùn)行了DragGridView結(jié)果出異常奔潰了。想必問(wèn)題的原因大家都知道,是Android 6.0新引入了在運(yùn)行時(shí)權(quán)限申請(qǐng)(Runtime Permissions)的功能。那么這所謂的運(yùn)行時(shí)申請(qǐng)權(quán)限究竟是怎么一回事呢,下面跟著小編一起來(lái)看看吧!2016-08-08Android直播系統(tǒng)平臺(tái)搭建之圖片實(shí)現(xiàn)陰影效果的方法小結(jié)
這篇文章主要介紹了Android直播系統(tǒng)平臺(tái)搭建, 圖片實(shí)現(xiàn)陰影效果的若干種方法,本文給大家?guī)?lái)三種方法,每種方法通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2021-08-08android之HttpPost&HttpGet使用方法介紹
下文直接講用法,先知道怎么用,再知道怎么回事,具體如下,感興趣的朋友可以參考下哈2013-06-06android實(shí)現(xiàn)獲取正在運(yùn)行的應(yīng)用程序
android如何獲取正在運(yùn)行的應(yīng)用程序,因?yàn)樵趂ramework中想添加這個(gè)功能,所以寫了個(gè)appliction來(lái)實(shí)現(xiàn)一下獲取正在運(yùn)行的應(yīng)用程序2013-01-01Flutter數(shù)字切換動(dòng)畫實(shí)現(xiàn)示例詳解
這篇文章主要為大家介紹了Flutter數(shù)字切換動(dòng)畫實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08Android RecyclerView實(shí)現(xiàn)多種item布局的方法
本篇文章主要介紹了Android RecyclerView實(shí)現(xiàn)多種item布局的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-12-12