Android的消息機制

1. ANR異常

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

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

1、訪問網(wǎng)絡(luò),2、大文件的拷貝,3、阻塞式的請求,socket

2. Handler、Looper、Message、MessageQueue

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

在Android中,線程內(nèi)部或者線程之間進(jìn)行信息交互時經(jīng)常會使用消息,這些基礎(chǔ)的東西如果我們熟悉其內(nèi)部的原理,將會使我們?nèi)菀?、更好地架?gòu)系統(tǒng),避免一些低級的錯誤。在學(xué)習(xí)Android中消息機制之前,我們先了解與消息有關(guān)的幾個類:

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

當(dāng)我們的Android應(yīng)用程序的進(jìn)程一創(chuàng)建的時候,系統(tǒng)就給這個進(jìn)程提供了一個Looper,Looper是一個死循環(huán),它內(nèi)部維護(hù)這個一個消息隊列。Looper不停地從消息隊列中取消息(Message),取到消息就發(fā)送給了Handler,最后Handler根據(jù)接收到的消息去修改UI。Handler的sendMessage方法就是將消息添加到消息隊列中。

3. UI線程

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

每一個線程里可含有一個Looper對象以及一個MessageQueue數(shù)據(jù)結(jié)構(gòu)。在你的應(yīng)用程序里,可以定義Handler的子類別來接收Looper所送出的消息。在你的Android程序里,新誕生一個線程,或執(zhí)行 (Thread)時并不會自動建立其Message Looper。

Android里并沒有Global的Message Queue數(shù)據(jù)結(jié)構(gòu),例如,不同APK里的對象不能透過Massage Queue來交換訊息(Message)。

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

4. Activity.runOnUiThread()

Activity中提供了一個runOnUiThread方法,用于進(jìn)行消息處理。此方法是通過線程合并join來實現(xiàn)消息處理的。
線程合并:主線程將子線程的任務(wù)拿到自己這里來執(zhí)行并終止子線程。實例代碼如下:

/**
 * 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線程中運行我們的任務(wù),如果當(dāng)前線程是UI線程,則立即執(zhí)行,如果
 * 不是則該任務(wù)發(fā)送到UI線程的事件隊列。 
 */
runOnUiThread(new Runnable() {
    @Override
    public void run() {
        //自定義我們的業(yè)務(wù)代碼
    }
});

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

6. Message消息

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

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

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

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

7. MessageQueue消息隊列

消息隊列,用來存放Message對象的數(shù)據(jù)結(jié)構(gòu),按照“先進(jìn)先出”的原則存放消息。存放并非實際意義的保存,而是將Message對象以鏈表的方式串聯(lián)起來的。MessageQueue對象不需要我們自己創(chuàng)建,而是有Looper對象對其進(jìn)行管理,一個線程最多只可以擁有一個MessageQueue。我們可以通過Looper.myQueue()獲取當(dāng)前線程中的MessageQueue。

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

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

在Android系統(tǒng)中,除了主線程有默認(rèn)的Looper對象,其它線程默認(rèn)是沒有Looper對象。如果想讓我們新創(chuàng)建的線程擁有Looper對象時,我們首先應(yīng)調(diào)用Looper.prepare()方法,然后再調(diào)用Looper.loop()方法。典型的用法如下:

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

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

8. Handler消息處理器

消息的處理者。通過Handler對象我們可以封裝Message對象,然后通過sendMessage(msg)把Message對象添加到MessageQueue中;當(dāng)MessageQueue循環(huán)到該Message時,就會調(diào)用該Message對象對應(yīng)的handler對象的handleMessage()方法對其進(jìn)行處理。由于是在handleMessage()方法中處理消息,因此我們應(yīng)該編寫一個類繼承自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)
        {
            // 根據(jù)what字段判斷是哪個消息
            switch (msg.what)
            {
            case KUKA:
                //獲取msg的obj字段。我們可在此編寫我們所需要的功能代碼
                Log.i(TAG, "The obj field of msg:" + msg.obj);
                break;
            // other cases
            default:
                break;
            }
            // 如果我們Service已完成任務(wù),則停止Service
            stopSelf(msg.arg1);
        }
    }
    @Override
    public void onCreate()
    {
        Log.i(TAG, "MessageService-->onCreate()");
        // 默認(rèn)情況下Service是運行在主線程中,而服務(wù)一般又十分耗費時間,如果
        // 放在主線程中,將會影響程序與用戶的交互,因此把Service
        // 放在一個單獨的線程中執(zhí)行
        HandlerThread thread = new HandlerThread("MessageDemoThread", Process.THREAD_PRIORITY_BACKGROUND);
        thread.start();
        // 獲取當(dāng)前線程中的looper對象
        looper = thread.getLooper();
        //創(chuàng)建Handler對象,把looper傳遞過來使得handler、
        //looper和messageQueue三者建立聯(lián)系
        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)方法,停止服務(wù)
        msg.arg1 = startId;
        // msg的標(biāo)志
        msg.what = KUKA;
        // 在這里我創(chuàng)建一個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;
    }
}

運行結(jié)果:

消息機制

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

8.1 onCreate()

首先啟動服務(wù)時將會調(diào)用onCreate()方法,在該方法中我們new了一個HandlerThread對象,提供了線程的名字和優(yōu)先級。緊接著我們調(diào)用了start()方法,執(zhí)行該方法將會調(diào)用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()方法中,系統(tǒng)給線程添加的Looper,同時調(diào)用了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()方法是個死循環(huán),將會不停的從MessageQueue對象中獲取Message對象,如果MessageQueue 對象中不存在Message對象,則結(jié)束本次循環(huán),然后繼續(xù)循環(huán);如果存在Message對象,則執(zhí)行 msg.target.dispatchMessage(msg),但是這個msg的.target字段的值是什么呢?我們先暫時停止跟蹤源碼,返回到onCreate()方法中。線程執(zhí)行完start()方法后,我們可以獲取線程的Looper對象,然后new一個ServiceHandler對象,我們把Looper對象傳到ServiceHandler構(gòu)造函數(shù)中將使handler、looper和messageQueue三者建立聯(lián)系。

8.2 onStartCommand()

執(zhí)行完onStart()方法后,將執(zhí)行onStartCommand()方法。首先我們從消息池中獲取一個Message實例,然后給Message對象的arg1、what、obj三個字段賦值。緊接著調(diào)用sendMessage(msg)方法,我們跟蹤源代碼,該方法將會調(diào)用sendMessageDelayed(msg, 0)方法,而sendMessageDelayed()方法又會調(diào)用sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis)方法,在該方法中我們要注意該句代碼msg.target = this,msg的target指向了this,而this就是ServiceHandler對象,因此msg的target字段指向了ServiceHandler對象,同時該方法又調(diào)用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;
}

該方法主要的任務(wù)就是把Message對象的添加到MessageQueue中(數(shù)據(jù)結(jié)構(gòu)最基礎(chǔ)的東西,自己畫圖理解下)。

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

handler

8.3 handleMessage(msg)

onStartCommand()執(zhí)行完畢后我們的Service中的方法就執(zhí)行完畢了,那么handleMessage()是怎么調(diào)用的呢?在前面分析的loop()方法中,我們當(dāng)時不知道m(xù)sg的target字段代碼什么,通過上面分析現(xiàn)在我們知道它代表ServiceHandler對象,msg.target.dispatchMessage(msg);則表示執(zhí)行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字段為空,所以最終將會執(zhí)行handleMessage()方法,也就是我們ServiceHandler類中復(fù)寫的方法。在該方法將根據(jù)what字段的值判斷執(zhí)行哪段代碼。

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

9. Handler的源碼分析

先看構(gòu)造方法

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的構(gòu)造方法中,調(diào)用Looper.myLooper()方法獲取一個Looper對象,如果Looper對象為空,則會拋異常,沒有Looper對象不能創(chuàng)建Handler對象。但是我們在主線程new Handler的時候,并沒有調(diào)用Looper.prepare()和Looper.loop()方法初始化Looper,也不會出現(xiàn)異常,這是因為Android系統(tǒng)在主線程創(chuàng)建的時候幫我們把Looper初始化了

9.1 主線程設(shè)置Looper,在ActivityThread類里面

public static final void main(String[] args) {
    ....
    // 1.主線程創(chuàng)建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 系統(tǒng)創(chuàng)建并維護(hù)的,創(chuàng)建的時候系統(tǒng)執(zhí)行了Looper.prepare();方法,該方法內(nèi)部創(chuàng)建了MessageQueue 消息隊列(也叫消息池),該消息隊列是Message 消息的容器,用于存儲通過handler發(fā)送過來的Message。MessageQueue 是Looper 對象的成員變量,Looper 對象通過ThreadLocal 綁定在MainThread 中。因此我們可以簡單的這么認(rèn)為:MainThread 擁有唯一的一個Looper 對象,該Looper 對象有用唯一的MessageQueue 對象,MessageQueue 對象可以存儲多個Message。

MainThread 中需要程序員手動創(chuàng)建Handler 對象,并覆寫Handler 中的handleMessage(Message msg)方法,該方法將來會在主線程中被調(diào)用,在該方法里一般會寫與UI 修改相關(guān)的代碼。

MainThread 創(chuàng)建好之后,系統(tǒng)自動執(zhí)行了Looper.loop();方法,該方法內(nèi)部開啟了一個“死循環(huán)”不斷的去之前創(chuàng)建好的MessageQueue 中取Message。如果一有消息進(jìn)入MessageQueue,那么馬上會被Looper.loop();取出來,取出來之后就會調(diào)用之前創(chuàng)建好的handler 對象的handleMessage(Message)方法。

newThread 線程是我們程序員自定new 出來的子線程。在該子線程中處理完我們的“耗時”或者網(wǎng)絡(luò)訪問任務(wù)后,調(diào)用主線程中的handler 對象的sendMessage(msg)方法,該方法一被執(zhí)行,內(nèi)部將就msg添加到了主線程中的MessageQueue 隊列中,這樣就成為了Looper.loop()的盤中餐了,等待著被消費。

上面的過程有點類似生產(chǎn)者和消費者的過程。newThread 屬于生產(chǎn)者,負(fù)責(zé)生產(chǎn)Message,MainThread 屬于消費者。這是一個很復(fù)雜的過程,但是Android 顯然已經(jīng)將這種模式給封裝起來了,就叫Handler 機制。我們使用時只需要在主線程中創(chuàng)建Handler,并覆寫handler 中的handleMessage 方法,然后在子線程中調(diào)用handler 的sendMessage(msg)方法即可。

獲取Looper對象后,接著獲取Looper身上的MessageQueue對象,Handler就是把消息發(fā)送到該消息隊列

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

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

發(fā)送消息幾個方法sendXxx(),最終都是調(diào)用enqueueMessage()方法,在該方法內(nèi)部調(diào)用的是消息隊列MessageQueue的enqueueMessage()方法,把消息發(fā)送到消息隊列

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

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) {
                // 當(dāng)前發(fā)送的message需要馬上被處理調(diào),needWake喚醒狀態(tài)置true
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                // 當(dāng)前發(fā)送的message被排隊到其他message的后面,needWake喚醒狀態(tài)置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;
}

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

9.4 刪除Callback和Message

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

一般在Activity銷毀的時候調(diào)用

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

**10. Looper **

輪詢器,從messagequeue取消息,分發(fā)給handler處理。創(chuàng)建Handler對象,必須有Looper對象,而Looper對象的初始化需要調(diào)用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、在主線程中設(shè)置Looper, new Looper()里面創(chuàng)建了一個MessageQueue
    sThreadLocal.set(new Looper());
}

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

先從ThreadLocal中獲取一個Looper對象,如果該Looper對象不為空,則拋異常,這是因為一個線程僅能夠綁定一個Looper對象。ThreadLocal是一個用于線程范圍內(nèi)共享數(shù)據(jù)的底層是一個map結(jié)構(gòu)的類,key是當(dāng)前線程,value是Looper。如果當(dāng)前線程沒有綁定Looper對象,則new Looper()創(chuàng)建一個Looper對象,并把該Looper對象設(shè)置sThreadLocal

10.2 Looper.loop()

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

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

應(yīng)用場景;主進(jìn)程拿著讀取描述符等待讀取,沒有內(nèi)容時就阻塞,另一個進(jìn)程拿寫入描述符去寫內(nèi)容,喚醒主進(jìn)程,主進(jìn)程拿著讀取描述符讀取到內(nèi)容,繼續(xù)執(zhí)行。

Handler應(yīng)用場景:Handler在主線程中創(chuàng)建,Looper會在死循環(huán)里等待取消息,1、沒取到,就阻塞,2、一旦被子線程喚醒,取到消息,就把Message交給Handler處理。子線程用Handler去發(fā)送消息,拿寫入描述符去寫消息,喚醒主線程。

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);
    }
}

調(diào)用Looper.loop()會開啟一個死循環(huán),從消息隊列MessageQueue取消息,取到消息后調(diào)用msg.target.dispatchMessage(msg);即調(diào)用Handler的dispatchMessage()方法,在該方法內(nèi)部調(diào)用的是handleMessage(),對,就是我們new Handler的時候?qū)崿F(xiàn)的handleMessage()方法

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

handler

11.1 消息處理的優(yōu)先級

在dispatchMessage()方法中,如果msg.callback(一個Runnable)不為空,則先處理Message的Runnable;然后判斷mCallback(通過Handler的構(gòu)造方法傳進(jìn)來的Callback)是否為空,不為空,則執(zhí)行Callback的handleMessage()方法,最后才是執(zhí)行Handler的handleMessage()

所有消息處理的優(yōu)先級是Message的callback --> Handler的mCallback --> Handler的handleMessage()

12. Handler機制的應(yīng)用

12.1 在主線程中給子線程發(fā)送消息

public class MainActivity extends Activity {

    private Handler subHandler;//是在子線程中創(chuàng)建的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);
        /*
         * 匿名內(nèi)部類對象對外部類有一個隱式的強引用
         */
        new Thread(new Runnable() {
            @Override
            public void run() {
                // 1. 創(chuàng)建了Looper對象,然后Looper對象中創(chuàng)建了MessageQueue
                // 2. 并將當(dāng)前的Looper對象跟當(dāng)前的線程(子線程)綁定ThreadLocal
                Looper.prepare();

                // 1. 創(chuàng)建了handler對象,然后從當(dāng)前線程中獲取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();//獲取當(dāng)前線程中的Looper對象
                
                /*
                 * 1. 從當(dāng)前線程中找到之前創(chuàng)建的Looper對象,然后找到 MessageQueue
                 * 2. 開啟死循環(huán),遍歷消息池中的消息
                 * 3. 當(dāng)獲取到msg的時候,調(diào)用這個msg的handler的disPatchMsg方法,讓msg執(zhí)行起來
                 */
                Looper.loop();
                Log.d("tag", "loop()方法執(zhí)行完了");
            }
        }).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;//區(qū)分發(fā)送的消息
                msg.obj = "來自子線程的問候";
                handler.sendMessage(msg);
            }
        }).start();
    }
    
    public void sendMsg2(View view) {
        //從消息池中獲取一個舊的msg,如果沒有重新創(chuàng)建消息
        subHandler.obtainMessage(2, "我是主線程發(fā)送來的祝福").sendToTarget();
    }
}

12.2 同線程內(nèi)不同組件間的消息傳遞

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

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

/**
 * ============================================================
 * Copyright:${TODO}有限公司版權(quán)所有 (c) 2017
 * Author:   AllenIverson
 * Email:    815712739@qq.com
 * 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:
                    // 取得當(dāng)前線程的Looper,此時的線程為主線程(UI線程)
                    Looper looper = Looper.myLooper();
                    // 構(gòu)造一個Handler對象使之與Looper通信
                    MyHandler mHandler = newMyHandler(looper);
                    // 產(chǎn)生一個消息通過Handler傳遞給Looper
                    String msgStr = "main";
                    // 構(gòu)造一個消息,這里what參數(shù)設(shè)為1,obj參數(shù)設(shè)為msgStr變量。
                    Message msg = mHandler.obtainMessage(1, 1, 1, msgStr);
                    // 發(fā)送消息,調(diào)用Handler對象的handleMessage方法
                    mHandler.sendMessage(msg);
                    break;
            }
        }
    }
    // 自定義Handler類
    class MyHandler extends Handler {
        // 指定Looper對象來構(gòu)造Handler對象,而我們平時直接使用的Handler無參構(gòu)造方法實際上默認(rèn)是本線程的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;
            }
        }
    }
}

說明:此程序啟動時,當(dāng)前線程(即主線程, mainthread)已誕生了一個Looper對象,并且有了一個MessageQueue數(shù)據(jù)結(jié)構(gòu)。

  • 調(diào)用Looper類別的靜態(tài)myLooper()函數(shù),以取得目前線程里的Looper對象。looper = Looper.myLooper ();

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

  • 先構(gòu)造一個Message對象,并將數(shù)據(jù)存入對象里。
    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對象接到此訊息時,會調(diào)用其handleMessage()函數(shù)來處理,于是讓msgStr顯示于TextView上(更新UI)。

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

/**
 * ============================================================
 * Copyright:${TODO}有限公司版權(quán)所有 (c) 2017
 * Author:   AllenIverson
 * Email:    815712739@qq.com
 * 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對象來構(gòu)造Handler對象,而我們平時直接使用的Handler無參構(gòu)造方法實際上默認(rèn)是本線程的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() {
            // 獲得當(dāng)前線程的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。于是執(zhí)行mHandler= new MyHandler (mainLooper);此mHandler屬于主線程

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

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

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();
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 引言 由于Android對消息機制的封裝,開發(fā)者在平常的開發(fā)過程中,直接使用Handler對象就能滿足大部分的應(yīng)用...
    紅灰李閱讀 806評論 1 2
  • 前言 Handler是Android消息機制的上層接口,平時使用起來很方便,我們可以通過它把一個任務(wù)切換到Hand...
    eagleRock閱讀 1,765評論 0 13
  • Android平臺上,主要用到的通信機制有兩種:Handler和Binder,前者用于進(jìn)程內(nèi)部的通信,后者主要用于...
    帝都De霧霾閱讀 1,572評論 1 7
  • 本文出自 “阿敏其人” 簡書博客,轉(zhuǎn)載或引用請注明出處。 能簡單說得我們盡量不復(fù)雜: 為了避免ANR,我們會通常把...
    阿敏其人閱讀 34,033評論 5 110
  • 卷閘門不甘心就此搭起結(jié)界, 但這盞燈卻捻滅了煙。 排擋露骨的誘香,我聞不見。 生銹鐵軌擺布轉(zhuǎn)角的過去與未來。 也獨...
    慕?jīng)鯻Florence閱讀 276評論 0 0

友情鏈接更多精彩內(nèi)容