Android非同步處理三:Handler+Looper+MessageQueue深入詳解

來源:互聯網
上載者:User

標籤:

在《Android非同步處理一:使用Thread+Handler實現非UI線程更新UI介面》中,我們講到使用Thread+Handler的方式來實現介面的更新,其實是在非UI線程發送訊息到UI線程,通知UI線程進行介面更新,這一篇我們將深入學習Android線程間通訊的實現原理。

概述:Android使用訊息機制實現線程間的通訊,線程通過Looper建立自己的訊息迴圈,MessageQueue是FIFO的訊息佇列,Looper負責從MessageQueue中取出訊息,並且分發到訊息指定目標Handler對象。Handler對象綁定到線程的局部變數Looper,封裝了發送訊息和處理訊息的介面。

例子:在介紹原理之前,我們先介紹Android線程通訊的一個例子,這個例子實現點擊按鈕之後從主線程發送訊息"hello"到另外一個名為” CustomThread”的線程。

代碼下載

LooperThreadActivity.java

package com.zhuozhuo;import android.app.Activity;import android.os.Bundle;import android.os.Handler;import android.os.Looper;import android.os.Message;import android.util.Log;import android.view.View;import android.view.View.OnClickListener;public class LooperThreadActivity extends Activity{    /** Called when the activity is first created. */        private final int MSG_HELLO = 0;    private Handler mHandler;        @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.main);        new CustomThread().start();//建立並啟動CustomThread執行個體                findViewById(R.id.send_btn).setOnClickListener(new OnClickListener() {                        @Override            public void onClick(View v) {//點擊介面時發送訊息                String str = "hello";                Log.d("Test", "MainThread is ready to send msg:" + str);                mHandler.obtainMessage(MSG_HELLO, str).sendToTarget();//發送訊息到CustomThread執行個體                            }        });            }                        class CustomThread extends Thread {        @Override        public void run() {            //建立訊息迴圈的步驟            Looper.prepare();//1、初始化Looper            mHandler = new Handler(){//2、綁定handler到CustomThread執行個體的Looper對象                public void handleMessage (Message msg) {//3、定義處理訊息的方法                    switch(msg.what) {                    case MSG_HELLO:                        Log.d("Test", "CustomThread receive msg:" + (String) msg.obj);                    }                }            };            Looper.loop();//4、啟動訊息迴圈        }    }}

main.xml

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:orientation="vertical"    android:layout_width="fill_parent"    android:layout_height="fill_parent"    ><TextView      android:layout_width="fill_parent"     android:layout_height="wrap_content"     android:text="@string/hello"    /><Button android:text="發送訊息" android:id="@+id/send_btn" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button></LinearLayout>

Log列印結果:

 

原理:

 

我們看到,為一個線程建立訊息迴圈有四個步驟:

1、  初始化Looper

2、  綁定handler到CustomThread執行個體的Looper對象

3、  定義處理訊息的方法

4、  啟動訊息迴圈

下面我們以這個例子為線索,深入Android原始碼,說明Android Framework是如何建立訊息迴圈,並對訊息進行分發的。

1、  初始化Looper : Looper.prepare()

Looper.java

private static final ThreadLocal sThreadLocal = new ThreadLocal();public static final void prepare() {        if (sThreadLocal.get() != null) {            throw new RuntimeException("Only one Looper may be created per thread");        }        sThreadLocal.set(new Looper());}

一個線程在調用Looper的靜態方法prepare()時,這個線程會建立一個Looper對象,並放入到線程的局部變數中,而這個變數是不和其他線程共用的(關於ThreadLocal的介紹)。下面我們看看Looper()這個建構函式:

Looper.java

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

可以看到在Looper的建構函式中,建立了一個訊息佇列對象mQueue,此時,調用Looper. prepare()的線程就建立起一個訊息迴圈的對象(此時還沒開始進行訊息迴圈)。

2、  綁定handler到CustomThread執行個體的Looper對象 : mHandler= new Handler()

Handler.java

final MessageQueue mQueue; final Looper mLooper;public Handler() {        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 = null;}

Handler通過mLooper = Looper.myLooper();綁定到線程的局部變數Looper上去,同時Handler通過mQueue =mLooper.mQueue;獲得線程的訊息佇列。此時,Handler就綁定到建立此Handler對象的線程的訊息佇列上了。

3、定義處理訊息的方法:Override public void handleMessage (Message msg){}

     子類需要覆蓋這個方法,實現接受到訊息後的處理方法。

4、啟動訊息迴圈 : Looper.loop()

      所有準備工作都準備好了,是時候啟動訊息迴圈了!Looper的靜態方法loop()實現了訊息迴圈。

Looper.java

 public static final void loop() {        Looper me = myLooper();        MessageQueue queue = me.mQueue;                // Make sure the identity of this thread is that of the local process,        // and keep track of what that identity token actually is.        Binder.clearCallingIdentity();        final long ident = Binder.clearCallingIdentity();                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);                                // Make sure that during the course of dispatching the                // identity of the thread wasn‘t corrupted.                final long newIdent = Binder.clearCallingIdentity();                if (ident != newIdent) {                    Log.wtf("Looper", "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();            }        }    }

while(true)體現了訊息迴圈中的“迴圈“,Looper會在迴圈體中調用queue.next()擷取訊息佇列中需要處理的下一條訊息。當msg != null且msg.target != null時,調用msg.target.dispatchMessage(msg);分發訊息,當分發完成後,調用msg.recycle();回收訊息。

msg.target是一個handler對象,表示需要處理這個訊息的handler對象。Handler的void dispatchMessage(Message msg)方法如下:

Handler.java

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 並且mCallback == null時,這個例子是由handleMessage(msg);處理訊息,上面我們說到子類覆蓋這個方法可以實現訊息的具體處理過程。

 

總結:從上面的分析過程可知,訊息迴圈的核心是Looper,Looper持有訊息佇列MessageQueue對象,一個線程可以把Looper設為該線程的局部變數,這就相當於這個線程建立了一個對應的訊息佇列。Handler的作用就是封裝發送訊息和處理訊息的過程,讓其他線程只需要操作Handler就可以發訊息給建立Handler的線程。由此可以知道,在上一篇《Android非同步處理一:使用Thread+Handler實現非UI線程更新UI介面》中,UI線程在建立的時候就建立了訊息迴圈(在ActivityThread的public static final void main(String[] args)方法中實現),因此我們可以在其他線程給UI線程的handler發送訊息,達到更新UI的目的。

本博文地址:http://blog.csdn.net/mylzc/article/details/6771331 轉載請註明出處

Android非同步處理三:Handler+Looper+MessageQueue深入詳解

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.