Android:異步處理之Handler、Looper、MessageQueue之間的恩怨(三

前言

 

  如果你在閱讀本文之前,你不知道Handler在Android中為何物,我建議你先看看本系列的第一篇博文《Android:異步處理之Handler+Thread的應用(一)》;我們都知道在Android系統中不能在子線程中直接更新UI界面,所以我們一般借助Handler+Thread或者AsyncTask這兩種方法來實現UI界面的更新。而Handler+Thread這方法其實就是子線程向UI主線程進行消息傳遞,通知UI主線程去更新界面的一套機制。因為有時候面試官比較喜歡和藹可親的考你Handler的這套機制,所以我們結合源代碼深入的研究這套通訊機制是灰常有必要的,你想想如果能鄙視一下面試官,呵呵o(╯□╰)o。。

 

概述

 

  谷歌的這套消息機制是參考windows設計的,姑爺微爺之間有啥專利官司咱也不關心。一般來說,線程都會通過Looper來建立自己的消息循環,並且鎖定一個FIFO的消息隊列MessageQueue,Handler通過Looper來實現Message(消息)在MessageQueue中的存取。每一個Hanlder在實例化的時候都會自動或者手動綁定一個Looper,間接向一個MessageQueue發送Message,所以Handler也封裝瞭消息發送和接收的接口。

 

入門例子

 

  看概述好悶的,琢磨文字不說,晦澀又難懂,記得住又成一個大問題。來不如來個例子瞧瞧比較實在,所以我在這裡給大傢寫瞭一個向子線程發送消息並顯示輸出的例子,強調一下下哦,是向子線程喲。

 

 主要代碼如下:

 

復制代碼

public class MainActivity extends ActionBarActivity {

 

    private Handler handler;

    private Button btn;

    

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        

        btn = (Button) findViewById(R.id.sendmsg);

        

        new HandlerThread().start();//啟動子線程

        

        btn.setOnClickListener(new OnClickListener() {

            @Override

            public void onClick(View v) {

                handler.sendEmptyMessage(0);//向子線程發送消息

            }

        });

    }

 

    class HandlerThread extends Thread{

        @Override

        public void run() {

            //開始建立消息循環

            Looper.prepare();//初始化Looper

            handler = new Handler(){//默認綁定本線程的Looper

                @Override

                public void handleMessage(Message msg) {

                    switch(msg.what){

                    case 0:

                        Toast.makeText(MainActivity.this, "子線程收到消息", Toast.LENGTH_SHORT).show();

                    }

                }

            };

            Looper.loop();//啟動消息循環

        }

    }

}

復制代碼

佈局文件:

 

復制代碼

<LinearLayout 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:orientation="vertical">

 

    <Button

        android:id="@+id/sendmsg" 

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:text="向子線程放炮!"

        />

    

</LinearLayout>

復制代碼

我們隻需要點擊按鈕,發送成功。。。。。

 

 

 

我在裡簡單說一下消息發送的過程:

 

1、啟動一個子線程,並在子線程初始化一個Looper。

 

2、在HandlerThread中實例化Handler,Handler自動綁定上當前線程的Looper。

 

3、重寫Handler裡面的消息處理方法。

 

4、執行Looper.loop()啟動消息循環,子線程進入等待消息狀態。

 

做個小研究

 

  當然,由例子入手講解才容易理解。我們就通過上面梳理好的消息發送流程,結合源代碼來探究消息循環的建立、消息的分發和處理的原理。

 

1、Looper的初始化

 

我們進入Looper中查看源碼:

 

復制代碼

public static void prepare() {

  prepare(true);

}

 

private static void prepare(boolean quitAllowed) {

  if (sThreadLocal.get() != null) {

    throw new RuntimeException("Only one Looper may be created per thread");

  }

  sThreadLocal.set(new Looper(quitAllowed));

}

復制代碼

在我們調用Looper的prepare這個靜態方法的時候,我們發現這個線程創建瞭一個Looper實例,並將其賦值給sThreadLocal這個線程的局部變量中,當然我們可以肯定這個sThreadLocal是當前的線程私有的,不信自己度娘去。我們接下來就要看Looper的構造方法。

 

private Looper(boolean quitAllowed) {

  mQueue = new MessageQueue(quitAllowed);

  mThread = Thread.currentThread();

}

相信大傢的眼睛都是雪亮的吧?!在Looper()中,實例化瞭一個消息隊列(MessageQueue)!並且如我們所願的綁定到瞭mQueue這個局部變量上,在這裡我們可以得出這麼一個結論:調用Looper. prepare()的線程就建立起一個消息循環的對象,但是!並還沒有開始展開消息循環這件大事件。

 

2、實例化Handler並綁定當前線程的Looper

 

我們可以看看Handler的源代碼——Handler的構造方法

 

復制代碼

public Handler() {

  this(null, false);

}

 

public Handler(Callback callback, boolean async) {

  if (FIND_POTENTIAL_LEAKS) {

    final Class<? extends Handler> klass = getClass();

  if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&

      (klass.getModifiers() & Modifier.STATIC) == 0) {

    Log.w(TAG, "The following Handler class should be static or leaks might occur: " +

      klass.getCanonicalName());

    }

  }

 

  mLooper = Looper.myLooper();

  if (mLooper == null) {

    throw new RuntimeException(

      "Can't create handler inside thread that has not called Looper.prepare()");

  }

  mQueue = mLooper.mQueue;

  mCallback = callback;

  mAsynchronous = async;

}

復制代碼

在代碼中我們通過handler = new Handler() 調用到瞭Handler(Callback callback, boolean async)這個方法;我們發現mLooper = Looper.myLooper()把線程中的Looper綁定到瞭Handler上,通過mQueue = mLooper.mQueue獲取瞭線程的消息隊列,我當然也可以換句話說:Handler已經綁定到瞭創建此Handler對象的線程的消息隊列上瞭,所以咱們可以開始幹壞事瞭。。。。

 

3、重寫Handler的handleMessage()方法

 

public void handleMessage(Message msg) {}

沒啥好說的,一個空方法而已,提供我們override的入口函數。

 

4、通過Looper.loop()啟動消息循環

 

還是上面的思路,我們進入loop()裡面看看,總會有收獲的。。

 

復制代碼

public static void loop() {

  final Looper me = myLooper();

  if (me == null) {

    throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");

  }

  final MessageQueue queue = me.mQueue;

 

  Binder.clearCallingIdentity();

  final long ident = Binder.clearCallingIdentity();

 

  for (;;) {

    Message msg = queue.next(); // might block

    if (msg == null) {

      return;

    }

 

    Printer logging = me.mLogging;

    if (logging != null) {

      logging.println(">>>>> Dispatching to " + msg.target + " " +

        msg.callback + ": " + msg.what);

    }

 

    msg.target.dispatchMessage(msg);

 

    if (logging != null) {

      logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);

    }

 

    final long newIdent = Binder.clearCallingIdentity();

      if (ident != newIdent) {

        Log.wtf(TAG, "Thread identity changed from 0x"

              + Long.toHexString(ident) + " to 0x"

              + Long.toHexString(newIdent) + " while dispatching to "

              + msg.target.getClass().getName() + " "

              + msg.callback + " what=" + msg.what);

      }

 

    msg.recycle();

  }

}

復制代碼

  在loop()的這個靜態方法中,我們可以註意到for (;;)這個方法,這是死胡同死循環,所以我們將其稱作為“消息循環”,說起來挺形象滴。在消息循環中會調用queue.next()來獲取消息隊列中排隊等待處理的消息,並將其賦值到msg這個變量上;接下來就判斷如果msg != null 就開始分發消息,也就是執行msg.target.dispatchMessage(msg)。在分發消息結束後,將會回收掉這個消息,體現在msg.recycle()這個函數上。

 

msg.target是一個handler對象,表示需要處理這個消息的handler對象,所以我們回到Handler看看dispatchMessage()這個方法瞭:

 

復制代碼

public void dispatchMessage(Message msg) {

  if (msg.callback != null) {

    handleCallback(msg);

  } else {

    if (mCallback != null) {

      if (mCallback.handleMessage(msg)) {

        return;

      }

    }

    handleMessage(msg);

  }

}

復制代碼

  不知道大傢有沒有一眼發現handleMessage()這個方法,這可不是我們在第三步重寫Handler中的方法麼。真相大白,當 msg.callback != null 並且 mCallback != null 時將會調用 handleMessage(msg) 來處理其他線程發送來的消息,我們通過覆蓋這個方法來實現我們具體的消息處理過程;這也就是Handler消息處理機制的全部內容。

 

做個小結吧

 

  通讀全文,我們可以知道消息循環機制的核心就是Looper,因為Looper持有瞭MessageQueue的對象,並且可以被一個線程設為該線程的一個局部變量,我們可以這麼認為這個線程通過Looper擁有瞭一個消息隊列。而Handler的用處就是封裝瞭消息發送和消息處理的方法,在線程通信中,線程可以通過Handler發送消息給創建Handler的線程,通過Looper將消息放入進入消息接收線程的消息隊列,等待Looper取出消息並在最後交給Handler處理具體消息。

 

再說一句

 

  我們會發現在Activity中實例化一個Handler並不需要Looper.prepare()來初始化一個Looper和Looper.loop()來啟動消息循環,因為Activity在構造過程中已經對Looper進行瞭初始化並且建立瞭消息循環,參見ActivityThread.java中的代碼:

 

復制代碼

public final class ActivityThread {

    public static final void main(String[] args) {

        ……

        Looper.prepareMainLooper();

        ……

        ActivityThread thread = new ActivityThread();

        thread.attach(false);

        ……

        Looper.loop();

        ……

        thread.detach();

        ……

    }

}

復制代碼

  Android應用程序進程在啟動的時候,會在進程中加載ActivityThread類,並且執行這個類的main函數,應用程序的消息循環過程就是在這個main函數裡面實現的;如果大傢想要更深入瞭解的話,建議大傢去研究下Activity的啟動機制哈。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。