Android消息機制,通常我們也稱為Handler機制,在實際開發中,我們通常使用Handler來控制主UI界面的變化,Handler的作用可以總結為:與其他線程協同工作,接收其他線程的消息并通過接收到的消息更新主UI線程的内容。
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiN5YTN1MzMwIDNygDM1EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
Message:消息,由MessageQueue統一列隊,終由Handler處理。
Handler:處理者,負責Message的發送及處理。
MessageQueue:消息隊列,用來存放Handler發送過來的消息。
Looper:消息泵,不斷地從MessageQueue中抽取Message執行。一個線程中隻有一個Looper執行個體。
Thread:線程,負責排程整個消息循環,即消息循環的執行場所。
消息處理總體流程:
在消息使用完畢後,在Looper.loop()方法中調用msg.recycle(),将消息進行回收,即将消息的所有字段恢複為初始狀态。
源碼解析
Looper
對于Looper主要是prepare()和loop()兩個方法。
首先看prepare()方法:
public static final void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(true));
}
sThreadLocal是一個ThreadLocal對象,可以在一個線程中存儲變量。可以看到,在第5行,将一個Looper的執行個體放入了ThreadLocal,并且2-4行判斷了sThreadLocal是否為null,否則抛出異常。這也就說明了Looper.prepare()方法不能被調用兩次,同時也保證了一個線程中隻有一個Looper執行個體。
Looper的構造方法:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mRun = true;
mThread = Thread.currentThread();
}
在構造方法中,建立了一個MessageQueue(消息隊列)。
然後我們看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;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
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);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycle();
}
}
...
public static Looper myLooper() {
return sThreadLocal.get();
}
...
第2行:方法直接傳回了sThreadLocal存儲的Looper執行個體,如果me為null則抛出異常,也就是說looper方法必須在prepare方法之後運作。
第6行:拿到該looper執行個體中的mQueue(消息隊列)
13到45行:就進入了我們所說的無限循環。
14行:取出一條消息,如果沒有消息則阻塞。
27行:使用調用 msg.target.dispatchMessage(msg);把消息交給msg的target的dispatchMessage方法去處理。Msg的target是什麼呢?其實就是handler對象,下面會進行分析。
44行:釋放消息占據的資源。
Looper主要作用:
1、 與目前線程綁定,保證一個線程隻會有一個Looper執行個體,同時一個Looper執行個體也隻有一個MessageQueue。
2、 loop()方法,不斷從MessageQueue中去取消息,交給消息的target屬性的dispatchMessage去處理。
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) == ) {
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;
}
14行:通過Looper.myLooper()擷取了目前線程儲存的Looper執行個體,然後在19行又擷取了這個Looper執行個體中儲存的MessageQueue(消息隊列),這樣就保證了handler的執行個體與我們Looper執行個體中MessageQueue關聯上了。
然後看我們最常用的sendMessage方法:
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, );
}
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < ) {
delayMillis = ;
}
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);
}
輾轉反側最後調用了sendMessageAtTime,在此方法内部有直接擷取MessageQueue然後調用了enqueueMessage方法,我們再來看看此方法:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
enqueueMessage中首先為meg.target指派為this,也就是把目前的handler作為msg的target屬性。最終會調用queue的enqueueMessage的方法,也就是說handler發出的消息,最終會儲存到消息隊列中去。
當Looper擷取到消息後,回調建立這個消息的handler中的dispathMessage方法,下面來看一下這個方法:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
第10行,調用了handleMessage方法,下面我們去看這個方法:
/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) {
}
可以看到這是一個空方法,消息最終回調是我們在建立handler時複寫handleMessage方法。
總結:
1、首先Looper.prepare()在本線程中儲存一個Looper執行個體,然後該執行個體中儲存一個MessageQueue對象;因為Looper.prepare()在一個線程中隻能調用一次,是以MessageQueue在一個線程中隻會存在一個。
2、Looper.loop()會讓目前線程進入一個無限循環,不斷從MessageQueue的執行個體中讀取消息,然後回調msg.target.dispatchMessage(msg)方法。
3、Handler提供了多個構造方法,如果有手動傳入的參數Looper,則與傳入的Looper所在的線程相關聯,如果預設沒有傳入Looper則會得到目前線程中儲存的Looper執行個體,進而與目前線程相關聯。
4、Handler的sendMessage方法,會給msg的target指派為handler自身,然後加入MessageQueue中。
5、在構造Handler執行個體時,我們會重寫handleMessage方法,也就是msg.target.dispatchMessage(msg)最終調用的方法。
Message
message有這麼幾點需要注意:
1.盡管Message有public的預設構造方法,但是你應該通過Message.obtain()來從消息池中獲得空消息對象,以節省資源。
2.如果你的message隻需要攜帶簡單的int資訊,請優先使用Message.arg1和Message.arg2來傳遞資訊,這比用Bundle更省記憶體
3.擅用message.what來辨別資訊,以便用不同方式處理message。
拓展
1、在主線程(UI線程)裡,我們并沒有顯示的調用Looper.prepare()和Looper.loop()方法,建立Handler時也沒有傳入Looper對象,是因為在Activity的啟動代碼中,已經在目前UI線程調用了Looper.prepare()和Looper.loop()方法。Looper對象直接使用主線程(UI線程)的Looper對象。
2、在其它線程裡,如果建立Handler時不傳入Looper對象,那麼,這個Handler将不能接收處理消息。在這種情況下,通用的作法是:
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
}
3、ANR異常的了解:當使用者執行操作(比如點選了一個按鈕)系統會生成一個Message對象,把使用者操作的資訊寫入Message對象,并把這個Message對象壓入MessageQueue隊列的尾部。系統過一段時間(一般是五秒)後會再來檢查,剛剛放入的資訊是不是已經被處理了,如果資訊還在隊列中就表明。處理前面資訊的過程當中發生的阻塞,使用者的操作沒有及時得到響應。系統彈出ANR對話框。
4、UI線程需要保持一直運作的狀态,是以要有一個循環保持這個線程不會死掉,但這個線程又必需阻塞,以減少cpu的消耗。android中的這個循環就是通過Looper實作的。有了這個 Looper,Looper就占據了整個線程,導緻所有的方法想在些線程中運作就必需通過這個Looper,是以要有個方法可以進入這個Looper的内部。MessageQueue就擔當了這個通道 的角色。Message擔當了集合的角色。所有在UI線程中運作的方法都必需通過MessageQueue進入Looper内部,不管是使用者定義的方法還是系統事件包括onCreate(),onStop(),使用者點選事件等等。
5、不是所有的Handler都能更新UI
Handler處理消息總是在建立Handler的線程裡運作。而我們的消息進行中,不乏更新UI的操作,不正确的線程直接更新UI将引發異常。是以,需要時刻關心Handler在哪個線程裡建立的。如何更新UI才能不出異常呢?SDK告訴我們,有以下4種方式可以從其它線程通路UI線程(也即線程間通信):
Activity.runOnUiThread(Runnable)
View.post(Runnable)
View.postDelayed(Runnable, long)
在UI線程中建立的Handler
其中,重點說一下的是View.post(Runnable)方法。在post(Runnableaction)方法裡,View獲得目前線程(即UI線程)的Handler,然後将action對象post到Handler裡。在Handler裡,它将傳遞過來的action對象包裝成一個Message(Message的callback為action),然後将其投入UI線程的消息循環中。在 Handler再次處理該Message時,有一條分支(未解釋的那條)就是為它所設,直接調用runnable的run方法。而此時,已經路由到UI線程裡,是以,我們可以毫無顧慮的來更新UI。
幾點小結 :
一個Looper對應一個MessageQueue,一個線程對應一個Looper,一個Looper可以對應多個Handler
不确定目前線程時,更新UI時盡量調用View.post方法。
handler應該由處理消息的線程建立,Handler的處理過程運作在建立Handler的線程裡 。handler與建立它的線程相關聯,而且也隻與建立它的線程相關聯。handler運作在建立它的線程中,是以,如果在handler中進行耗時的操作,會阻塞建立它的線程。
Android的線程分為有消息循環的線程和沒有消息循環的線程,有消息循環的線程一般都會有一個Looper。主線程(UI線程)就是一個消息循環的線程。
Looper.myLooper(); //獲得目前的Looper Looper.getMainLooper() //獲得UI線程的Lopper
Handle的初始化函數(構造函數),如果沒有參數,那麼他就預設使用的是目前的Looper,如果有Looper參數,就是用對應的線程的Looper。 如果一個線程中調用Looper.prepare(),那麼系統就會自動的為該線程建立一個消息隊列,然後調用Looper.loop();之後就進入了消息循環,這個之後就可以發消息、取消息、和處理消息。
線程之間的消息如何進行傳遞示例
主線程給自己發送Message
package test.message;
import android .app .Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.View ;
import android.widget.Button;
import android.widget.TextView ;
public class MainActivity extends Activity {
private Button btnTest;
private TextView textView;
private Handler handler ;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
btnTest = (Button)this.findViewById(R.id.btn_01);
textView = (TextView)this.findViewById(R.id.view_01);
btnTest.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View arg0) {
Looper looper = Looper.getMainLooper(); //主線程的Looper對象
//這裡以主線程的Looper對象建立了handler ,
//是以,這個handler 發送的Message會被傳遞給主線程的MessageQueue。
handler = new MyHandler(looper);
handler .removeMessages();
//建構Message對象
//第一個參數:是自己指定的message代号,友善在handler 選擇性地接收
//第二三個參數沒有什麼意義
//第四個參數需要封裝的對象
Message msg = handler .obtainMessage(,,,"主線程發消息了");
handler .sendMessage(msg); //發送消息
}
});
}
class MyHandler extends Handler{
public MyHandler(Looper looper){
super(looper);
}
public void handleMessage(Message msg){
super.handleMessage(msg);
textView.setText("我是主線程的Handler,收到了消息:"+(String)msg.obj);
}
}
}
其他線程給主線程發送Message
package test.message;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends Activity {
private Button btnTest;
private TextView textView;
private Handler handler ;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
btnTest = (Button)this.findViewById(R.id.btn_01);
textView = (TextView)this.findViewById(R.id.view_01);
btnTest.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View arg0) {
//可以看出這裡啟動了一個線程來操作消息的封裝和發送的工作
//這樣原來主線程的發送就變成了其他線程的發送,簡單吧?呵呵
new MyThread().start();
}
});
}
class MyHandler extends Handler{
public MyHandler(Looper looper){
super(looper);
}
public void handleMessage(Message msg){
super.handleMessage(msg);
textView.setText("我是主線程的Handler,收到了消息:"+(String)msg.obj);
}
}
//加了一個線程類
class MyThread extends Thread{
public void run(){
Looper looper = Looper.getMainLooper(); //主線程的Looper對象
//這裡以主線程的Looper對象建立了handler ,
//是以,這個handler 發送的Message會被傳遞給主線程的MessageQueue。
handler = new MyHandler(looper);
//建構Message對象
//第一個參數:是自己指定的message代号,友善在handler 選擇性地接收
//第二三個參數沒有什麼意義
//第四個參數需要封裝的對象
Message msg = handler .obtainMessage(,,,"其他線程發消息了");
handler .sendMessage(msg); //發送消息
}
}
}
主線程給其他線程發送Message
package test.message;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends Activity {
private Button btnTest;
private TextView textView;
private Handler handler ;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
btnTest = (Button)this.findViewById(R.id.btn_01);
textView = (TextView)this.findViewById(R.id.view_01);
//啟動線程
new MyThread().start();
btnTest.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View arg0) {
//這裡handler 的執行個體化線上程中
//線程啟動的時候就已經執行個體化了
Message msg = handler .obtainMessage(,,,"主線程發送的消息");
handler .sendMessage(msg);
}
});
}
class MyHandler extends Handler{
public MyHandler(Looper looper){
super(looper);
}
public void handleMessage(Message msg){
super.handleMessage(msg);
textView.setText("我是主線程的Handler,收到了消息:"+(String)msg.obj);
}
}
class MyThread extends Thread{
public void run(){
Looper.prepare(); //建立該線程的Looper對象,用于接收消息
//注意了:這裡的handler 是定義在主線程中的哦,呵呵,
//前面看到直接使用了handler 對象,是不是在找,在什麼地方執行個體化的呢?
//現在看到了吧???呵呵,開始的時候執行個體化不了,因為該線程的Looper對象
//還不存在呢。現在可以執行個體化了
//這裡Looper.myLooper()獲得的就是該線程的Looper對象了
handler = new ThreadHandler(Looper.myLooper());
//這個方法,有疑惑嗎?
//其實就是一個循環,循環從MessageQueue中取消息。
//不經常去看看,你怎麼知道你有新消息呢???
Looper.loop();
}
//定義線程類中的消息處理類
class ThreadHandler extends Handler{
public ThreadHandler(Looper looper){
super(looper);
}
public void handleMessage(Message msg){
//這裡對該線程中的MessageQueue中的Message進行處理
//這裡我們再傳回給主線程一個消息
handler = new MyHandler(Looper.getMainLooper());
Message msg2 = handler .obtainMessage(,,,"子線程收到:"+(String)msg.obj);
handler .sendMessage(msg2);
}
}
}
}
其他線程給自己發送Message
package test.message;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends Activity {
private Button btnTest;
private TextView textView;
private Handler handler ;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
btnTest = (Button)this.findViewById(R.id.btn_01);
textView = (TextView)this.findViewById(R.id.view_01);
btnTest.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View arg0) {
//啟動線程
new MyThread().start();
}
});
}
class MyHandler extends Handler{
public MyHandler(Looper looper){
super(looper);
}
public void handleMessage(Message msg){
super.handleMessage(msg);
textView.setText((String)msg.obj);
}
}
class MyThread extends Thread{
public void run(){
Looper.prepare(); //建立該線程的Looper對象
//這裡Looper.myLooper()獲得的就是該線程的Looper對象了
handler = new ThreadHandler(Looper.myLooper());
Message msg = handler .obtainMessage(,,,"我自己");
handler .sendMessage(msg);
Looper.loop();
}
//定義線程類中的消息處理類
class ThreadHandler extends Handler{
public ThreadHandler(Looper looper){
super(looper);
}
public void handleMessage(Message msg){
//這裡對該線程中的MessageQueue中的Message進行處理
//這裡我們再傳回給主線程一個消息
//加入判斷看看是不是該線程自己發的資訊
if(msg.what == && msg.obj.equals("我自己")){
handler = new MyHandler(Looper.getMainLooper());
Message msg2 = handler .obtainMessage(,,,"禀告主線程:我收到了自己發給自己的Message");
handler .sendMessage(msg2);
}
}
}
}
}
用到的xml檔案,main.xml :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android rientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView android:id="@+id/view_01"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello "
/>
<Button android:id="@+id/btn_01"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="測試消息" />
</LinearLayout>