天天看點

Android消息處理機制——Looper,Handler,Message,MessageQueue,Thread源碼解析線程之間的消息如何進行傳遞示例

Android消息機制,通常我們也稱為Handler機制,在實際開發中,我們通常使用Handler來控制主UI界面的變化,Handler的作用可以總結為:與其他線程協同工作,接收其他線程的消息并通過接收到的消息更新主UI線程的内容。

Android消息處理機制——Looper,Handler,Message,MessageQueue,Thread源碼解析線程之間的消息如何進行傳遞示例

Message:消息,由MessageQueue統一列隊,終由Handler處理。

Handler:處理者,負責Message的發送及處理。

MessageQueue:消息隊列,用來存放Handler發送過來的消息。

Looper:消息泵,不斷地從MessageQueue中抽取Message執行。一個線程中隻有一個Looper執行個體。

Thread:線程,負責排程整個消息循環,即消息循環的執行場所。

消息處理總體流程:

Android消息處理機制——Looper,Handler,Message,MessageQueue,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>