天天看點

Android消息處理機制—— Looper, Handler, Message

Android消息處理有三大核心類:Looper, Handler和Message,下面就是詳細的诠釋這三者作用與關聯。

Looper

Looper時常被稱為消息泵,它是被設計用來使一個普通線程程式設計Looper線程,所謂的Looper線程就是循環工作的線程。這樣的線程會不斷循環,一旦有新任務則執行,執行完之後繼續等待下一個任務。使用Looper類建立Looper線程隻有兩行核心代碼:

public class LooperThread extends Thread {
    @Override
    public void run() {
        // 将目前線程初始化為Looper線程
        Looper.prepare();

        // ...其他處理,如執行個體化handler

        // 開始循環處理消息隊列
        Looper.loop();
    }
}
           

這樣就把該LooperThread線程轉化為Looper線程了。

1.  Looper.prepare()
           

圖1 普通線程轉化為Looper線程

Android消息處理機制—— Looper, Handler, Message

通過Looper類将一個普通線程轉化為Looper線程,則在該Looper線程中,有一個Looper對象,它的内部維護了一個消息隊列MessageQueue。其中,一個線程中至多隻能有一個Looper對象,這一點在源碼中可以看出:

public class Looper {
    // 每個線程中的Looper對象其實是一個ThreadLocal,即線程本地存儲(TLS)對象
    private static final ThreadLocal sThreadLocal = new ThreadLocal();
    // Looper内的消息隊列
    final MessageQueue mQueue;
    // 目前線程
    Thread mThread;
    // 。。。其他屬性

    // 每個Looper對象中有它的消息隊列,和它所屬的線程
    private Looper() {
        mQueue = new MessageQueue();
        mRun = true;
        mThread = Thread.currentThread();
    }

    // 我們調用該方法會在調用線程的TLS中建立Looper對象
    public static final void prepare() {
        if (sThreadLocal.get() != null) {
            // 試圖在有Looper的線程中再次建立Looper将抛出異常
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper());
    }
    // 其他方法
}
           

關于Looper的構造函數:

(1)建立并維護一個MessageQueue 的消息隊列;

(2)将Looper對象指向它的執行個體所線上程(即Looper在建立的時候會關聯一個線程);

關于Looper.prepare()函數:

**(1)**ThreadLocal對象用于儲存Looper對象的引用,其本身是一個

public static final void loop() {
        Looper me = myLooper();  //得到目前線程Looper
        MessageQueue queue = me.mQueue;  //得到目前looper的MQ

        // 這兩行沒看懂= = 不過不影響了解
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();
        // 開始循環
        while (true) {
            Message msg = queue.next(); // 取出message
            if (msg != null) {
                if (msg.target == null) {
                    // message沒有target為結束信号,退出循環
                    return;
                }
                // 日志。。。
                if (me.mLogging!= null) me.mLogging.println(
                        ">>>>> Dispatching to " + msg.target + " "
                        + msg.callback + ": " + msg.what
                        );
                // 非常重要!将真正的處理工作交給message的target,即後面要講的handler
                msg.target.dispatchMessage(msg);
                // 還是日志。。。
                if (me.mLogging!= null) me.mLogging.println(
                        "<<<<< Finished to    " + msg.target + " "
                        + msg.callback);

                // 下面沒看懂,同樣不影響了解
                final long newIdent = Binder.clearCallingIdentity();
                if (ident != newIdent) {
                    Log.wtf("Looper", "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);
                }
                // 回收message資源
                msg.recycle();
            }
        }
    }
           

當然,除了prepare()和loop()函數,Looper類還提供了一些有用的方法,比如Looper.myLooper()得到目前線程的Looper對象:

public static final Looper myLooper() {
        // 在任意線程調用Looper.myLooper()傳回的都是那個線程的looper
        return (Looper)sThreadLocal.get();
    }
           

這邊可以看到,就是通過ThreadLocal變量拿到Looper對象的。還有getThread()得到Looper對象所在的線程,quit()方法來結束Looper線程。

對于Looper有以下總結:

1).每個線程最多隻能有一個Looper對象,并且此對象會存在ThreadLocal中;

2).每個Looper對象中會維護一個MessageQueue的消息隊列,loop()方法調用後線程會不斷地從隊列中取出消息并交由handler去處理;

3).Looper類可以将一個普通線程轉化為Looper線程。

Handler

下面問題是如何往MessageQueue上發送消息呢?這就要用到Handler!

Handler是什麼呢?handler的主要作用是往MessageQueue上添加和處理消息,當然隻會處理由自己發送的消息。Handler在建立的時候會關聯一個looper,預設的構造方法将關聯目前線程的looper,這個在源碼中很清晰:

public class handler {

    final MessageQueue mQueue;  // 關聯的MQ
    final Looper mLooper;  // 關聯的looper
    final Callback mCallback; 
    // 其他屬性

    public Handler() {
        // 沒看懂,直接略過,,,
        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());
            }
        }
        // 預設将關聯目前線程的looper
        mLooper = Looper.myLooper();
        // looper不能為空,即該預設的構造方法隻能在looper線程中使用
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        // 重要!!!直接把關聯looper的MQ作為自己的MQ,是以它的消息将發送到關聯looper的MQ上
        mQueue = mLooper.mQueue;
        mCallback = null;
    }

    // 其他方法
}
           

下面如果要在LooperThread中加入Handler:

public class LooperThread extends Thread {
    private Handler handler1;
    private Handler handler2;

    @Override
    public void run() {
        // 将目前線程初始化為Looper線程
        Looper.prepare();

        // 執行個體化兩個handler
        handler1 = new Handler();
        handler2 = new Handler();

        // 開始循環處理消息隊列
        Looper.loop();
    }
}
           

加入Handler執行個體的效果圖如下所示:

圖3 加入handler對象的Looper線程

Android消息處理機制—— Looper, Handler, Message

是以可以看得出來:一個LooperThread隻能有一個Looper對象,但是可以用多個Handler。

Handler發送消息

有了handler就可以實用post(Runnable),postAtTime(Runnable,long),sendEmptyMessage(int),sendMessage(Message)等,看到這些API可以看出,發送兩種類型的消息:Message 和 Runnable,但是其實Runnable也是被封裝成Message的。

// 此方法用于向關聯的MQ上發送Runnable對象,它的run方法将在handler關聯的looper線程中執行

public final boolean post(Runnable r)
    {
       // 注意getPostMessage(r)将runnable封裝成message
       return  sendMessageDelayed(getPostMessage(r), );
    }
    private final Message getPostMessage(Runnable r) {
        Message m = Message.obtain();  //得到空的message
        m.callback = r;  //将runnable設為message的callback,
        return m;
    }
           

通過handler發送Message有如下特點:

1.message.target即為該handler對象,這樣可以確定looper執行到該Message時能夠找到處理它的handler,即在loop()函數中的關鍵代碼:

2.post發出的消息,其callback為runnable對象;

Handler如何處理消息

消息的處理是通過核心方法dispatchMessage(Message msg)和鈎子函數handleMessage(Message msg)完成的,源碼如下:

// 處理消息,該方法由looper調用
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            // 如果message設定了callback,即runnable消息,處理callback!
            handleCallback(msg);
        } else {
            // 如果handler本身設定了callback,則執行callback
            if (mCallback != null) {
                 /* 這種方法允許讓activity等來實作Handler.Callback接口,避免了自己編寫handler重寫handleMessage方法。見http://alex-yang-xiansoftware-com.iteye.com/blog/850865 */
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            // 如果message沒有callback,則調用handler的鈎子方法handleMessage
            handleMessage(msg);
        }
    }

    // 處理runnable消息
    private final void handleCallback(Message message) {
        message.callback.run();  //直接調用run方法!
    }
    // 由子類實作的鈎子方法
    public void handleMessage(Message msg) {
    }
           

看到這裡,開發者可以實作handleMessage函數和Runnable對象的run()函數,以此來完成整個Handler機制!

Handler用處的總結:

1. handler可以在任意線程發送消息,并且這些消息會被添加到關聯的MessageQueue上。

圖4 handler發送消息到MessageQueue

Android消息處理機制—— Looper, Handler, Message

2. handler是在它關聯的Looper線程中來處理消息的。

圖5 handler處理Message

Android消息處理機制—— Looper, Handler, Message

這樣就解決了Android最經典的不能在其他非主線程中更新UI的問題:Android的主線程必是一個Looper線程,我們在其中建立的handler對象将預設關聯主線程的Looper對象(該Looper對象是由系統自動建立的),也就自然關聯到該Looper對象鎖維護的MessageQueue。其中一個最常見的實用案例就是,建立handler并将其引用傳給某個thread,當該thread完成耗時操作後,将實用handler将消息回發到消息隊列,再由loop()函數将消息彈出,交由handler的handleMessage(Message msg)的使用者自實作的鈎子函數來更新UI。

具體實作如圖6所示:

Android消息處理機制—— Looper, Handler, Message

給一個執行個體代碼:

public class TestDriverActivity extends Activity {
    private TextView textview;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        textview = (TextView) findViewById(R.id.textview);
        // 建立并啟動工作線程
        Thread workerThread = new Thread(new SampleTask(new MyHandler()));
        workerThread.start();
    }

    public void appendText(String msg) {
        textview.setText(textview.getText() + "\n" + msg);
    }

    class MyHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            String result = msg.getData().getString("message");
            // 更新UI
            appendText(result);
        }
    }
}
           
public class SampleTask implements Runnable {
    private static final String TAG = SampleTask.class.getSimpleName();
    Handler handler;

    public SampleTask(Handler handler) {
        super();
        this.handler = handler;
    }

    @Override
    public void run() {
        try {  // 模拟執行某項任務,下載下傳等
            Thread.sleep();
            // 任務完成後通知activity更新UI
            Message msg = prepareMessage("task completed!");
            // message将被添加到主線程的MQ中
            handler.sendMessage(msg);
        } catch (InterruptedException e) {
            Log.d(TAG, "interrupted!");
        }

    }

    private Message prepareMessage(String str) {
        Message result = handler.obtainMessage();
        Bundle data = new Bundle();
        data.putString("message", str);
        result.setData(data);
        return result;
    }![這裡寫圖檔描述](http://img.blog.csdn.net/20150720232945900)
}