Android的消息機制

1. ANR異常

Application No Response:應用程序無響應。在主線程中,是不允許執行耗時的操作的,如果主線程阻塞的時間大於6秒,就很有可能出現anr異常。主線程,要完成界面的更新,事件的處理,窗體顯示的回調,所以如果主線程阻塞時間較長,就不能很好的處理以上比較重要的事情,那麼Android有一個機制,就是如果他發現消息隊列中有很多消息,主線程沒辦法響應的話,他就會拋出anr異常。所以,比較耗時的操作都必須要交給子線程。

解決辦法:可以通過Handler來解決這個問題,將比較耗時的操作交給子線程,然後子線程通過Handler,發送消息給主線程,讓主線程去更新界面。什麼樣的操作時比較耗時的?

1、訪問網絡,2、大文件的拷貝,3、阻塞式的請求,socket

2. Handler、Looper、Message、MessageQueue

Handler機制

Android 的Handler 機制(也有人叫消息機制)目的是爲了跨線程通信,也就是多線程通信。之所以需要跨線程通信是因爲在Android 中主線程通常只負責UI 的創建和修改,子線程負責網絡訪問和耗時操作,因此,主線程和子線程需要經常配合使用才能完成整個Android 功能。

在Android中,線程內部或者線程之間進行信息交互時經常會使用消息,這些基礎的東西如果我們熟悉其內部的原理,將會使我們容易、更好地架構系統,避免一些低級的錯誤。在學習Android中消息機制之前,我們先了解與消息有關的幾個類:

  • Handler:消息處理器,發送消息和處理消息。你可以構造Handler對象來與Looper溝通,以便push新消息到Message Queue裏,或者接收Looper(從Message Queue取出)所送來的消息。
  • Looper:輪詢器,從messagequeue取消息,分發給handler處理。一個線程可以產生一個Looper對象,由它來管理此線程裏的Message Queue(消息隊列)
  • Message 消息,數據的載體
  • MessageQueue 消息隊列,存儲消息

當我們的Android應用程序的進程一創建的時候,系統就給這個進程提供了一個Looper,Looper是一個死循環,它內部維護這個一個消息隊列。Looper不停地從消息隊列中取消息(Message),取到消息就發送給了Handler,最後Handler根據接收到的消息去修改UI。Handler的sendMessage方法就是將消息添加到消息隊列中。

3. UI線程

線程:UI thread 通常就是main thread,而Android啓動程序時會替它建立一個Message Queue。

每一個線程裏可含有一個Looper對象以及一個MessageQueue數據結構。在你的應用程序裏,可以定義Handler的子類別來接收Looper所送出的消息。在你的Android程序裏,新誕生一個線程,或執行 (Thread)時並不會自動建立其Message Looper。

Android裏並沒有Global的Message Queue數據結構,例如,不同APK裏的對象不能透過Massage Queue來交換訊息(Message)。

例如:線程A的Handler對象可以傳遞消息給別的線程,讓別的線程B或C等能送消息來給線程A(存於A的Message Queue裏)。線程A的Message Queue裏的消息,只有線程A所屬的對象可以處理。使用Looper.myLooper()可以取得當前線程的Looper對象。可以自定義Handler類,只要繼承Handler即可。使用new EventHandler(Looper.myLooper()); 可用來構造當前線程的Handler對象(其中EventHandler是自定義的Handler類)。

4. Activity.runOnUiThread()

Activity中提供了一個runOnUiThread方法,用於進行消息處理。此方法是通過線程合併join來實現消息處理的。
線程合併:主線程將子線程的任務拿到自己這裏來執行並終止子線程。實例代碼如下:

/**
 * Runs the specified action on the UI thread. If thecurrent thread is
 * the UI thread, then the action is executedimmediately. If the
 * current thread is not the UI thread, the action is posted to the
 * event queue of the UI thread.
 *
 * 上面的意思爲:在UI線程中運行我們的任務,如果當前線程是UI線程,則立即執行,如果
 * 不是則該任務發送到UI線程的事件隊列。 
 */
runOnUiThread(new Runnable() {
    @Override
    public void run() {
        //自定義我們的業務代碼
    }
});

5. View.post()、View.postDelayed()

6. Message消息

消息對象,顧名思義就是記錄消息信息的類。這個類有幾個比較重要的字段:

  • arg1和arg2:我們可以使用兩個字段用來存放我們需要傳遞的整型值,在Service中,我們可以用來存放Service的ID。
  • obj:該字段是Object類型,我們可以讓該字段傳遞某個多項到消息的接受者中。
  • what:這個字段可以說是消息的標誌,在消息處理中,我們可以根據這個字段的不同的值進行不同的處理,類似於我們在處理Button事件時,通過switch(v.getId())判斷是點擊了哪個按鈕。

在使用Message時,我們可以通過new Message()創建一個Message實例,但是Android更推薦我們通過Message.obtain()或者Handler.obtainMessage()獲取Message對象。這並不一定是直接創建一個新的實例,而是先從消息池中看有沒有可用的Message實例,存在則直接取出並返回這個實例。反之如果消息池中沒有可用的Message實例,則根據給定的參數new一個新Message對象。通過分析源碼可得知,Android系統默認情況下在消息池中實例化10個Message對象。

//創建或獲取消息的幾種方式
Message msg = new Message();// 創建一個新的消息對象
Message msg = handler.obtainMessage();// 獲取一個消息,如果消息池存在消息,則複用消息池中的消息,否則新創建一個消息對象
Message msg = Message.obtain();
Message.obtain(handler, what, obj).sendToTarget();

7. MessageQueue消息隊列

消息隊列,用來存放Message對象的數據結構,按照“先進先出”的原則存放消息。內部採用單鏈表的數據結構來存儲消息列表,存放並非實際意義的保存,而是將Message對象以鏈表的方式串聯起來的。MessageQueue對象不需要我們自己創建,而是有Looper對象對其進行管理,一個線程最多只可以擁有一個MessageQueue。我們可以通過Looper.myQueue()獲取當前線程中的MessageQueue。

MessageQueue的管理者,在一個線程中,如果存在Looper對象,則必定存在MessageQueue對象,並且只存在一個Looper對象和一個MessageQueue對象。

public class Looper {
    MessageQueue mQueue;//Looper身上維持着一個消息隊列
    ...
}

在Android系統中,除了主線程有默認的Looper對象,其它線程默認是沒有Looper對象。如果想讓我們新創建的線程擁有Looper對象時,我們首先應調用Looper.prepare()方法,然後再調用Looper.loop()方法。典型的用法如下:

class LooperThread extends Thread
{
    public Handler mHandler;
    public void run()
    {
        Looper.prepare();
        //其它需要處理的操作
        Looper.loop();
    }
}

倘若我們的線程中存在Looper對象,則我們可以通過Looper.myLooper()獲取,此外我們還可以通過Looper.getMainLooper()獲取當前應用系統中主線程的Looper對象。在這個地方有一點需要注意,假如Looper對象位於應用程序主線程中,則Looper.myLooper()和Looper.getMainLooper()獲取的是同一個對象。

8. Handler消息處理器

消息的處理者。通過Handler對象我們可以封裝Message對象,然後通過sendMessage(msg)把Message對象添加到MessageQueue中;當MessageQueue循環到該Message時,就會調用該Message對象對應的handler對象的handleMessage()方法對其進行處理。由於是在handleMessage()方法中處理消息,因此我們應該編寫一個類繼承自Handler,然後在handleMessage()處理我們需要的操作。

下面我們通過跟蹤代碼分析在Android中是如何處理消息。首先貼上測試代碼:

public class MessageService extends Service
{
    private static final String TAG = "MessageService";
    private static final int KUKA = 0;
    private Looper looper;
    private ServiceHandler handler;
    /**
     * 由於處理消息是在Handler的handleMessage()方法中,因此我們需要自己編寫類
     * 繼承自Handler類,然後在handleMessage()中編寫我們所需要的功能代碼
     * @author coolszy
     */
    private final class ServiceHandler extends Handler
    {
        public ServiceHandler(Looper looper)
        {
            super(looper);
        }
        @Override
        public void handleMessage(Message msg)
        {
            // 根據what字段判斷是哪個消息
            switch (msg.what)
            {
            case KUKA:
                //獲取msg的obj字段。我們可在此編寫我們所需要的功能代碼
                Log.i(TAG, "The obj field of msg:" + msg.obj);
                break;
            // other cases
            default:
                break;
            }
            // 如果我們Service已完成任務,則停止Service
            stopSelf(msg.arg1);
        }
    }
    @Override
    public void onCreate()
    {
        Log.i(TAG, "MessageService-->onCreate()");
        // 默認情況下Service是運行在主線程中,而服務一般又十分耗費時間,如果
        // 放在主線程中,將會影響程序與用戶的交互,因此把Service
        // 放在一個單獨的線程中執行
        HandlerThread thread = new HandlerThread("MessageDemoThread", Process.THREAD_PRIORITY_BACKGROUND);
        thread.start();
        // 獲取當前線程中的looper對象
        looper = thread.getLooper();
        //創建Handler對象,把looper傳遞過來使得handler、
        //looper和messageQueue三者建立聯繫
        handler = new ServiceHandler(looper);
    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId)
    {
        Log.i(TAG, "MessageService-->onStartCommand()");
        //從消息池中獲取一個Message實例
        Message msg = handler.obtainMessage();
        // arg1保存線程的ID,在handleMessage()方法中
        // 我們可以通過stopSelf(startId)方法,停止服務
        msg.arg1 = startId;
        // msg的標誌
        msg.what = KUKA;
        // 在這裏我創建一個date對象,賦值給obj字段
        // 在實際中我們可以通過obj傳遞我們需要處理的對象
        Date date = new Date();
        msg.obj = date;
        // 把msg添加到MessageQueue中
        handler.sendMessage(msg);
        return START_STICKY;
    }
    @Override
    public void onDestroy()
    {
        Log.i(TAG, "MessageService-->onDestroy()");
    }
    @Override
    public IBinder onBind(Intent intent)
    {
        return null;
    }
}

運行結果:

消息機制

注:在測試代碼中我們使用了HandlerThread類,該類是Thread的子類,該類運行時將會創建looper對象,使用該類省去了我們自己編寫Thread子類並且創建Looper的麻煩。下面我們分析下程序的運行過程:

8.1 onCreate()

首先啓動服務時將會調用onCreate()方法,在該方法中我們new了一個HandlerThread對象,提供了線程的名字和優先級。緊接着我們調用了start()方法,執行該方法將會調用HandlerThread對象的run()方法:

public void run() {
    mTid = Process.myTid();
    Looper.prepare();
    synchronized (this) {
        mLooper = Looper.myLooper();
        notifyAll();
    }
    Process.setThreadPriority(mPriority);
    onLooperPrepared();
    Looper.loop();
    mTid = -1;
}

在run()方法中,系統給線程添加的Looper,同時調用了Looper的loop()方法:

public static final void loop() {
    Looper me = myLooper();
    MessageQueue queue = me.mQueue;
    while (true) {
        Message msg = queue.next(); // might block
        //if (!me.mRun) {
        //    break;
        //}
        if (msg != null) {
            if (msg.target == null) {
                // No target is a magic identifier for the quit message.
                return;
            }
            if (me.mLogging!= null) me.mLogging.println(
                    ">>>>> Dispatching to " + msg.target + " "
                    + msg.callback + ": " + msg.what
                    );
            msg.target.dispatchMessage(msg);
            if (me.mLogging!= null) me.mLogging.println(
                    "<<<<< Finished to    " + msg.target + " "
                    + msg.callback);
            msg.recycle();
        }
    }
}

通過源碼我們可以看到loop()方法是個死循環,將會不停的從MessageQueue對象中獲取Message對象,如果MessageQueue 對象中不存在Message對象,則結束本次循環,然後繼續循環;如果存在Message對象,則執行 msg.target.dispatchMessage(msg),但是這個msg的.target字段的值是什麼呢?我們先暫時停止跟蹤源碼,返回到onCreate()方法中。線程執行完start()方法後,我們可以獲取線程的Looper對象,然後new一個ServiceHandler對象,我們把Looper對象傳到ServiceHandler構造函數中將使handler、looper和messageQueue三者建立聯繫。

8.2 onStartCommand()

執行完onStart()方法後,將執行onStartCommand()方法。首先我們從消息池中獲取一個Message實例,然後給Message對象的arg1、what、obj三個字段賦值。緊接着調用sendMessage(msg)方法,我們跟蹤源代碼,該方法將會調用sendMessageDelayed(msg, 0)方法,而sendMessageDelayed()方法又會調用sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis)方法,在該方法中我們要注意該句代碼msg.target = this,msg的target指向了this,而this就是ServiceHandler對象,因此msg的target字段指向了ServiceHandler對象,同時該方法又調用MessageQueue 的enqueueMessage(msg, uptimeMillis)方法:

final boolean enqueueMessage(Message msg, long when) {
    if (msg.when != 0) {
        throw new AndroidRuntimeException(msg
                + " This message is already in use.");
    }
    if (msg.target == null && !mQuitAllowed) {
        throw new RuntimeException("Main thread not allowed to quit");
    }
    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;
        } else if (msg.target == null) {
            mQuiting = true;
        }
        msg.when = when;
        //Log.d("MessageQueue", "Enqueing: " + msg);
        Message p = mMessages;
        if (p == null || when == 0 || when < p.when) {
            msg.next = p;
            mMessages = msg;
            this.notify();
        } else {
            Message prev = null;
            while (p != null && p.when <= when) {
                prev = p;
                p = p.next;
            }
            msg.next = prev.next;
            prev.next = msg;
            this.notify();
        }
    }
    return true;
}

該方法主要的任務就是把Message對象的添加到MessageQueue中(數據結構最基礎的東西,自己畫圖理解下)。

handler.sendMessage()–>handler.sendMessageDelayed()–>handler.sendMessageAtTime()–>msg.target = this;queue.enqueueMessage==>把msg添加到消息隊列中

handler

8.3 handleMessage(msg)

onStartCommand()執行完畢後我們的Service中的方法就執行完畢了,那麼handleMessage()是怎麼調用的呢?在前面分析的loop()方法中,我們當時不知道msg的target字段代碼什麼,通過上面分析現在我們知道它代表ServiceHandler對象,msg.target.dispatchMessage(msg);則表示執行ServiceHandler對象中的dispatchMessage()方法:

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

該方法首先判斷callback是否爲空,我們跟蹤的過程中未見給其賦值,因此callback字段爲空,所以最終將會執行handleMessage()方法,也就是我們ServiceHandler類中複寫的方法。在該方法將根據what字段的值判斷執行哪段代碼。

至此,我們看到,一個Message經由Handler的發送,MessageQueue的入隊,Looper的抽取,又再一次地回到Handler的懷抱中。而繞的這一圈,也正好幫助我們將同步操作變成了異步操作。

9. Handler的源碼分析

先看構造方法

public class Handler {
    private Looper       mLooper;
    private MessageQueue mQueue;

    public Handler() {
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
    }
}

在Handler的構造方法中,調用Looper.myLooper()方法獲取一個Looper對象,如果Looper對象爲空,則會拋異常,沒有Looper對象不能創建Handler對象。但是我們在主線程new Handler的時候,並沒有調用Looper.prepare()和Looper.loop()方法初始化Looper,也不會出現異常,這是因爲Android系統在主線程創建的時候幫我們把Looper初始化了

9.1 主線程設置Looper,在ActivityThread類裏面

public static final void main(String[] args) {
    ....
    // 1.主線程創建Looper 
    Looper.prepareMainLooper();
    if (sMainThreadHandler == null) {
        sMainThreadHandler = new Handler();
    }
    ActivityThread thread = new ActivityThread();
    thread.attach(false);
    if (false) {
        Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread"));
    }
    Looper.loop();
}

MainThread 是Android 系統創建並維護的,創建的時候系統執行了Looper.prepare();方法,該方法內部創建了MessageQueue 消息隊列(也叫消息池),該消息隊列是Message 消息的容器,用於存儲通過handler發送過來的Message。MessageQueue 是Looper 對象的成員變量,Looper 對象通過ThreadLocal 綁定在MainThread 中。因此我們可以簡單的這麼認爲:MainThread 擁有唯一的一個Looper 對象,該Looper 對象有用唯一的MessageQueue 對象,MessageQueue 對象可以存儲多個Message。

MainThread 中需要程序員手動創建Handler 對象,並覆寫Handler 中的handleMessage(Message msg)方法,該方法將來會在主線程中被調用,在該方法裏一般會寫與UI 修改相關的代碼。

MainThread 創建好之後,系統自動執行了Looper.loop();方法,該方法內部開啓了一個“死循環”不斷的去之前創建好的MessageQueue 中取Message。如果一有消息進入MessageQueue,那麼馬上會被Looper.loop();取出來,取出來之後就會調用之前創建好的handler 對象的handleMessage(Message)方法。

newThread 線程是我們程序員自定new 出來的子線程。在該子線程中處理完我們的“耗時”或者網絡訪問任務後,調用主線程中的handler 對象的sendMessage(msg)方法,該方法一被執行,內部將就msg添加到了主線程中的MessageQueue 隊列中,這樣就成爲了Looper.loop()的盤中餐了,等待着被消費。

上面的過程有點類似生產者和消費者的過程。newThread 屬於生產者,負責生產Message,MainThread 屬於消費者。這是一個很複雜的過程,但是Android 顯然已經將這種模式給封裝起來了,就叫Handler 機制。我們使用時只需要在主線程中創建Handler,並覆寫handler 中的handleMessage 方法,然後在子線程中調用handler 的sendMessage(msg)方法即可。

獲取Looper對象後,接着獲取Looper身上的MessageQueue對象,Handler就是把消息發送到該消息隊列

Handler對象創建後,就可以通過handler.sendXxx()發送消息,可以發送一個普通的消息,也可以發送一個空消息,可以發送一個延時消息,也可以發送一個定時消息

public class Handler {
    ...
    // 發送一個普通消息
    public boolean sendMessage(Message msg) {
        return sendMessageDelayed(msg, 0)
    }
    // 發送一個空消息
    public boolean sendEmptyMessage(int what) {
        return sendEmptyMessageDelayed(what, 0);
    }
    // 發送一個空的延時消息
    public boolean sendEmptyMessageDelayed(int what, long delayMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageDelayed(msg, 0);
    }
    // 發送一個延時消息
    public boolean sendMessageDelayed(Message msg, long delayTime) {
        if (delayTime < 0)
            delayTime = 0;
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayTime);
    }
    // 發送一個定時消息
    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        return enqueueMessage(queue, msg, uptimeMillis);
    }
    //發送消息幾個方法sendXxx(),最終都是調用enqueueMessage()方法,消息入隊
    public boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        // 把Message的target置爲當前發送的Handler,以便Looper取到message後根據target把message分發給正確的Handler
        msg.target = this;
        // 往隊列裏面添加消息Message
        return queue.enqueueMessage(msg, uptimeMillis);
    }
    ...
}

發送消息幾個方法sendXxx(),最終都是調用enqueueMessage()方法,在該方法內部調用的是消息隊列MessageQueue的enqueueMessage()方法,把消息發送到消息隊列

把this,也就是當前handler對象賦值給Message 的target屬性,當多個Handler發送消息到消息隊列的時候,可以通過該屬性判斷消息是哪個Handler發送的

9.2 MessageQueue.enqueueMessage

boolean enqueueMessage(Message msg, long when) {
        ...
        synchronized (this) {
            ...
            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
                // 當前發送的message需要馬上被處理調,needWake喚醒狀態置true
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                // 當前發送的message被排隊到其他message的後面,needWake喚醒狀態置false
                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;
    }

9.3 Handler的post()、postAtTime()、postDelayed()

public final boolean post(Runnable r)
{
   return  sendMessageDelayed(getPostMessage(r), 0);
}

public final boolean postAtTime(Runnable r, long uptimeMillis)
{
    return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}

public final boolean postDelayed(Runnable r, long delayMillis)
{
    return sendMessageDelayed(getPostMessage(r), delayMillis);
}

// 把Runnable包裝成一個消息Message
private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();// 獲取消息對象
    m.callback = r;// 把消息賦值給Message的callback屬性
    return m;
}

可以調用Handler的post()、postAtTime()、postDelayed()分別發送一個普通的、定時、延時的Runnable任務,Runnable會賦值給Message的callback屬性,最終封裝成一個消息發送出去

9.4 刪除Callback和Message

  • Handler.removeCallbacks() 從消息隊列中刪除所有回調
  • Handler.removeMessages() 從消息隊列刪除所有消息
  • Handler.removeCallbacksAndMessages() 從消息隊列中刪除所有Message和Callback

一般在Activity銷燬的時候調用

public void onDestroy(){
    handler.removeCallbacks();
    handler.removeMessages();
    handler.removeCallbacksAndMessages();
}

10. Looper

輪詢器,從messagequeue取消息,分發給handler處理。創建Handler對象,必須有Looper對象,而Looper對象的初始化需要調用Looper.prepare()和Looper.loop()方法

10.1 Looper.prepare()

ThreadLocal<Looper> sThreadLocal;

public static final void prepare() {
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    // 3、在主線程中設置Looper, new Looper()裏面創建了一個MessageQueue
    sThreadLocal.set(new Looper());
}

public static final void prepareMainLooper() {
    // 2、調用prepare
    prepare();
    setMainLooper(myLooper());
    if (Process.supportsProcesses()) {
        myLooper().mQueue.mQuitAllowed = false;
    }
}

先從ThreadLocal中獲取一個Looper對象,如果該Looper對象不爲空,則拋異常,這是因爲一個線程僅能夠綁定一個Looper對象。ThreadLocal是一個用於線程範圍內共享數據的底層是一個map結構的類,key是當前線程,value是Looper。如果當前線程沒有綁定Looper對象,則new Looper()創建一個Looper對象,並把該Looper對象設置sThreadLocal

10.2 Looper.loop()

public static void loop() {
    Looper me = myLooper(); // 獲取當前線程的Looper對象,爲空則拋異常
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    MessageQueue queue = me.mQueue; // 獲取Looper對象維持的消息隊列
    for (; ; ) { // 開啓死循環從消息隊列獲取消息
        // 調用MessageQueue的next()取消息,如果沒有消息,就阻塞
        Message msg = queue.next();
        // msg.target即Handler,獲取消息後調用Handler的dispatchMessage()處理消息
        msg.target.dispatchMessage(msg);
    }
}

主線程調用Looper.loop()方法,主線程就會阻塞,是一個死循環,使用管道(Pipe),是Linux中的一種進程間通信方式,使用了特殊的文件,有兩個文件描述符(一個是讀取,一個是寫入)

應用場景;主進程拿着讀取描述符等待讀取,沒有內容時就阻塞,另一個進程拿寫入描述符去寫內容,喚醒主進程,主進程拿着讀取描述符讀取到內容,繼續執行。

Handler應用場景:Handler在主線程中創建,Looper會在死循環裏等待取消息,1、沒取到,就阻塞,2、一旦被子線程喚醒,取到消息,就把Message交給Handler處理。子線程用Handler去發送消息,拿寫入描述符去寫消息,喚醒主線程。

11. Handler.dispatchMessage

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        // 把Message交給Handler處理
        handleMessage(msg);
    }
}

調用Looper.loop()會開啓一個死循環,從消息隊列MessageQueue取消息,取到消息後調用msg.target.dispatchMessage(msg);即調用Handler的dispatchMessage()方法,在該方法內部調用的是handleMessage(),對,就是我們new Handler的時候實現的handleMessage()方法

所以Android的消息機制大概流程是:Handler把消息Message發送到消息隊列MessageQueue,Looper從消息隊列取消息,取到消息後回調Handler的handleMessage()方法

handler機制

11.1 消息處理的優先級

在dispatchMessage()方法中,如果msg.callback(一個Runnable)不爲空,則先處理Message的Runnable;然後判斷mCallback(通過Handler的構造方法傳進來的Callback)是否爲空,不爲空,則執行Callback的handleMessage()方法,最後纔是執行Handler的handleMessage()

所有消息處理的優先級是Message的callback –> Handler的mCallback –> Handler的handleMessage()

12. Handler機制的應用

12.1 在主線程中給子線程發送消息

public class MainActivity extends Activity {

    private Handler subHandler;//是在子線程中創建的Handler對象
    private Looper  myLooper;//子線程中的Looper對象

    private Handler handler = new Handler(){
        @Override
        public void handleMessage(android.os.Message msg) {
            switch (msg.what) {
            case 1:
                Toast.makeText(MainActivity.this, msg.obj.toString(), Toast.LENGTH_SHORT).show();
                break;
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        /*
         * 匿名內部類對象對外部類有一個隱式的強引用
         */
        new Thread(new Runnable() {
            @Override
            public void run() {
                // 1. 創建了Looper對象,然後Looper對象中創建了MessageQueue
                // 2. 並將當前的Looper對象跟當前的線程(子線程)綁定ThreadLocal
                Looper.prepare();

                // 1. 創建了handler對象,然後從當前線程中獲取Looper對象,然後獲取到MessageQueue對象
                subHandler = new Handler(){
                    @Override
                    public void handleMessage(Message msg) {
                        Toast.makeText(MainActivity.this, msg.obj.toString(), Toast.LENGTH_SHORT).show();
                    }
                };

                myLooper = Looper.myLooper();//獲取當前線程中的Looper對象

                /*
                 * 1. 從當前線程中找到之前創建的Looper對象,然後找到 MessageQueue
                 * 2. 開啓死循環,遍歷消息池中的消息
                 * 3. 當獲取到msg的時候,調用這個msg的handler的disPatchMsg方法,讓msg執行起來
                 */
                Looper.loop();
                Log.d("tag", "loop()方法執行完了");
            }
        }).start();

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (myLooper!=null) {
            myLooper.quit();
            myLooper = null;
        }
    }

    public void sendMsg(View view){
        new Thread(new Runnable() {
            @Override
            public void run() {
                SystemClock.sleep(2000);//模擬一個耗時操作
                Message msg = new Message();
                msg.what = 1;//區分發送的消息
                msg.obj = "來自子線程的問候";
                handler.sendMessage(msg);
            }
        }).start();
    }

    public void sendMsg2(View view) {
        //從消息池中獲取一箇舊的msg,如果沒有重新創建消息
        subHandler.obtainMessage(2, "我是主線程發送來的祝福").sendToTarget();
    }
}

12.2 同線程內不同組件間的消息傳遞

Looper類用來管理特定線程內對象之間的消息交換(MessageExchange)。你的應用程序可以產生許多個線程。而一個線程可以有許多個組件,這些組件之間常常需要互相交換訊息。如果有這種需要,您可以替線程構造一個Looper對象,來擔任訊息交換的管理工作。Looper對象會建立一個MessageQueue數據結構來存放各對象傳來的消息(包括UI事件或System事件等)。每一個線程裏可含有一個Looper對象以及一個MessageQueue數據結構。在你的應用程序裏,可以定義Handler的子類別來接收Looper所送出的消息。

同線程不同組件之間的消息傳遞代碼如下:

/**
 * ============================================================
 * Copyright:${TODO}有限公司版權所有 (c) 2017
 * Author:   AllenIverson
 * Email:    [email protected]
 * GitHub:   https://github.com/JackChen1999
 * 博客:     http://blog.csdn.net/axi295309066
 * 微博:     AndroidDeveloper
 * GitBook: https://www.gitbook.com/@alleniverson
 * <p>
 * Project_Name:HandlerDemo
 * Package_Name:com.github.handlerdemo.activity
 * Version:1.0
 * time:2017/3/1 16:23
 * des :
 * gitVersion:2.12.0.windows.1
 * updateAuthor:$Author$
 * updateDate:$Date$
 * updateDes:${TODO}
 * ============================================================
 */
public classHandlerActivity extends Activity
{
    private Button sendBtn;
    private TextView tv;
    @Override
    protected void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        sendBtn=(Button)findViewById(R.id.send);
        tv=(TextView)findViewById(R.id.textview);
        sendBtn.setOnClickListener(newMyOnClickListener());
    }
    class MyOnClickListener implements OnClickListener {
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
                case R.id.send:
                    // 取得當前線程的Looper,此時的線程爲主線程(UI線程)
                    Looper looper = Looper.myLooper();
                    // 構造一個Handler對象使之與Looper通信
                    MyHandler mHandler = newMyHandler(looper);
                    // 產生一個消息通過Handler傳遞給Looper
                    String msgStr = "main";
                    // 構造一個消息,這裏what參數設爲1,obj參數設爲msgStr變量。
                    Message msg = mHandler.obtainMessage(1, 1, 1, msgStr);
                    // 發送消息,調用Handler對象的handleMessage方法
                    mHandler.sendMessage(msg);
                    break;
            }
        }
    }
    // 自定義Handler類
    class MyHandler extends Handler {
        // 指定Looper對象來構造Handler對象,而我們平時直接使用的Handler無參構造方法實際上默認是本線程的looper,可通過查看SDk源代碼瞭解。
        public MyHandler(Looper looper) {
            super(looper);
        }
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 1:
                    tv.setText(String.valueOf(msg.obj));
                    break;
            }
        }
    }
}

說明:此程序啓動時,當前線程(即主線程, mainthread)已誕生了一個Looper對象,並且有了一個MessageQueue數據結構。

  • 調用Looper類別的靜態myLooper()函數,以取得目前線程裏的Looper對象。looper = Looper.myLooper ();

  • 構造一個MyHandler對象來與Looper溝通。Activity等對象可以藉由MyHandler對象來將消息傳給Looper,然後放入MessageQueue裏;MyHandler對象也扮演Listener的角色,可接收Looper對象所送來的消息。mHandler = new MyHandler (looper);

  • 先構造一個Message對象,並將數據存入對象裏。
    Message msg = mHandler.obtainMessage(1, 1, 1, msgStr);
    這裏也可以這樣寫:

Message msg = new Message();
msg.what = 1;
msg.obj = msgStr;
  • 通過mHandler對象將消息m傳給Looper,然後放入MessageQueue裏。mHandler.sendMessage(msg);

此時,Looper對象看到MessageQueue裏有消息m,就將它廣播出去,mHandler對象接到此訊息時,會調用其handleMessage()函數來處理,於是讓msgStr顯示於TextView上(更新UI)。

12.3 子線程傳遞消息給主線程

/**
 * ============================================================
 * Copyright:${TODO}有限公司版權所有 (c) 2017
 * Author:   AllenIverson
 * Email:    [email protected]
 * GitHub:   https://github.com/JackChen1999
 * 博客:     http://blog.csdn.net/axi295309066
 * 微博:     AndroidDeveloper
 * GitBook: https://www.gitbook.com/@alleniverson
 * <p>
 * Project_Name:HandlerDemo
 * Package_Name:com.github.handlerdemo.activity
 * Version:1.0
 * time:2017/3/1 16:23
 * des :
 * gitVersion:2.12.0.windows.1
 * updateAuthor:$Author$
 * updateDate:$Date$
 * updateDes:${TODO}
 * ============================================================
 */
public classHandlerActivity extends Activity
{
    private Button sendBtn;
    private TextView tv;
    private MyHandler mHandler = null;
    Thread thread;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        sendBtn = (Button)findViewById(R.id.send);
        tv = (TextView)findViewById(R.id.textview);
        sendBtn.setOnClickListener(newMyOnClickListener());
    }
    class MyOnClickListener implements OnClickListener {
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
                case R.id.send:
                    thread = new MyThread();
                    thread.start();
                    break;
            }
        }
    }
    // 自定義Handler類
    class MyHandler extends Handler {
        // 指定Looper對象來構造Handler對象,而我們平時直接使用的Handler無參構造方法實際上默認是本線程的looper,可通過查看SDk源代碼瞭解。  
        public MyHandler(Looper looper) {
            super(looper);
        }
        @Override
        public void handleMessage(Message msg){
            switch (msg.what) {
                case 1:
                    tv.setText(String.valueOf(msg.obj));
                    break;
            }
        }
    }
    private class MyThread extends Thread {
        @Override
        public void run() {
            // 獲得當前線程的Looper對象  
            Looper curLooper =Looper.myLooper();
            // 獲得主線程(UI線程)的Looper對象  
            Looper mainLooper =Looper.getMainLooper();
            String msgStr;
            if (curLooper == null) {
                mHandler = newMyHandler(mainLooper);
                msgStr = "curLooper isnull";
            } else {
                mHandler = newMyHandler(curLooper);
                msgStr = "This iscurLooper";
            }
            Message msg =mHandler.obtainMessage(1, 1, 1, msgStr);
            mHandler.sendMessage(msg);
        }
    }
}

Android會自動替主線程建立MessageQueue。在這個子線程裏並沒有建立Message Queue。所以curLooper值爲null,而mainLooper則指向主線程裏的Looper。於是執行mHandler= new MyHandler (mainLooper);此mHandler屬於主線程

mHandler.sendMessage(msg);就將msg消息存入到主線程的MessageQueue裏

mainLooper看到Message Queue裏有訊息,就會作出處理,於是由主線程執行到mHandler的handleMessage()來處理消息。

12.4 在子線程中直接更新View不拋異常的問題

下面代碼,我們在子線程中直接操作View,但是並不會拋出異常

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    tv = (TextView) findViewById(R.id.tv);
    iv = (ImageView) findViewById(R.id.iv);
    //搞不懂爲什麼在子線程裏可以修改ui
    new Thread(){
        public void run() {
        tv.setText("wwwwwwwww");
        iv.setImageResource(R.drawable.ic_launcher);
        }
    }.start();
}

原因:

在更新ui界面時View會把要界面傳給ViewRoot

自定義Handler它是Activity和WindowMangerImpl之間的橋樑,要修改界面上的數據的經過它,它把數據傳遞給WindowManagerImple或DecorView之前,會調用checkThread方法,判斷當前線程是否是主線程,不是的話跑異常。

而上面的代碼是在onCreate裏面修改的view,那時候view還沒有被真正的被放在ContentView裏面,ViewRoot還沒有被創建,mParent爲null。mparent(放ContentView的容器類型ViewParent)爲null時不調用checkThread方法,當執行到ActivityThread的handlerResumeActivity方法時ViewRoot方法纔會被創建,該方法在Activity的onResume方法執行完以後的200到300毫秒以後得到執行,並且把界面傳遞給WidowManagerImpl進行顯示,由於在viewRoot被創建前已經修改了控件的值,所以顯示的是已經修改的值。

所以在onResume方法被執行完的200毫秒之前那是還沒有ViewRoot和mparent是可以修改主線程UI界面的 只有當viewRoot被創建了以後

在子線程view被添加到了contentView上時,或已添加到ContentView上在修改顯示內容時會調用View的requestLayout方法,requestLayout會調用ViewRoot的checkThread方法,纔會拋異常。

如果修改一個view的內容不調用requestLayout方法時是可以子線程中修改的並且不拋異常。如給一個view設置監聽事件,雖然設置監聽是在子線程裏設置的但是調用執行監聽事件的代碼是在主線程裏調用的從而執行也將在主線程執行。又如post方法,它只是給主線程所在的handler發送消息,handler分發消息時執行具體任務,並沒有執行checkThread。又如創建一個VIew,爲VIEW設置內容,添加view到該view等等,因爲還沒被添加到ContentView上所以不會執行checkThread。

13. Handler的核心代碼

public class Handler {
    private Looper mLooper;
    private MessageQueue mQueue;
    public Handler(){
        mLooper = Looper.myLooper();
        if (mLooper == null){
            throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
    }
    public void handleMessage(){

    }

    public void dispatchMessage(Message msg){
        handleMessage();
    }

    public boolean sendMessage(Message msg){
        return sendMessageDelayed(msg,0)
    }

    public boolean sendEmptyMessage(int what){
        return sendEmptyMessageDelayed(what,0);   
    }

    public boolean sendEmptyMessageDelayed(int what, long delayMillis){
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageDelayed(msg,0);
    }

    public boolean sendMessageDelayed(Message msg, long delayTime){
        if (delayTime < 0) delayTime = 0;
        return sendMessageAtTime(msg,SystemClock.uptimeMillis()+delayTime);
    }

    public boolean sendMessageAtTime(Message msg, long uptimeMillis){
        MessageQueue queue = mQueue;
        return enqueueMessage(queue,msg,uptimeMillis);
    }

    public boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis){
        msg.target = this;
        return queue.enqueueMessage(msg, uptimeMillis);
    }

    public void post(Runnable r){
        sendMessageDelayed(getPostMessage(r),0);
    }

    public static Message getPostMessage(Runnable r){
        Message m = Message.obtain();
        m.callback = r;
        return m;
    } 
}

14. Looper的核心代碼

public class Looper {
    static final ThreadLocal<Looper> mThreadLocal = new InheritableThreadLocal<>();
    MessageQueue mQueue;
    Thread mCurrentThread;
    private static Looper sMainLooper;

    private Looper() {
        mQueue = new MessageQueue();
        mCurrentThread = Thread.currentThread();
    }

    public static void prepare() {
        if (mThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        mThreadLocal.set(new Looper());
    }

    public void prepareMainLooper(){
        prepare();
        sMainLooper = myLooper();
    }

    public static void loop() {
        Looper me = myLooper();
        if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");}
        MessageQueue queue = me.mQueue;
        for(;;){
            Message msg = queue.next();//block
            msg.target.dispatchMessage(msg);
        }
    }

    public static Looper myLooper() {
        return mThreadLocal.get();
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章