首先說說Handler 使用中隱藏的坑
1、delay的時間過長,導致 activity未被回收內存泄漏以及邏輯錯誤
可以將Handler攜程static靜態內部類,或者而降handler中引用的activity位軟引用
2、new 了過多的message,導致內存泄漏,應該在處理后remove這些msg
3、Activity finish()后應該remove所有的msg和runable
------------------------------------------------------------------------------------------------------------------------------
各種類的含義
Message:消息,其中包含了消息ID,消息處理對象以及處理的數據等,由MessageQueue統一列隊,終由Handler處理。
Handler:處理者,負責Message的發送及處理。使用Handler時,需要實現handleMessage(Message msg)方法來對特定的Message進行處理,例如更新UI等。
MessageQueue:消息隊列,用來存放Handler發送過來的消息,並按照FIFO規則執行。當然,存放Message並非實際意義的保存,而是將Message以鏈表的方式串聯起來的,等待Looper的抽取。
Looper:消息泵,不斷地從MessageQueue中抽取Message執行。因此,一個MessageQueue需要一個Looper。
Thread:線程,負責調度整個消息循環,即消息循環的執行場所。
------------------------------------------------------------------------------------------------------------------------------
關系
Handler,Looper和MessageQueue就是簡單的三角關系。
Looper和MessageQueue一一對應,創建一個Looper的同時,會創建一個MessageQueue。
而Handler與它們的關系,只是簡單的聚集關系,即Handler里會引用當前線程里的特定Looper和MessageQueue。
這樣說來,多個Handler都可以共享同一Looper和MessageQueue了。
這些Handler也就運行在同一個線程里,每個線程一個Loop而 一個MessageQueue。
------------------------------------------------------------------------------------------------------------------------------------------------------------
使用:
子線程網主線程中發消息,直接在主線程中handler = new Handler(),子線程中可以直接用handler.sendmessage。。。
主線程網子線程中,Looper.prepare()... handler = new Handler()... looper.loop...然后handler.sendmessage
一個Message經由Handler的發送,MessageQueue的入隊,Looper的抽取,又再一次地回到Handler的懷抱
Handler的處理過程運行在創建Handler的線程里
· 一個Looper對應一個MessageQueue
· 一個線程對應一個Looper
· 一個Looper可以對應多個Handler
· 不確定當前線程時,更新UI時盡量調用post方法
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Android的主線程循環創建
Android程序的運行入口點可以認為是android.app.ActivityThread類的main()方法(源碼2.3.3):
public static final void main(String[] args) { // other codes... // 創建主線程循環 Looper.prepareMainLooper(); if (sMainThreadHandler == null) { sMainThreadHandler = new Handler(); } ActivityThread thread = new ActivityThread(); thread.attach(false); // other codes... // 進入當前線程(此時是主線程)消息循環 Looper.loop(); // other codes... thread.detach(); // other codes... }
這個main()方法里面為程序創建了主線程循環。
Looper類中的主線程創建方法prepareMainLooper():
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
ThreadLocal為每個使用該變量的線程提供獨立的變量副本,所以每一個線程都可以獨立地改變自己的副本,而不會影響其它線程所對應的副本。
今天用子線程調Toast報了一個Can't create handler inside thread that has not calledLooper.prepare()錯誤。
解決辦法很簡單:
Looper.prepare();
new handler...
Looper.loop();
Looper
public static final void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper()); //在當前線程中創建一個Looper
}
private Looper() {
mQueue = new MessageQueue(); //關鍵在這,創建Looper都干了什么。 其實是創建了消息隊列
mRun = true;
mThread = Thread.currentThread();
}
一般如果不是在主線程中又開啟了新線程的話,一般都會碰到這個問題。
原因是在創建新線程的時候默認情況下不會去創建新的MessageQueue。