Message:消息;其中包含了消息ID,消息對象以及處理的數據等,由MessageQueue統一列隊,終由Handler處理異步
Handler:處理者;負責Message發送消息及處理。Handler經過與Looper進行溝通,從而使用Handler時,須要實現handlerMessage(Message msg)方法來對特定的Message進行處理,例如更新UI等(主線程中才行)async
MessageQueue:消息隊列;用來存放Handler發送過來的消息,並按照FIFO(先入先出隊列)規則執行。固然,存放Message並不是實際意義的保存,而是將Message以鏈表的方式串聯起來的,等Looper的抽取。ide
Looper:消息泵,不斷從MessageQueue中抽取Message執行。所以,一個線程中的MessageQueue須要一個Looper進行管理。Looper是當前線程建立的時候產生的(UI Thread即主線程是系統幫忙建立的Looper,而若是在子線程中,須要手動在建立線程後當即建立Looper[調用Looper.prepare()方法])。也就是說,會在當前線程上綁定一個Looper對象。函數
Thread:線程;負責調度消息循環,即消息循環的執行場所。oop
知識要點 測試
1、說明ui
一、handler應該由處理消息的線程建立。this
二、handler與建立它的線程相關聯,並且也只與建立它的線程相關聯。handler運行在建立它的線程中,因此,若是在handler中進行耗時的操做,會阻塞建立它的線程。spa
2、一些知識點線程
一、Android的線程分爲有消息循環的線程和沒有消息循環的線程,有消息循環的線程通常都會有一個Looper。主線程(UI線程)就是一個消息循環的線程。
二、獲取looper:
Looper.myLooper(); //得到當前的Looper
Looper.getMainLooper () //得到UI線程的Lopper
三、Handle的初始化函數(構造函數),若是沒有參數,那麼他就默認使用的是當前的Looper,若是有Looper參數,就是用對應的線程的Looper。
四、若是一個線程中調用Looper.prepare(),那麼系統就會自動的爲該線程創建一個消息隊列,而後調用 Looper.loop();以後就進入了消息循環,這個以後就能夠發消息、取消息、和處理消息。
消息處理機制原理:
1、大體流程:
在建立Activity以前,當系統啓動的時候,先加載ActivityThread這個類,在這個類中的main函數,調用了Looper.prepareMainLooper();方法進行初始化Looper對象;而後建立了主線程的handler對象(Tips:加載ActivityThread的時候,其內部的Handler對象[靜態的]還未建立);隨後才建立了ActivityThread對象;最後調用了Looper.loop();方法,不斷的進行輪詢消息隊列的消息。也就是說,在ActivityThread和Activity建立以前(一樣也是Handler建立以前,固然handler因爲這二者初始化),就已經開啓了Looper的loop()方法,不斷的進行輪詢消息。須要注意的是,這個輪詢的方法是阻塞式的,沒有消息就一直等待(實際是等着MessageQueue的next()方法返回消息)。在應用一執行的時候,就已經開啓了Looper,並初始化了Handler對象。此時,系統的某些組件或者其餘的一些活動等發送了系統級別的消息,這個時候主線程中的Looper就能夠進行輪詢消息,並調用msg.target.dispatchMessage(msg)(msg.target即爲handler)進行分發消息,並經過handler的handleMessage方法進行處理;因此會優於咱們本身建立的handler中的消息而處理系統消息。
0、準備數據和對象:
①、若是在主線程中處理message(即建立handler對象),那麼如上所述,系統的Looper已經準備好了(固然,MessageQueue也初始化了),且其輪詢方法loop已經開啓。【系統的Handler準備好了,是用於處理系統的消息】。【Tips:若是是子線程中建立handler,就須要顯式的調用Looper的方法prepare()和loop(),初始化Looper和開啓輪詢器】
②、經過Message.obtain()準備消息數據(實際是從消息池中取出的消息)
③、建立Handler對象,在其構造函數中,獲取到Looper對象、MessageQueue對象(從Looper中獲取的),並將handler做爲message的標籤設置到msg.target上
一、發送消息:sendMessage():經過Handler將消息發送給消息隊列
二、給Message貼上handler的標籤:在發送消息的時候,爲handler發送的message貼上當前handler的標籤
三、開啓HandlerThread線程,執行run方法。
四、在HandlerThread類的run方法中開啓輪詢器進行輪詢:調用Looper.loop()方法進行輪詢消息隊列的消息
【Tips:這兩步須要再斟酌,我的認爲這個類是本身手動建立的一個線程類,Looper的開啓在上面已經詳細說明了,這裏是說本身手動建立線程(HandlerThread)的時候,纔會在這個線程中進行Looper的輪詢的】
五、在消息隊列MessageQueue中enqueueMessage(Message msg, long when)方法裏,對消息進行入列,即依據傳入的時間進行消息入列(排隊)
六、輪詢消息:與此同時,Looper在不斷的輪詢消息隊列
七、在Looper.loop()方法中,獲取到MessageQueue對象後,從中取出消息(Message msg = queue.next())
八、分發消息:從消息隊列中取出消息後,調用msg.target.dispatchMessage(msg);進行分發消息
九、將處理好的消息分發給指定的handler處理,即調用了handler的dispatchMessage(msg)方法進行分發消息。
十、在建立handler時,複寫的handleMessage方法中進行消息的處理
十一、回收消息:在消息使用完畢後,在Looper.loop()方法中調用msg.recycle(),將消息進行回收,即將消息的全部字段恢復爲初始狀態
測試代碼:
/**
* Handler 構造函數測試
*/
public class HandlerConstructorTest extends Activity {
private Handler handler1 = new Handler(new Callback() {
@Override
public boolean handleMessage(Message msg) {
System.out.println("使用了Handler1中的接口Callback");
return false; // 此處,若是返回 false,下面的 handlerMessage方法會執行,true ,下面的不執行
}
});
private Handler handler2 = new Handler() {
public void handleMessage(Message msg) {
System.out.println("Handler2");
}
};
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//消息1
Message obtain1 = Message.obtain();
obtain1.obj = "sendMessage";
obtain1.what = 1;
handler1.sendMessage(obtain1);
//消息2
Message obtain2 = handler2.obtainMessage();
handler2.sendMessage(obtain2); //①
// handler2.dispatchMessage(obtain2); //②
}
}
2、詳細解釋:
1、準備Looper對象
兩種狀況初始化Looper對象:
1)在主線程中不須要顯式的建立Looper對象,直接建立Handler對象便可;由於在主線程ActivityThread的main函數中已經自動調用了建立Looper的方法:Looper.prepareMainLooper();,並在最後調用了Looper.loop()方法進行輪詢。
2)若是在子線程中建立Handler對象,須要建立Looper對象,即調用顯式的調用Looper.prepare()
初始化Looper的工做:
1)初始化Looper對象:經過調用Looper.prepare()初始化Looper對象,在這個方法中,新建立了Looper對象
2)將Looper綁定到當前線程:在初始化中,調用sThreadLocal.set(new Looper(quitAllowed))方法,將其和ThreadLocal進行綁定
在ThreadLocal對象中的set方法,是將當前線程和Looper綁定到一塊兒:首先獲取到當前的線程,並獲取線程內部類Values,經過Thread.Values的put方法,將當前線程和Looper對象進行綁定到一塊兒。即將傳入的Looper對象掛載到當前線程上。
Tips:在Looper對象中,能夠經過getThread()方法,獲取到當前線程,即此Looper綁定的線程對象。
源代碼:
Looper中:
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
ThreadLocal中:
public void set(T value) {
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values == null) {
values = initializeValues(currentThread);
}
values.put(this, value);
}
2、建立消息Message:
消息的建立能夠經過兩種方式:
1)new Message()
2)Message.obtain():【當存在多個handler的時候,能夠經過Message.obtain(Handler handler)建立消息,指定處理的handler對象】
Tips:建議使用第二種方式更好一些。緣由:
由於經過第一種方式,每有一個新消息,都要進行new一個Message對象,這會建立出多個Message,很佔內存。
而若是經過obtain的方法,是從消息池sPool中取出消息。每次調用obtain()方法的時候,先判斷消息池是否有消息(if (sPool != null)),沒有則建立新消息對象,有則從消息池中取出消息,並將取出的消息從池中移除【具體看obtain()方法】
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
sPoolSize--;
return m;
}
}
return new Message();
}
public Message() {
}
3、建立Handler對象
兩種形式建立Handler對象:
1)建立無參構造函數的Handler對象:
2)建立指定Looper對象的Handler對象
最終都會調用相應的含有Callback和boolean類型的參數的構造函數
【這裏的Callback是控制是否分發消息的,其中含有一個返回值爲boolean的handleMessage(Message msg)方法進行判斷的;
boolean類型的是參數是判斷是否進行異步處理,這個參數默認是系統處理的,咱們無需關心】
在這個構造函數中,進行了一系列的初始化工做:
①、獲取到當前線程中的Looper對象
②、經過Looper對象,獲取到消息隊列MessageQueue對象
③、獲取Callback回調對象
④、獲取異步處理的標記
源代碼:
①、建立無參構造函數的Handler對象:
public Handler() {
this(null, false);
}
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && (klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
②、建立指定Looper對象的Handler對象
public Handler(Looper looper) {
this(looper, null, false);
}
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
4、Handler對象發送消息:
1)Handler發送消息給消息隊列:
Handler對象經過調用sendMessage(Message msg)方法,最終將消息發送給消息隊列進行處理
這個方法(全部重載的sendMessage)最終調用的是enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)
(1)先拿到消息隊列:在調用到sendMessageAtTime(Message msg, long uptimeMillis)方法的時候,獲取到消息隊列(在建立Handler對象時獲取到的)
(2)當消息隊列不爲null的時候(爲空直接返回false,告知調用者處理消息失敗),再調用處理消息入列的方法:
enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)
這個方法,作了三件事:
①、爲消息打上標籤:msg.target = this;:將當前的handler對象這個標籤貼到傳入的message對象上,爲Message指定處理者
②、異步處理消息:msg.setAsynchronous(true);,在asyn爲true的時候設置
③、將消息傳遞給消息隊列MessageQueue進行處理:queue.enqueueMessage(msg, uptimeMillis);
public final boolean sendMessage(Message msg){
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis){
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
2)MessageQueue消息隊列處理消息:
在其中的enqueueMessage(Message msg, long when)方法中,工做以下:
在消息未被處理且handler對象不爲null的時候,進行以下操做(同步代碼塊中執行)
①、將傳入的處理消息的時間when(即爲上面的uptimeMillis)賦值爲當前消息的when屬性。
②、將next()方法中處理好的消息賦值給新的消息引用:Message p = mMessages;
在next()方法中:不斷的從消息池中取出消息,賦值給mMessage,當沒有消息發來的時候,Looper的loop()方法因爲是阻塞式的,就一直等消息傳進來
③、當傳入的時間爲0,且next()方法中取出的消息爲null的時候,將傳入的消息msg入列,排列在消息隊列上,此時爲消息是先進先出的
不然,進入到死循環中,不斷的將消息入列,根據消息的時刻(when)來排列發送過來的消息,此時消息是按時間的前後進行排列在消息隊列上的
final boolean enqueueMessage(Message msg, long when) {
if (msg.isInUse()) {
throw new AndroidRuntimeException(msg + " This message is already in use.");
}
if (msg.target == null) {
throw new AndroidRuntimeException("Message must have a target.");
}
boolean needWake;
synchronized (this) {
if (mQuiting) {
RuntimeException e = new RuntimeException(msg.target + " sending message to a Handler on a dead thread");
Log.w("MessageQueue", e.getMessage(), e);
return false;
}
msg.when = when;
Message p = mMessages;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
}
if (needWake) {
nativeWake(mPtr);
}
return true;
}
5、輪詢Message
1)開啓loop輪詢消息
當開啓線程的時候,執行run方法,在HandlerThread類中,調用的run方法中將開啓loop進行輪詢消息隊列:
在loop方法中,先拿到MessageQueue對象,而後死循環不斷從隊列中取出消息,當消息不爲null的時候,經過handler分發消息:msg.target.dispatchMessage(msg)。消息分發完以後,調用msg.recycle()回收消息,
2)回收消息:
在Message的回收消息recycle()這個方法中:首先調用clearForRecycle()方法,將消息的全部字段都恢復到原始狀態【如flags=0,what=0,obj=null,when=0等等】
而後在同步代碼塊中將消息放回到消息池sPool中,從新利用Message對象
源代碼:
Looper.loop()
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
return;
}
// This must be in a local variable, in case a UI event sets the logger
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
msg.target.dispatchMessage(msg);
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, 「……」);
}
msg.recycle();
}
}
msg.recycle();:
public void recycle() {
clearForRecycle();
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
/*package*/ void clearForRecycle() {
flags = 0;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
when = 0;
target = null;
callback = null;
data = null;
}
六、處理Message
在Looper.loop()方法中調用了msg.target.dispatchMessage(msg);的方法,就是調用了Handler中的dispatchMessage(Message msg)方法:
1)依據Callback中的handleMessage(msg)的真假判斷是否要處理消息,若是是真則不進行消息分發,則不處理消息,不然進行處理消息
2)當Callback爲null或其handleMessage(msg)的返回值爲false的時候,進行分發消息,即調用handleMessage(msg)處理消息【這個方法須要本身複寫】
/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) {
}
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
==========
場景一:
在主線程中建立Handler,其中複寫了handlerMessager方法(處理message,更新界面)
而後建立子線程,其中建立Message對象,並設置消息,經過handler發送消息
示例代碼:
public class MainActivity2 extends Activity implements OnClickListener{
private Button bt_send;
private TextView tv_recieve;
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
tv_recieve.setText((String) msg.obj);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bt_send = (Button) findViewById(R.id.bt_send);
tv_recieve = (TextView) findViewById(R.id.tv_recieve);
bt_send.setOnClickListener(this);
tv_recieve.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.bt_send:
new Thread(){
public void run() {
Message msg = new Message();
msg.obj = "消息來了"+ System.currentTimeMillis();
handler.sendMessage(msg);
}
}.start();
break;
}
}
}
執行過程:
一、Looper.prepare()
在當前線程(主線程)中準備一個Looper對象,即輪詢消息隊列MessageQueue的對象;此方法會建立一個Looper,在Looper的構造函數中,初始化的建立了一個MessageQueue對象(用於存放消息),並準備好了一個線程供調用
二、new Hnader():
在當前線程中建立出Handler,須要複寫其中的handleMessage(Message msg),對消息進行處理(更新UI)。在建立Handler中,會將Looper設置給handler,並隨帶着MessageQueue對象;其中Looper是經過調用其靜態方法myLooper(),返回的是ThreadLocal中的currentThread,並準備好了MessageQueue【mQueue】
三、Looper.loop():
無限循環,對消息隊列進行不斷的輪詢,若是沒有獲取到消息,就會結束循環;若是有消息,直接從消息隊列中取出消息,並經過調用msg.target.dispatchMessage(msg)進行分發消息給各個控件進行處理。
[其中的msg.target實際就是handler]。
四、建立子線程,handler.sendMessage(msg)
在handler.sendMessage(msg)方法中,實際上最終調用sendMessageAtTime(Message msg,long uptimeMillis)方法[sendMessageXXX方法都是最終調用的sendMessageAtTime方法];此方法返回的enqueueMessage(queue,msg,uptimeMillis),實際上返回的是MessageQueue中的enqueueMessage(msg,uptimeMillis),其中進行的操做時對存入的消息進行列隊,即根據接收到的消息的時間前後進行排列[使用的單鏈形式];而後將消息就都存入到了消息隊列中,等待着handler進行處理。
場景二:
建立兩個子線程,一個線程中建立Handler並進行處理消息,另外一個線程使用handler發送消息。
示例代碼:
public class MainActivity extends Activity implements OnClickListener{
private Button bt_send;
private TextView tv_recieve;
private Handler handler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bt_send = (Button) findViewById(R.id.bt_send);
tv_recieve = (TextView) findViewById(R.id.tv_recieve);
bt_send.setOnClickListener(this);
tv_recieve.setOnClickListener(this);
new Thread(){
public void run() {
//Looper.prepare();
handler = new Handler(Looper.getMainLooper()){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
tv_recieve.setText((String) msg.obj);
}
};
//Looper.loop();
}
}.start();
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.bt_send:
new Thread(){
public void run() {
Message msg = new Message();
msg.obj = "消息來了"+ System.currentTimeMillis();
handler.sendMessage(msg);
}
}.start();
break;
}
}
}
簡單說明執行過程:
說明:在子線程中是不能更新界面的操做的,只能放在主線程中進行更新。因此必須將處理的消息放到主線程中,才能進行更新界面,不然會報錯
一、子線程中建立Handler,並處理消息
1)建立Handler:
源碼以下:
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
這個構造函數作了一下幾步工做:
①、建立輪詢器:
因爲新建立的子線程中沒有輪詢器,就須要建立一個輪詢器,才能進行消息的輪詢處理。傳入的是主線程的輪詢器,就已經將這個looper綁定到主線程上了【傳入哪一個線程的Looper,就綁定在哪一個線程上】
②、將消息隊列加入到輪詢器上。
消息隊列MessageQueue是存放handler發來的消息的,等着Looper進行輪詢獲取;在一個線程中的MessageQueue須要一個Looper進行管理,因此二者須要同在一個線程中。
③、回調和異步加載。(此處不作分析[其實我還沒分析好])
須要注意的是界面更新:
上面說到了,在子線程中是不能夠進行更新界面的操做的,這就須要使用帶有輪詢器參數的handler構造函數進行建立,傳入主線程的輪詢器:Looper.getMainLooper(),從而將消息加入到主線程的消息隊列之中。所以就可進行在handleMessage方法中進行處理消息更新界面了。
2)、消息處理:
複寫其中的handleMessage(Message msg),對消息進行處理(更新UI)。在建立Handler中,會將Looper設置給handler,並隨帶着MessageQueue對象;其中Looper是經過調用其靜態方法myLooper(),返回的是ThreadLocal中的currentThread,並準備好了MessageQueue【mQueue】
雖然是在子線程中編寫的代碼,可是因爲傳入的是主線程的looper,因此,Looper從MessageQueue隊列中輪詢獲取消息、再進行更新界面的操做都是在主線程中執行的。
3)、Looper.loop():
說明:因爲傳入的是主線程的Looper,而在主線程中已經有這一步操做了,因此這裏就不須要進行顯示的調用了。可是主線程在這個時候是作了這個輪詢的操做的。
無限循環,對消息隊列進行不斷的輪詢,若是沒有獲取到消息,就會結束循環;若是有消息,直接從消息隊列中取出消息,並經過調用msg.target.dispatchMessage(msg)進行分發消息給各個控件進行處理。
[其中的msg.target實際就是handler]。
二、建立子線程,發送消息handler.sendMessage(msg)
新開一個子線程,發送消息給另外一個子線程
在handler.sendMessage(msg)方法中,實際上最終調用sendMessageAtTime(Message msg,long uptimeMillis)方法[sendMessageXXX方法都是最終調用的sendMessageAtTime方法]
此方法返回的enqueueMessage(queue,msg,uptimeMillis),實際上返回的是MessageQueue中的enqueueMessage(msg,uptimeMillis),其中進行的操做時對存入的消息進行列隊,即根據接收到的消息的時間前後進行排列[使用的單鏈形式];而後將消息就都存入到了消息隊列中,等待着handler進行處理。
dub本身總結: