Android實戰技巧之三十八:Handler使用中可能引發的記憶體流失,androidhandler
問題描述
曾幾何時,我們用原來的辦法使用Handler時會有下面一段溫馨的提示:
This Handler class should be static or leaks might occur
下面是更詳細的說明(Android Studio上的警告,不知道Eclipse上是否相同)
Since this Handler is declared as an inner class, it may prevent the outer class from being garbage collected. If the Handler is using a Looper or MessageQueue for a thread other than the main thread, then there is no issue. If the Handler is using the Looper or MessageQueue of the main thread, you need to fix your Handler declaration, as follows: Declare the Handler as a static class; In the outer class, instantiate a WeakReference to the outer class and pass this object to your Handler when you instantiate the Handler; Make all references to members of the outer class using the WeakReference object.
大概意思就是:
一旦Handler被聲明為內部類,那麼可能導致它的外部類不能夠被記憶體回收。如果Handler是在其他線程(我們通常成為worker thread)使用Looper或MessageQueue(訊息佇列),而不是main線程(UI線程),那麼就沒有這個問題。如果Handler使用Looper或MessageQueue在主線程(main thread),你需要對Handler的聲明做如下修改:
聲明Handler為static類;在外部類中執行個體化一個外部類的WeakReference(弱引用)並且在Handler初始化時傳入這個對象給你的Handler;將所有引用的外部類成員使用WeakReference對象。
解決方案一
上面的描述中基本上把推薦的修改方法明確表達了出來,下面的代碼是我自己使用中的一個實現,請參考:
private CopyFileHandler mHandler;@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_appstart); mHandler = new CopyFileHandler(this); startCopyDBThread();}private void startCopyFileThread(){ Log.d(TAG, "startCopyDBThread"); new Thread(new Runnable() { @Override public void run() { //DO SOMETHING LIKE: copyDBFile(); Message msg=mHandler.obtainMessage(); mHandler.sendMessage(msg); } }).start();}private static class CopyFileHandler extends Handler { WeakReference<AppStartActivity> mActivity; public CopyFileHandler(AppStartActivity activity) { mActivity = new WeakReference<>(activity); } public void handleMessage(Message msg) { final AppStartActivity activity = mActivity.get(); //handle you message here! }}
為什麼會記憶體流失
那麼為什麼不這樣做會引發記憶體流失呢?
這與幾個關鍵詞有關:內部類、Handler的訊息迴圈(Looper)、Java記憶體回收機制。
需要強調一下,並不是每次使用Handler都會引發記憶體流失,這裡面有一定的幾率,需要滿足特定條件才會引起泄漏。
內部類會有一個指向外部類的引用。
記憶體回收機制中約定,當記憶體中的一個對象的引用計數為0時,將會被回收。
Handler作為Android上的非同步訊息處理機制(好吧,我大多用來進行worker thread與UI線程同步),它的工作是需要Looper和MessageQueue配合的。簡單的說,要維護一個迴圈體(Looper)處理訊息佇列(MessageQueue)。每迴圈一次就從MessageQueue中取出一個Message,然後回調相應的訊息處理函數。
如果,我是說如果,迴圈體中有訊息未處理(Message排隊中),那麼Handler會一直存在,那麼Handler的外部類(通常是Activity)的引用計數一直不會是0,所以那個外部類就不能被記憶體回收。很多人會遇到activity的onDestroy方法一直不執行就是這個原因。
另一個解決方案的嘗試
警告描述中提到了Handler在worker thread中使用Looper或MessageQueue,我嘗試了一下,請大家品鑒。
private Handler testHandler; private Thread mThread = new Thread() { public void run() { Log.d(TAG,"mThread run"); Looper.prepare(); testHandler = new Handler() { public void handleMessage(Message msg) { Log.d("TAG", "worker thread:"+Thread.currentThread().getName()); switch (msg.what) { //handle message here } } }; Looper.loop(); } }; //start thread here if(Thread.State.NEW == mThread.getState()) { Log.d(TAG, "mThread name: " + mThread.getName()); mThread.start(); } //send message here testHandler.sendEmptyMessage(1);
參考:
http://stackoverflow.com/questions/11407943/this-handler-class-should-be-static-or-leaks-might-occur-incominghandler
http://m.blog.csdn.net/blog/wurensen/41907663
http://blog.csdn.net/lmj623565791/article/details/38377229
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。