天天看點

【Android 開發】: Android 消息處理機制之四: Android 消息循環 Looper 及其源碼解析

  相關連接配接: Android 消息處理機制之一: Handler 與 Message

           Android 消息處理機制之二: Message 中 obtain()源代碼剖析

           Android 消息處理機制之三: Handler 中 sendMessage() 源代碼剖析

  上一講我們學習Handler和Message的一些使用方式,我們知道Handler它會發送消息和處理消息,并且關聯一個子線程,如何發送消息入隊和出隊處理消息等這些都是交給Looper去管理分發的,也就是它是負責整個消息隊列運轉的一個類,這一講我們就來學習一下Android中的Looper的操作。

一、Looper類介紹

  這個類是用來在一個線程中運作一個消息循環(Message),預設情況下線程是沒有一個消息循環來關聯它們的,在這個線程中調用prepare()方法來啟動一個循環,然後調用loop()就可以處理消息至到循環停止。

  下面就是一個典型的例子實作一個Looper線程,使用 prepare()方法 和 loop()來建立一個初始的Handler并且能夠與消息循環(Looper)進行溝通關聯

【Android 開發】: Android 消息處理機制之四: Android 消息循環 Looper 及其源碼解析

  【注意】:預設情況下的android新誕生的一個線程是沒有開啟一個消息循環(Looper)的,但是主線程除外,主線程系統會自動為其建立Looper對象,開啟消息循環。

二、程式Demo

1. 布局檔案定義一個Button和TextView,這裡不貼出來,讀者可以閱讀附件源碼

2. MainActivity.java

...
public class MainActivity extends Activity {

    private Button btn;
    private TextView txt;
    private MyHandler mHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
       ...
        mHandler = new MyHandler();
        btn.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                // 啟動線程
                new Thread(new MyThread()).start();
            }
        });
    }

    public class MyThread implements Runnable {

        @Override
        public void run() {
            // TODO Auto-generated method stub
            Message msg = Message.obtain();
            msg.obj = "AHuier";
            mHandler.sendMessage(msg);
        }

    }

    public class MyHandler extends Handler {

        public MyHandler() {
            super();
            // TODO Auto-generated constructor stub
        }

        // Handler中有個傳遞Looper對象的構造方法,這個構造方法比較少用
        public MyHandler(Looper looper) {
            super(looper);
            // TODO Auto-generated constructor stub
        }

        @Override
        public void handleMessage(Message msg) {
            // TODO Auto-generated method stub
            super.handleMessage(msg);
            txt.setText("接受子線程發送的消息 --->" + msg.obj);
        }
    }
...
 }
           
3. 程式執行結果
【Android 開發】: Android 消息處理機制之四: Android 消息循環 Looper 及其源碼解析

4. 【說明】: 在上面的代碼中我們并沒有去手動生成Looper對象,主線程依然可以完成接受子線程消息并顯示的操作,在這裡我們需要明白為什麼我們之前的例子中雖然沒有建立一個Looper去管理消息,但是子線程中發送消息依然能夠被主線程接受到,原因是因為我們主線程中已經存在了預設的一個Looper對象。

   這裡我們在做一個小測試,我們給其生成一個Looper對象,在onCreate()方法中添加代碼如下:

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initComponent();
        // 在Activity中有一個預設的Looper對象,來處理子線程發送的消息
        // 這裡我們嘗試的給其生成一個Looper對象,也是可以的
        Looper looper = Looper.myLooper(); //獲得與子線程關聯的Looper對象
        mHandler = new MyHandler(looper);
        btn.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                // 啟動線程
                new Thread(new MyThread()).start();
            }
        });
    }
           

程式執行依然會接受到子線程發送的消息。為什麼會是這樣的呢?我們來檢視一下它們的源碼

1) 檢視Handler源碼中的構造方法

/**
     * Default constructor associates this handler with the queue for the
     * current thread.
     *
     * If there isn't one, this handler won't be able to receive messages.
     */
    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;
    }
           

可以發現在其構造方法中就已經預設為幫其生成一個Looper對象了: mLooper = Looper.myLooper();

同時從Looper中擷取到一個消息隊列,并且指派給Handler的本地的mQueque,我們在看一下Handler(Looper looper)這個構造方法如下:

/**
     * Use the provided queue instead of the default one.
     */
    public Handler(Looper looper) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = null;
    }
           

同樣也是接受使用者生成的一個Looper對象。是以是底層實作方式都是一模一樣了,從這裡我們也知道了為什麼預設情況下主線程都會預設的Looper對象去維護了。

2) 這裡我們需要在看一下為什麼會調用 Looper.myLooper();會擷取到一個Looper對象,跟蹤其源碼如下:

/**
     * Return the Looper object associated with the current thread.  Returns
     * null if the calling thread is not associated with a Looper.
     */
    public static Looper myLooper() {
        return sThreadLocal.get();
    }
           
繼續跟蹤是誰給其sThreadLocal執行個體化
// sThreadLocal.get() will return null unless you've called prepare().
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
           
sThreadLocal 是從一個本地線程中擷取Looper類型的本地線程ThreadLocal對象,這裡隻需要明白ThreadLocal是一個Android提供管理線程的一個東西。
/** Initialize the current thread as a looper.
      * This gives you a chance to create handlers that then reference
      * this looper, before actually starting the loop. Be sure to call
      * {@link #loop()} after calling this method, and end it by calling
      * {@link #quit()}.
      */
    public static void prepare() {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper());
    }
           
在prepare()方法中,會從sThreadLocal通過get擷取一個本地線程的對象, 如果是空的話,這個東西中将new出來的Looper對象set到本地線程中。檢視ThreadLocal的get和set方法
public T get() {
        // Optimized for the fast path.
        Thread currentThread = Thread.currentThread();
        Values values = values(currentThread);
        if (values != null) {
            Object[] table = values.table;
            int index = hash & values.mask;
            if (this.reference == table[index]) {
                return (T) table[index + 1];
            }
        } else {
            values = initializeValues(currentThread);
        }

        return (T) values.getAfterMiss(this);
    }
           
public void set(T value) {
        Thread currentThread = Thread.currentThread();
        Values values = values(currentThread);
        if (values == null) {
            values = initializeValues(currentThread);
        }
        values.put(this, value);
    }
           
也就是說它終究是通過set的方式講new出來的Looper對象扔到ThreadLocal中,由它來完成初始化和關聯一個線程,如果要得到一個Looper對象就從ThreadLocal中get出來。通過這種方式來關聯和初始化指定線程的Looper對象。
5. 在上面的一個Demo中,我們是實作了子線程發送消息給主線程來更新UI的操作和Looper的關系,子線程預設情況下是沒有Looper的對象的,下面我就來測試一下主線程向子線程發送消息,由于子線程預設沒有Looper,我們就來測試一下這樣實作會發生什麼情況?[注意,這種方式我們一般在實際開發中是很少見的],Demo2如下所示:
1) MainActivity.java 中貼出onCreate()和 MyThread 類裡面的代碼段,讀者可以閱讀附件中的源代碼
@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initComponent();
        new Thread(new MyThread()).start();
        btn.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                // 點選按鈕的時候在UI主線程中向子線程發送消息
                Message message = Message.obtain();
                message.obj = "AHuier";
                mHandler.sendMessage(message);
            }
        });
    }
    public class MyThread implements Runnable {
        @Override
        public void run() {
            mHandler = new Handler(){

                @Override
                public void handleMessage(Message msg) {
                    // TODO Auto-generated method stub
                    super.handleMessage(msg);
                    // 由于不能在子線程中更新UI,是以我們輸出到控制台.
                    System.out.println("接受主線程中發出來的消息" + msg.obj);
                }
                
            };
        }
    }
           
編譯執行發出異常:
【Android 開發】: Android 消息處理機制之四: Android 消息循環 Looper 及其源碼解析

從這裡我們可以得出結論在子線程中,預設情況下是沒有Looper對象的,是以我們需要根據博文上面的Looper類的說明添加prepare()方法 和 loop()方法來啟動Looper消息循環。修改程式如下2)

2) 在MyThread子線程中添加prepare()方法 和 loop()方法完成Looper消息循環的啟動

public class MyThread implements Runnable {
        @Override
        public void run() {
            Looper.prepare();
            mHandler = new Handler(){

                @Override
                public void handleMessage(Message msg) {
                    // TODO Auto-generated method stub
                    super.handleMessage(msg);
                    // 由于不能在子線程中更新UI,是以我們輸出到控制台.
                    System.out.println("接受主線程中發出來的消息" + msg.obj);
                }
                
            };
            Looper.loop();
        }
    }
           
程式執行結果:
【Android 開發】: Android 消息處理機制之四: Android 消息循環 Looper 及其源碼解析
6. 在這裡為什麼添加完這兩個方法之後就會有Looper消息循環了?我們來檢視一下Looper的相關源代碼

1) prepare() 方法我們在上面已經知道,它會初始化目前的線程關聯一個Looper.

2) loop()源碼如下

/**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the loop.
     */
    public static void loop() {
        Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        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 (msg != null) {
                if (msg.target == null) {
                    // No target is a magic identifier for the quit message.
                    return;
                }

                long wallStart = 0;
                long threadStart = 0;

                // This must be in a local variable, in case a UI event sets the logger
                Printer logging = me.mLogging;
                if (logging != null) {
                    logging.println(">>>>> Dispatching to " + msg.target + " " +
                            msg.callback + ": " + msg.what);
                    wallStart = SystemClock.currentTimeMicro();
                    threadStart = SystemClock.currentThreadTimeMicro();
                }

                msg.target.dispatchMessage(msg);

                if (logging != null) {
                    long wallTime = SystemClock.currentTimeMicro() - wallStart;
                    long threadTime = SystemClock.currentThreadTimeMicro() - threadStart;

                    logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
                    if (logging instanceof Profiler) {
                        ((Profiler) logging).profile(msg, wallStart, wallTime,
                                threadStart, threadTime);
                    }
                }

                // 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(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();
            }
        }
    }
           
它首先擷取Looper對象,然後将消息從Looper中取出,然後指派給MessageQueue,讓MessageQueue去管理,接着在While(true)這個死循環裡面一直在輪轉的取消息和分發消息(從Message msg = queue.next();和msg.target.dispatchMessage(msg);)這兩句代碼讀出。

三、總結與相關源碼

    通過上述兩個Demo和Looper相關源碼的分析,我們可以知道Looper作為一個循環機制它的作用就是初始化線程和将Handler與該線程關聯的工作,以及管理,維護整個消息循環的機制。但是具體的發送消息還有處理消息都是靠Handler和Message來完成的。是以在一個新誕生的線程中,Looper都會關聯到這個Thread,以及它的MessageQueue和Handler.

    源碼下載下傳:

   http://download.csdn.net/detail/xukunhui2/6656293