這是一個針對技術開發者的一個應用,你可以在掘金上獲取最新最優質的技術干貨,不僅僅是Android知識、前端、后端以至於產品和設計都有涉獵,想成為全棧工程師的朋友不要錯過!
android使用java作為其開發環境。java的跨平台和垃圾回收機制已經幫助我們解決了底層的一些問題。但是盡管有了垃圾回收機制,在開發android的時候仍然時不時的遇到out of memory的問題,這個時候我們不禁要問,垃圾回收機器去哪兒了?
我們主要講的是handler引起的泄漏,並給出三種解決辦法,其中最后一種方法就是我們想介紹的WeakHandler
庫。
可能導致泄漏問題的handler一般會被提示 Lint警告:
This Handler class should be static or leaks might occur 意思:class使用靜態聲明否者可能出現內存泄露。
這是一個基本的activity。在handler的post方法中我們加入了一個匿名的runnable,同時我將其執行延遲了整整80秒。我們運行這個程序,並且旋轉幾次手機,然后分析內存。
現在內存中有7個activity了,這太不靠譜了,所以我們來研究下為什么GC沒有清理它。(上圖中我查詢內存中activity列表時用的是oql(對象查詢語言),簡單強大的工具,ps 怎么用的,誰能告訴我?)
從上圖中我們可以看到其中一個對mainactivity的引用是來自this$0
,this$0
是什么呢?以下是關於this$0
的解釋:
-------什么是this$0
---------
非static的inner class里面都會有一個this$0的字段保存它的父對象。在Java中,非靜態(匿名)內部類會默認隱性引用外部類對象。而靜態內部類不會引用外部類對象。一個編譯后的inner class 很可能是這樣的:
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
再次執行,同時旋轉手機,分析內存如下:
尼瑪,還是一樣的。我們看看是誰還拉着activity不放:
在最底下我們發現activity繼續被DoneRunnable
里面mTextView中的mContext引用着。看來在這種情況下,看來僅僅使用static並沒有解決問題啊。還需要做點工作才行。
靜態的Runnable加WeakReference
既然是因為mTextView引起的,那我們把mTextView換成弱引用好了:
需要注意的,既然mTextView是弱引用,所以隨時都可能為null,因此需要在使用前判斷是否為空。好了繼續看看內存的情況:
all right,我想我們已經完美的解決問題了。總結一下我們做了哪些工作:
使用靜態的內部類
對所有handler/Runnable中的變量都用弱引用。
但是這種方式代碼是不是很多,而且還必須得小心翼翼。
在onDestroy中清理掉所有Messages
Handler有個很方便的方法:removeCallbacksAndMessages
,當參數為null的時候,可以清除掉所有跟次handler相關的Runnable和Message,我們在onDestroy中調用次方法也就不會發生內存泄漏了。
運行,旋轉手機
但是如果你對代碼有更高的要求,覺得這樣還不方便可以使用作者提供的WeakHandler
庫
WeakHandler
WeakHandler
使用起來和handler一模一樣,但是他是安全的,WeakHandler
使用如下:
你只需要把以前的Handler替換成WeakHandler
就行了。
WeakHandler
的實現原理
WeakHandler的思想是將Handler和Runnable做一次封裝,我們使用的是封裝后的
WeakHandler
,但其實真正起到handler作用的是封裝的內部,而封裝的內部對handler和runnable都是用的弱引用。
第一幅圖是普通handler的引用關系圖
第二幅圖是使用
WeakHandler
的引用關系圖
其實原文有對
WeakHandler
更多的解釋,但是表述起來也挺復雜的。
原文地址:https://techblog.badoo.com/blog/2014/10/09/calabash-android-query/