Android-異步消息處理機(jī)制2以及HandlerThread的介紹

Android-異步消息處理機(jī)制2以及HandlerThread的介紹

之間在上篇文章中介紹過(guò)了Android的異步消息處理機(jī)制,這里再進(jìn)行一些補(bǔ)充,說(shuō)下多線程中的消息處理以及HandlerThread的介紹,如果你還不是很了解handler的機(jī)制,可以先看看上篇文章Android-異步消息處理機(jī)制(Handler,Looper,Message)

在上篇文章中開(kāi)頭說(shuō)過(guò)我們平時(shí)在子線程中去更新ui的操作一般是通過(guò)handler來(lái)發(fā)送message的方式進(jìn)行處理,但是其實(shí)還有幾種更簡(jiǎn)便的方式來(lái)進(jìn)行ui的操作:

  • Handler的post()方法

  • View的post()方法

  • Activity的runOnUiThread()方法

代碼如下:

new Thread(new Runnable() {
    @Override
    public void run() {
        //1.普遍的方式
        //發(fā)送消息
        Message msg = Message.obtain();
        msg.what = 111;
        Bundle bundle=new Bundle();
        bundle.putString("huan","hello");
        msg.setData(bundle);
        mHandler.sendMessage(msg);

        //2.post的方式
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                //可以直接在這兒進(jìn)行ui的更新
            }
        });

        //3.view.post方式
        mTvHelloWord.post(new Runnable() {
            @Override
            public void run() {
                //可以直接在這兒進(jìn)行ui的更新
            }
        });

        //4.runOnUiThread方式
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                //可以直接在這兒進(jìn)行ui的更新
            }
        });
    }
}).start();

從代碼中可以明顯的看出,后面三種方式要簡(jiǎn)便的多,而且是一種比一種簡(jiǎn)便,都不需要通過(guò)Message 的創(chuàng)建去封裝消息;第二種方式還需要使用handler作為載體,第三種方式需要使用view來(lái)作為載體,第四種方式直接用runOnUiThread方法即可。為什么可以這樣寫(xiě)也能達(dá)到目的?進(jìn)去看看知道了。

1.先說(shuō)下handler post方式的使用,我們不需要再去創(chuàng)建一個(gè)Message對(duì)象,把數(shù)據(jù)封裝在Message中,然后在handler的callback中進(jìn)行ui的操作,而是直接可以在run中進(jìn)行操作。進(jìn)入到post方法中:

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

所噶,原來(lái)還是通過(guò)sendMessageDelayed(Message msg, long delayMillis)這個(gè)方法來(lái)進(jìn)行操作的,并且將我們傳入的runnable對(duì)象通過(guò)getPostMessage方法轉(zhuǎn)為了一個(gè)Message;那我們?cè)龠M(jìn)入getPostMessage方法中一看應(yīng)該就能明了了:

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

創(chuàng)建了一個(gè)消息,并把這個(gè)消息的callback設(shè)置為了我們傳入的runnnable;還記得我們上篇文章說(shuō)過(guò)的在Looper進(jìn)行消息循環(huán)的時(shí)候,取出來(lái)的消息將會(huì)通過(guò)dispatchMessage這個(gè)方法進(jìn)行處理:

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

首先判斷的就是msg的callback是否為null,如果不為null就會(huì)執(zhí)行handleCallback(msg):

private static void handleCallback(Message message) {
    message.callback.run();
}

直接調(diào)用了msg的runnable對(duì)象也就是我們一開(kāi)始傳給msg的runnable對(duì)象,瞬間就很清晰明了了。

2.view.post方式

view.post方法和handler的寫(xiě)法幾乎一致,看看它的里面實(shí)現(xiàn)了什么:

public boolean post(Runnable action) {
    final AttachInfo attachInfo = mAttachInfo;
    if (attachInfo != null) {
        return attachInfo.mHandler.post(action);
    }
    // Assume that post will succeed later
    ViewRootImpl.getRunQueue().post(action);
    return true;
}

原來(lái)就是通過(guò)attachInfo獲取到了handler,然后執(zhí)行handler的post方法而已。換湯不換藥。

3.runOnUiThread方式

這個(gè)方法其實(shí)是屬于Activity的方法,也就是只能在Activity中才能使用,代碼如下:

public final void runOnUiThread(Runnable action) {
    if (Thread.currentThread() != mUiThread) {
        mHandler.post(action);
    } else {
        action.run();
    }
}

首先判斷了當(dāng)前線程是否是ui線程,如果不是,就用handler post的方式;否則就直接run。

看完了這三種方式的源代碼發(fā)現(xiàn)其實(shí)底層都是通過(guò)handler發(fā)送message 的方式來(lái)進(jìn)行消息的處理;所以平時(shí)我們?cè)陂_(kāi)發(fā)android的過(guò)程中如果明白了handler的消息機(jī)制;那么這幾種方法你都是可以任意使用的,哪種方便用哪種。

Looper和handler的同步關(guān)系:

接下來(lái)說(shuō)下在多線程中handler和looper的處理;先模擬一個(gè)運(yùn)行場(chǎng)景:

  1. 主線程中開(kāi)啟一個(gè)線程1;線程1開(kāi)啟的時(shí)候會(huì)啟動(dòng)線程2;
  2. 線程2啟動(dòng)后獲取到Looper,并執(zhí)行l(wèi)ooper.loop ;
  3. 將線程2中的looper對(duì)象賦值給線程1中的looper對(duì)象
  4. 通過(guò)線程1中的looper對(duì)象,在主線程中創(chuàng)建一個(gè)hanlder對(duì)象;
  5. 通過(guò)handler發(fā)送一個(gè)msg,請(qǐng)問(wèn)這個(gè)msg是在哪個(gè)線程中收到這個(gè)消息并進(jìn)行處理;

看下代碼:
線程1:

public class LooperThread1 extends Thread {

    /**
     * 定義一個(gè)public的成員變量Looper
     */
    public Looper myLooper = null;

    /**
     * 初始化Looper,開(kāi)啟消息循環(huán)
     */
    public void initLooper(){
        Looper.prepare();
        myLooper = Looper.myLooper();
        Looper.loop();
    }

    @Override
    public void run() {
        //開(kāi)啟另一個(gè)線程
        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                initLooper();
            }
        });
        thread2.start();
        Log.e("test", "thread2 id:" + thread2.getId());
    }
}

主線程:

//主線程id
Log.e("test", "Main Thread:" + Thread.currentThread().getId());
LooperThread1 myLooperThread1=new LooperThread1();
myLooperThread1.start();
//線程1 id
Log.e("test","thread1 id:"+myLooperThread1.getId());
Looper looper=myLooperThread1.myLooper;
android.os.Handler handler=new android.os.Handler(looper, new android.os.Handler.Callback() {
    @Override
    public boolean handleMessage(Message msg) {
        if(msg.what==111){
            Log.e("test","handleMessage :111");
            //當(dāng)前線程
            Log.e("test","current Thread:"+Thread.currentThread().getId());
        }
        return false;
    }
});
handler.sendEmptyMessage(111);

最開(kāi)始我是為了驗(yàn)證這個(gè)handler發(fā)送的消息最終是在哪個(gè)線程中接收,但是其實(shí)這段代碼是有問(wèn)題的,一運(yùn)行會(huì)報(bào)java.lang.NullPointerException。具體的原因就在與:創(chuàng)建handler的時(shí)候,傳入的這個(gè)looper為空。我們?cè)讷@取線程1的looper對(duì)象時(shí),這個(gè)時(shí)候可能線程2還沒(méi)有執(zhí)行完獲取到looper;所以問(wèn)題就出在這兒;不過(guò)我們可以加一個(gè)同步鎖的方式來(lái)解決,但是如果每次寫(xiě)你都去這樣操作那不是很麻煩? 所以,android給我們推薦了一個(gè) HandlerThread線程來(lái)解決多線程中handler和looper之間同步的問(wèn)題。

HandlerThread中的源碼:

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

/**
 * This method returns the Looper associated with this thread. If this thread not been started
 * or for any reason is isAlive() returns false, this method will return null. If this thread 
 * has been started, this method will block until the looper has been initialized. 
 * @return The looper.
 */
public Looper getLooper() {
    if (!isAlive()) {
        return null;
    }
   
    // If the thread has been started, wait until the looper has been created.
    synchronized (this) {
        while (isAlive() && mLooper == null) {
            try {
                wait();
            } catch (InterruptedException e) {
            }
        }
    }
    return mLooper;
}

其實(shí)就是加上了一個(gè)同步鎖的機(jī)制,并且在run中自動(dòng)幫我們創(chuàng)建好了looper;那么我們接下來(lái)再來(lái)驗(yàn)證下消息的處理是在哪個(gè)線程:

//主線程id
Log.e("test", "Main Thread:" + Thread.currentThread().getId());
HandlerThread handlerThread = new HandlerThread("handler_thread");
handlerThread.start();
Log.e("test", "HandlerThread:" + handlerThread.getId());
android.os.Handler handler=new android.os.Handler(handlerThread.getLooper(), new android.os.Handler.Callback() {
    @Override
    public boolean handleMessage(Message msg) {
        if(msg.what==222){
            Log.e("test","receiveMsh thread id:"+Thread.currentThread().getId());
        }
        return false;
    }
});
handler.sendEmptyMessage(222);

打印log為:

得出結(jié)論:handler發(fā)送的Message處理,不會(huì)依賴與創(chuàng)建handler時(shí)所在的線程,依賴與傳入的looper對(duì)象所在的線程,looper所在的線程來(lái)執(zhí)行msg的處理;

到這里,通過(guò)兩篇文章總算是將handler這個(gè)知識(shí)點(diǎn)大部分總結(jié)完了;接下來(lái)會(huì)繼續(xù)總結(jié)其他技術(shù)點(diǎn)的知識(shí);如果文章有疏漏,錯(cuò)誤,望君能給我評(píng)論指出。謝謝!

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

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

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