深入理解 Looper、Handler、Message三者關(guān)系,以及Android HandlerThread 解析

1、 概述

Handler 、 Looper 、Message 這三者都與Android異步消息處理線程相關(guān)的概念。那么什么叫異步消息處理線程呢?異步消息處理線程啟動后會進(jìn)入一個無限的循環(huán)體之中,每循環(huán)一次,從其內(nèi)部的消息隊(duì)列中取出一個消息,然后回調(diào)相應(yīng)的消息處理函數(shù),執(zhí)行完成一個消息后則繼續(xù)循環(huán)。若消息隊(duì)列為空,線程則會阻塞等待。說了這一堆,那么和Handler 、 Looper 、Message有啥關(guān)系?其實(shí)Looper負(fù)責(zé)的就是創(chuàng)建一個MessageQueue,然后進(jìn)入一個無限循環(huán)體不斷從該MessageQueue中讀取消息,而消息的創(chuàng)建者就是一個或多個Handler 。

2、 源碼解析

1、Looper
對于Looper主要是prepare()和loop()兩個方法。首先看prepare()方法

1,public static final void prepare() {  
2,        if (sThreadLocal.get() != null) {  
3,          throw new RuntimeException("Only one Looper may be created per thread");  
4,        }  
5,        sThreadLocal.set(new Looper(true));  
6,}  

sThreadLocal是一個ThreadLocal對象,可以在一個線程中存儲變量。可以看到,在第5行,將一個Looper的實(shí)例放入了ThreadLocal,并且2-4行判斷了sThreadLocal是否為null,否則拋出異常。這也就說明了Looper.prepare()方法不能被調(diào)用兩次,同時也保證了一個線程中只有一個Looper實(shí)例~相信有些哥們一定遇到這個錯誤。
下面看Looper的構(gòu)造方法:

private Looper(boolean quitAllowed) {  
        mQueue = new MessageQueue(quitAllowed);  
        mRun = true;  
        mThread = Thread.currentThread();  
}  

在構(gòu)造方法中,創(chuàng)建了一個MessageQueue(消息隊(duì)列)。
然后我們看loop()方法:

1,public static void loop() {  
2,        final Looper me = myLooper();  
3,        if (me == null) {  
4,            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");  
5,        }  
6,        final MessageQueue queue = me.mQueue;  
  
7,      // Make sure the identity of this thread is that of the local process,  
8,        // and keep track of what that identity token actually is.  
9,        Binder.clearCallingIdentity();  
10,        final long ident = Binder.clearCallingIdentity();  
11,
12,        for (;;) {  
13,            Message msg = queue.next(); // might block  
14,            if (msg == null) {  
15,                // No message indicates that the message queue is quitting.  
16,                return;  
17,            }  
18,
19,            // This must be in a local variable, in case a UI event sets the logger  
20,            Printer logging = me.mLogging;  
21,            if (logging != null) {  
22,                logging.println(">>>>> Dispatching to " + msg.target + " " +  
23,                        msg.callback + ": " + msg.what);  
24,            }  
25,  
26,            msg.target.dispatchMessage(msg);  
27,  
28,            if (logging != null) {  
29,                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);  
30,            }  
31,  
32,            // Make sure that during the course of dispatching the  
33,            // identity of the thread wasn't corrupted.  
34,            final long newIdent = Binder.clearCallingIdentity();  
35,            if (ident != newIdent) {  
36,                Log.wtf(TAG, "Thread identity changed from 0x"  
37,                        + Long.toHexString(ident) + " to 0x"  
38,                        + Long.toHexString(newIdent) + " while dispatching to "  
39,                        + msg.target.getClass().getName() + " "  
40,                        + msg.callback + " what=" + msg.what);  
41,            }  
42,  
43,            msg.recycle();  
44,        }  
45,}  

第2行:
public static Looper myLooper() {
return sThreadLocal.get();
}
方法直接返回了sThreadLocal存儲的Looper實(shí)例,如果me為null則拋出異常,也就是說looper方法必須在prepare方法之后運(yùn)行。
第6行:拿到該looper實(shí)例中的mQueue(消息隊(duì)列)
13到45行:就進(jìn)入了我們所說的無限循環(huán)。
14行:取出一條消息,如果沒有消息則阻塞。
27行:使用調(diào)用 msg.target.dispatchMessage(msg);把消息交給msg的target的dispatchMessage方法去處理。Msg的target是什么呢?其實(shí)就是handler對象,下面會進(jìn)行分析。
44行:釋放消息占據(jù)的資源。

Looper主要作用:
1、 與當(dāng)前線程綁定,保證一個線程只會有一個Looper實(shí)例,同時一個Looper實(shí)例也只有一個MessageQueue。
2、 loop()方法,不斷從MessageQueue中去取消息,交給消息的target屬性的dispatchMessage去處理。
好了,我們的異步消息處理線程已經(jīng)有了消息隊(duì)列(MessageQueue),也有了在無限循環(huán)體中取出消息的哥們,現(xiàn)在缺的就是發(fā)送消息的對象了,于是乎:Handler登場了。
2、Handler
使用Handler之前,我們都是初始化一個實(shí)例,比如用于更新UI線程,我們會在聲明的時候直接初始化,或者在onCreate中初始化Handler實(shí)例。所以我們首先看Handler的構(gòu)造方法,看其如何與MessageQueue聯(lián)系上的,它在子線程中發(fā)送的消息(一般發(fā)送消息都在非UI線程)怎么發(fā)送到MessageQueue中的。

01. public Handler() {  
02.         this(null, false);  
03. }  
04. public Handler(Callback callback, boolean async) {  
05.         if (FIND_POTENTIAL_LEAKS) {  
06.             final Class<? extends Handler> klass = getClass();  
07.             if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&  
08.                     (klass.getModifiers() & Modifier.STATIC) == 0) {  
09.                 Log.w(TAG, "The following Handler class should be static or leaks might occur: " +  
10.                    klass.getCanonicalName());  
11.           }  
12.        }  
13.  
14.        mLooper = Looper.myLooper();  
15.       if (mLooper == null) {  
16.            throw new RuntimeException(  
17.                "Can't create handler inside thread that has not called Looper.prepare()");  
18.       }  
19.        mQueue = mLooper.mQueue;  
20.        mCallback = callback;  
21.        mAsynchronous = async;  
22.    }  

14行:通過Looper.myLooper()獲取了當(dāng)前線程保存的Looper實(shí)例,然后在19行又獲取了這個Looper實(shí)例中保存的MessageQueue(消息隊(duì)列),這樣就保證了handler的實(shí)例與我們Looper實(shí)例中MessageQueue關(guān)聯(lián)上了。
然后看我們最常用的sendMessage方法

public final boolean sendMessage(Message msg)  
 {  
     return sendMessageDelayed(msg, 0);  
 } 
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 < 0) {  
           delayMillis = 0;  
       }  
       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);  
   }  

輾轉(zhuǎn)反則最后調(diào)用了sendMessageAtTime,在此方法內(nèi)部有直接獲取MessageQueue然后調(diào)用了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,【如果大家還記得Looper的loop方法會取出每個msg然后交給msg,target.dispatchMessage(msg)去處理消息】,也就是把當(dāng)前的handler作為msg的target屬性。最終會調(diào)用queue的enqueueMessage的方法,也就是說handler發(fā)出的消息,最終會保存到消息隊(duì)列中去。

現(xiàn)在已經(jīng)很清楚了Looper會調(diào)用prepare()和loop()方法,在當(dāng)前執(zhí)行的線程中保存一個Looper實(shí)例,這個實(shí)例會保存一個MessageQueue對象,然后當(dāng)前線程進(jìn)入一個無限循環(huán)中去,不斷從MessageQueue中讀取Handler發(fā)來的消息。然后再回調(diào)創(chuàng)建這個消息的handler中的dispathMessage方法,下面我們趕快去看一看這個方法:

01. public void dispatchMessage(Message msg) {  
02.         if (msg.callback != null) {  
03.             handleCallback(msg);  
04.         } else {  
05.             if (mCallback != null) {  
06.                 if (mCallback.handleMessage(msg)) {  
07.                     return;  
08.                 }  
09.             }  
10.            handleMessage(msg);  
11.       }  
12.    } 

可以看到,第10行,調(diào)用了handleMessage方法,下面我們?nèi)タ催@個方法:

/** 
   * Subclasses must implement this to receive messages. 
   */  
  public void handleMessage(Message msg) {  
  }  

可以看到這是一個空方法,為什么呢,因?yàn)橄⒌淖罱K回調(diào)是由我們控制的,我們在創(chuàng)建handler的時候都是復(fù)寫handleMessage方法,然后根據(jù)msg.what進(jìn)行消息處理。
例如:

private Handler mHandler = new Handler()  
    {  
        public void handleMessage(android.os.Message msg)  
        {  
            switch (msg.what)  
            {  
            case value:  
                  
                break;  
  
            default:  
                break;  
            }  
        };  
    };  

到此,這個流程已經(jīng)解釋完畢,讓我們首先總結(jié)一下
1、首先Looper.prepare()在本線程中保存一個Looper實(shí)例,然后該實(shí)例中保存一個MessageQueue對象;因?yàn)長ooper.prepare()在一個線程中只能調(diào)用一次,所以MessageQueue在一個線程中只會存在一個。
2、Looper.loop()會讓當(dāng)前線程進(jìn)入一個無限循環(huán),不端從MessageQueue的實(shí)例中讀取消息,然后回調(diào)msg.target.dispatchMessage(msg)方法。
3、Handler的構(gòu)造方法,會首先得到當(dāng)前線程中保存的Looper實(shí)例,進(jìn)而與Looper實(shí)例中的MessageQueue想關(guān)聯(lián)。
4、Handler的sendMessage方法,會給msg的target賦值為handler自身,然后加入MessageQueue中。
5、在構(gòu)造Handler實(shí)例時,我們會重寫handleMessage方法,也就是msg.target.dispatchMessage(msg)最終調(diào)用的方法。
好了,總結(jié)完成,大家可能還會問,那么在Activity中,我們并沒有顯示的調(diào)用Looper.prepare()和Looper.loop()方法,為啥Handler可以成功創(chuàng)建呢,這是因?yàn)樵贏ctivity的啟動代碼中,已經(jīng)在當(dāng)前UI線程調(diào)用了Looper.prepare()和Looper.loop()方法。

3、Handler post
今天有人問我,你說Handler的post方法創(chuàng)建的線程和UI線程有什么關(guān)系?
其實(shí)這個問題也是出現(xiàn)這篇博客的原因之一;這里需要說明,有時候?yàn)榱朔奖?,我們會直接寫如下代碼:

mHandler.post(new Runnable()  
        {  
            @Override  
            public void run()  
            {  
                Log.e("TAG", Thread.currentThread().getName());  
                mTxt.setText("yoxi");  
            }  
        });  

然后run方法中可以寫更新UI的代碼,其實(shí)這個Runnable并沒有創(chuàng)建什么線程,而是發(fā)送了一條消息,下面看源碼:

public final boolean post(Runnable r)  
   {  
      return  sendMessageDelayed(getPostMessage(r), 0);  
   }  
private static Message getPostMessage(Runnable r) {  
      Message m = Message.obtain();  
      m.callback = r;  
      return m;  
  }  

可以看到,在getPostMessage中,得到了一個Message對象,然后將我們創(chuàng)建的Runable對象作為callback屬性,賦值給了此message.
注:產(chǎn)生一個Message對象,可以new ,也可以使用Message.obtain()方法;兩者都可以,但是更建議使用obtain方法,因?yàn)镸essage內(nèi)部維護(hù)了一個Message池用于Message的復(fù)用,避免使用new 重新分配內(nèi)存。

public final boolean sendMessageDelayed(Message msg, long delayMillis)  
   {  
       if (delayMillis < 0) {  
           delayMillis = 0;  
       }  
       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);  
   }  

最終和handler.sendMessage一樣,調(diào)用了sendMessageAtTime,然后調(diào)用了enqueueMessage方法,給msg.target賦值為handler,最終加入MessagQueue.
可以看到,這里msg的callback和target都有值,那么會執(zhí)行哪個呢?
其實(shí)上面已經(jīng)貼過代碼,就是dispatchMessage方法:

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

第2行,如果不為null,則執(zhí)行callback回調(diào),也就是我們的Runnable對象。
好了,關(guān)于Looper , Handler , Message 這三者關(guān)系上面已經(jīng)敘述的非常清楚了。
最后來張圖解:



希望圖片可以更好的幫助大家的記憶~~

4、后話
其實(shí)Handler不僅可以更新UI,你完全可以在一個子線程中去創(chuàng)建一個Handler,然后使用這個handler實(shí)例在任何其他線程中發(fā)送消息,最終處理消息的代碼都會在你創(chuàng)建Handler實(shí)例的線程中運(yùn)行。

new Thread()  
        {  
            private Handler handler;  
            public void run()  
            {  
  
                Looper.prepare();  
                  
                handler = new Handler()  
                {  
                    public void handleMessage(android.os.Message msg)  
                    {  
                        Log.e("TAG",Thread.currentThread().getName());  
                    };  
                };

Android不僅給我們提供了異步消息處理機(jī)制讓我們更好的完成UI的更新,其實(shí)也為我們提供了異步消息處理機(jī)制代碼的參考不僅能夠知道原理,最好還可以將此設(shè)計(jì)用到其他的非Android項(xiàng)目中去
轉(zhuǎn)載:http://blog.csdn.net/lmj623565791/article/details/38377229

======================================================================

以上講到了Looper、Handler、Message三者的關(guān)系.在我們的UI線程默默的為我們服務(wù)。其實(shí)我們可以借鑒UI線程Looper的思想,搞個子線程,也通過Handler、Message通信,可以適用于很多場景。

2、HandlerThread實(shí)例

下面看我們模擬大盤走勢的代碼,其實(shí)非常簡單,就一個Activity

package com.zhy.blogcodes.intentservice;

import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.text.Html;
import android.widget.TextView;

import com.zhy.blogcodes.R;


public class HandlerThreadActivity extends AppCompatActivity
{

    private TextView mTvServiceInfo;

    private HandlerThread mCheckMsgThread;
    private Handler mCheckMsgHandler;
    private boolean isUpdateInfo;

    private static final int MSG_UPDATE_INFO = 0x110;

    //與UI線程管理的handler
    private Handler mHandler = new Handler();


    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_thread_handler);

        //創(chuàng)建后臺線程
        initBackThread();

        mTvServiceInfo = (TextView) findViewById(R.id.id_textview);

    }

    @Override
    protected void onResume()
    {
        super.onResume();
        //開始查詢
        isUpdateInfo = true;
        mCheckMsgHandler.sendEmptyMessage(MSG_UPDATE_INFO);
    }

    @Override
    protected void onPause()
    {
        super.onPause();
        //停止查詢
        isUpdateInfo = false;
        mCheckMsgHandler.removeMessages(MSG_UPDATE_INFO);

    }

    private void initBackThread()
    {
        mCheckMsgThread = new HandlerThread("check-message-coming");
        mCheckMsgThread.start();
        mCheckMsgHandler = new Handler(mCheckMsgThread.getLooper())
        {
            @Override
            public void handleMessage(Message msg)
            {
                checkForUpdate();
                if (isUpdateInfo)
                {
                    mCheckMsgHandler.sendEmptyMessageDelayed(MSG_UPDATE_INFO, 1000);
                }
            }
        };


    }

    /**
     * 模擬從服務(wù)器解析數(shù)據(jù)
     */
    private void checkForUpdate()
    {
        try
        {
            //模擬耗時
            Thread.sleep(1000);
            mHandler.post(new Runnable()
            {
                @Override
                public void run()
                {
                    String result = "實(shí)時更新中,當(dāng)前大盤指數(shù):<font color='red'>%d</font>";
                    result = String.format(result, (int) (Math.random() * 3000 + 1000));
                    mTvServiceInfo.setText(Html.fromHtml(result));
                }
            });

        } catch (InterruptedException e)
        {
            e.printStackTrace();
        }

    }

    @Override
    protected void onDestroy()
    {
        super.onDestroy();
        //釋放資源
        mCheckMsgThread.quit();
    }


}

可以看到我們在onCreate中,去創(chuàng)建和啟動了HandlerThread,并且關(guān)聯(lián)了一個mCheckMsgHandler。然后我們分別在onResume和onPause中去開啟和暫停我們的查詢,最后在onDestory中去釋放資源。

這樣就實(shí)現(xiàn)了我們每隔5s去服務(wù)端查詢最新的數(shù)據(jù),然后更新我們的UI,當(dāng)然我們這里通過Thread.sleep()模擬耗時,返回了一個隨機(jī)數(shù),大家可以很輕易的換成真正的數(shù)據(jù)接口。

布局文庫

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                xmlns:tools="http://schemas.android.com/tools"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:paddingLeft="@dimen/activity_horizontal_margin"
                android:paddingRight="@dimen/activity_horizontal_margin"
                android:paddingTop="@dimen/activity_vertical_margin"
                android:paddingBottom="@dimen/activity_vertical_margin">

    <TextView
        android:id="@+id/id_textview"
        android:text="正在加載大盤指數(shù)..."
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

</RelativeLayout>

運(yùn)行效果圖


別問我為什么要用紅色?。?!
ok,當(dāng)然了,我們的效果很單調(diào),但是你完全可以去擴(kuò)展,比如ListView顯示用戶關(guān)注的股票數(shù)據(jù)?;蛘呤瞧渌男枰恢睓z測更新的數(shù)據(jù)。
看到這,你是否好奇呢,HandlerThread的內(nèi)部是如何做的呢?

HandlerThread 源碼分析

對于所有Looper,Handler相關(guān)細(xì)節(jié)統(tǒng)一參考上面提到的文章。

我們輕輕的掀開HandlerThread的源碼,還記得我們是通過

 mCheckMsgThread = new HandlerThread("check-message-coming");
 mCheckMsgThread.start(); 

創(chuàng)建和啟動的對象,那么隨便掃一眼:

package android.os;


public class HandlerThread extends Thread {
    int mPriority;
    int mTid = -1;
    Looper mLooper;

    public HandlerThread(String name) {
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }

    protected void onLooperPrepared() {
    }

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

看到了什么,其實(shí)我們就是初始化和啟動了一個線程;然后我們看run()方法,可以看到該方法中調(diào)用了Looper.prepare(),Loop.loop();

prepare()呢,中創(chuàng)建了一個Looper對象,并且把該對象放到了該線程范圍內(nèi)的變量中(sThreadLocal),在Looper對象的構(gòu)造過程中,初始化了一個MessageQueue,作為該Looper對象成員變量。

loop()就開啟了,不斷的循環(huán)從MessageQueue中取消息處理了,當(dāng)沒有消息的時候會阻塞,有消息的到來的時候會喚醒。如果你不明白我說的,參考上面推薦的文章。

接下來,我們創(chuàng)建了一個mCheckMsgHandler,是這么創(chuàng)建的:

mCheckMsgHandler = new Handler(mCheckMsgThread.getLooper())

對應(yīng)源碼

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

mCheckMsgThread.getLooper()返回的就是我們在run方法中創(chuàng)建的mLooper。

那么Handler的構(gòu)造呢,其實(shí)就是在Handler中持有一個指向該Looper.mQueue對象,當(dāng)handler調(diào)用sendMessage方法時,其實(shí)就是往該mQueue中去插入一個message,然后Looper.loop()就會取出執(zhí)行。

好了,到這我們就分析完了,其實(shí)就幾行代碼;不過有一點(diǎn)我想提一下:

如果你夠細(xì)心你會發(fā)現(xiàn),run方法里面當(dāng)mLooper創(chuàng)建完成后有個notifyAll(),getLooper()中有個wait(),這是為什么呢?因?yàn)榈膍Looper在一個線程中執(zhí)行,而我們的handler是在UI線程初始化的,也就是說,我們必須等到mLooper創(chuàng)建完成,才能正確的返回getLooper();wait(),notify()就是為了解決這兩個線程的同步問題。
轉(zhuǎn)載: http://blog.csdn.net/lmj623565791/article/details/47079737;

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

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

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