| 目錄(?)[+] |
曾幾何時(shí),我們用原來的辦法使用 Handler 時(shí)會有下面一段溫馨的提示:
This Handler class should be static or leaks might occur
下面是更詳細(xì)的說明(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 被聲明為內(nèi)部類,那么可能導(dǎo)致它的外部類不能夠被垃圾回收。如果 Handler 是在其他線程(我們通常成為 worker thread)使用 Looper 或 MessageQueue(消息隊(duì)列),而不是 main 線程(UI 線程),那么就沒有這個(gè)問題。如果 Handler 使用 Looper 或 MessageQueue 在主線程(main thread),你需要對 Handler 的聲明做如下修改:
聲明 Handler 為 static 類;在外部類中實(shí)例化一個(gè)外部類的 WeakReference(弱引用)并且在 Handler 初始化時(shí)傳入這個(gè)對象給你的 Handler;將所有引用的外部類成員使用 WeakReference 對象。
上面的描述中基本上把推薦的修改方法明確表達(dá)了出來,下面的代碼是我自己使用中的一個(gè)實(shí)現(xiàn),請參考:
private CopyFileHandler mHandler;
@Override
protected 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!
}
}
那么為什么不這樣做會引發(fā)內(nèi)存泄漏呢?
這與幾個(gè)關(guān)鍵詞有關(guān):內(nèi)部類、Handler 的消息循環(huán)(Looper)、Java 垃圾回收機(jī)制。
需要強(qiáng)調(diào)一下,并不是每次使用 Handler 都會引發(fā)內(nèi)存泄漏,這里面有一定的幾率,需要滿足特定條件才會引起泄漏。
內(nèi)部類會有一個(gè)指向外部類的引用。
垃圾回收機(jī)制中約定,當(dāng)內(nèi)存中的一個(gè)對象的引用計(jì)數(shù)為0時(shí),將會被回收。
Handler 作為 Android 上的異步消息處理機(jī)制(好吧,我大多用來進(jìn)行 worker thread 與 UI 線程同步),它的工作是需要 Looper 和 MessageQueue 配合的。簡單的說,要維護(hù)一個(gè)循環(huán)體(Looper)處理消息隊(duì)列(MessageQueue)。每循環(huán)一次就從 MessageQueue 中取出一個(gè) Message,然后回調(diào)相應(yīng)的消息處理函數(shù)。
如果,我是說如果,循環(huán)體中有消息未處理(Message 排隊(duì)中),那么 Handler 會一直存在,那么 Handler 的外部類(通常是 Activity)的引用計(jì)數(shù)一直不會是0,所以那個(gè)外部類就不能被垃圾回收。很多人會遇到 activity 的 onDestroy 方法一直不執(zhí)行就是這個(gè)原因。
警告描述中提到了 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