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

來源:互聯網
上載者:User

Android:非同步處理之Handler、Looper、MessageQueue之間的恩怨(三
)前言   如果你在閱讀本文之前,你不知道Handler在Android中為何物,我建議你先看看本系列的第一篇博文《Android:非同步處理之Handler+Thread的應用(一)》;我們都知道在Android系統中不能在子線程中直接更新UI介面,所以我們一般藉助Handler+Thread或者AsyncTask這兩種方法來實現UI介面的更新。而Handler+Thread這方法其實就是子線程向UI主線程進行訊息傳遞,通知UI主線程去更新介面的一套機制。因為有時候面試官比較喜歡和藹可親的考你Handler的這套機制,所以我們結合原始碼深入的研究這套通訊機制是灰常有必要的,你想想如果能鄙視一下面試官,呵呵o(╯□╰)o。。 概述   Google的這套訊息機制是參考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的啟動機制哈。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.