Android Weak Handler:能夠避免內存泄漏的Handler庫

 

這是一個針對技術開發者的一個應用,你能夠在掘金上獲取最新最優質的技術乾貨,不只僅是Android知識、前端、後端以致於產品和設計都有涉獵,想成爲全棧工程師的朋友不要錯過!前端

android使用java做爲其開發環境。java的跨平臺和垃圾回收機制已經幫助咱們解決了底層的一些問題。可是儘管有了垃圾回收機制,在開發android的時候仍然時不時的遇到out of memory的問題,這個時候咱們不由要問,垃圾回收機器去哪兒了?java

咱們主要講的是handler引發的泄漏,並給出三種解決辦法,其中最後一種方法就是咱們想介紹的WeakHandler 庫。android

可能致使泄漏問題的handler通常會被提示 Lint警告:git

This Handler class should be static or leaks might occur 意思:class使用靜態聲明否者可能出現內存泄露。

 

這是一個基本的activity。在handler的post方法中咱們加入了一個匿名的runnable,同時我將其執行延遲了整整80秒。咱們運行這個程序,而且旋轉幾回手機,而後分析內存。github

如今內存中有7個activity了,這太不靠譜了,因此咱們來研究下爲何GC沒有清理它。(上圖中我查詢內存中activity列表時用的是oql(對象查詢語言),簡單強大的工具,ps 怎麼用的,誰能告訴我?)swift

 

從上圖中咱們能夠看到其中一個對mainactivity的引用是來自this$0this$0是什麼呢?如下是關於this$0的解釋:後端

-------什麼是this$0---------安全

非static的inner class裏面都會有一個this$0的字段保存它的父對象。在Java中,非靜態(匿名)內部類會默認隱性引用外部類對象。而靜態內部類不會引用外部類對象。一個編譯後的inner class 極可能是這樣的:bash

1
2
3
4
5
6
7
8
9
class parent$inner
{
synthetic parent this $0;
parent$inner(parent this $0)
{
this . this $0 = this $0;
this $0.foo();
}
}

-------什麼是this$0結束---------工具

在咱們的代碼中,匿名的runnable是一個非靜態的內部類,所以他會使用this$0來保存MainActivity,而後runnable會繼續被它的callback引用,而callback又接着被接下來一連串的message引用,這樣主線程的引用就太他媽多了。 當Activity finish後,延時消息會繼續存在主線程消息隊列中80秒,而後處理消息,所以handler在繼續存在於內存中,而handler引用了Activity,在咱們旋轉手機的時候,Activity 不停的重建和finish,致使多個activity的引用出現。

一旦將Runnable或者是Message 發送進handler,將保存一連串的引用了主線程(這裏是MainActivity吧)的Message命令,直到message執行完。若是發送Runnable設置了延遲時間,那麼至少在這段延遲時間內內存泄漏是確定的,若是是直接發送,在Message比較大的狀況下,也是有可能發生暫時的泄漏的。

 

解決辦法一:使用Static

再次執行,同時旋轉手機,分析內存以下:

Analyse static runnable HPROF

尼瑪,仍是同樣的。咱們看看是誰還拉着activity不放:

Analyse static runnable HPROF

 

在最底下咱們發現activity繼續被DoneRunnable裏面mTextView中的mContext引用着。看來在這種狀況下,看來僅僅使用static並無解決問題啊。還須要作點工做才行。

 

靜態的Runnable加WeakReference

既然是由於mTextView引發的,那咱們把mTextView換成弱引用好了:

Static runnable with weak reference

須要注意的,既然mTextView是弱引用,因此隨時均可能爲null,所以須要在使用前判斷是否爲空。好了繼續看看內存的狀況:

Analyse static runnable with weak reference HPROF

all right,我想咱們已經完美的解決問題了。總結一下咱們作了哪些工做:

使用靜態的內部類

對全部handler/Runnable中的變量都用弱引用。

可是這種方式代碼是否是不少,並且還必須得當心翼翼。

 

在onDestroy中清理掉全部Messages

Handler有個很方便的方法:removeCallbacksAndMessages,當參數爲null的時候,能夠清除掉全部跟次handler相關的Runnable和Message,咱們在onDestroy中調用次方法也就不會發生內存泄漏了。

Remove callbacks code

運行,旋轉手機

Analise remove callbacks HPROF

 

可是若是你對代碼有更高的要求,以爲這樣還不方即可以使用做者提供的WeakHandler

 

WeakHandler

WeakHandler使用起來和handler如出一轍,可是他是安全的,WeakHandler使用以下:

WeakHandler code

你只須要把之前的Handler替換成WeakHandler就好了。

 

WeakHandler的實現原理

WeakHandler的思想是將Handler和Runnable作一次封裝,咱們使用的是封裝後的WeakHandler,但其實真正起到handler做用的是封裝的內部,而封裝的內部對handler和runnable都是用的弱引用。

WeakHandler diagram

第一幅圖是普通handler的引用關係圖

第二幅圖是使用WeakHandler的引用關係圖

其實原文有對WeakHandler更多的解釋,可是表述起來也挺複雜的。

原文地址:https://techblog.badoo.com/blog/2014/10/09/calabash-android-query/

github項目地址:https://github.com/badoo/android-weak-handler

相關文章
相關標籤/搜索