Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android開發中的Handler異步通信傳遞機制

Android開發中的Handler異步通信傳遞機制

編輯:關於Android編程

前言

最近剛好在做關於異步通信的需求,那麼,今天我們來講解下Android開發中的Handler異步通信傳遞機制(包括Looper、Message Queue)


目錄

\

定義

Android提供的一套消息傳遞機制


作用

用於實現子線程對UI線程的更新,實現異步消息的處理:
- 在新啟動的線程中發送消息
- 在主線程中獲取並處理信息


為什麼要用Handler

在安卓開發中:
- 為了保證Android的UI操作是線程安全的,Android規定了只允許UI線程修改Activity裡的UI組件;
- 但在實際開發中,必然會用到多個線程並發操作UI組件,這又將導致UI操作的線程不安全

所以問題在於:如何同時滿足:
- 保證線程安全
- 使多個線程並發操作UI組件

Handler消息傳遞機制就是這個問題的。


相關概念

主線程(UI線程)
- 定義:當程序第一次啟動時,Android會同時啟動一條主線程(Main Thread)
- 作用:主線程主要負責處理與UI相關的事件,所以主線程又叫UI線程

子線程則負責一些比較耗時的操作(聯網、取數據、SD卡數據加載等操作),而主線程和子線程之間的通信,就是要靠Handler了。

Message
- 定義:消息,理解為線程間通訊的數據單元(Handler接受和處理的消息對象。)

例如後台線程在處理數據完畢後需要更新UI,則可發送一條包含更新信息的Message給UI線程

Message Queue
- 定義:消息隊列
- 作用:用來存放通過Handler發過來的消息,按照先進先出執行

Handler
- 定義:Handler是Message的主要處理者
- 作用:負責將Message添加到消息隊列&處理Looper分派過來的Message

Looper
- 定義:循環器,扮演Message Queue和Handler之間橋梁的角色

作用:主要負責消息循環:循環取出Message Queue的Message;消息派發:將取出的Message交付給相應的Handler
每個線程中只能擁有一個Looper,但是一個Looper可以和多個線程的Handler綁定起來,也就是說很多個線程可以往一個Looper所持有的MessageQueue中發送消息。這就給我們提供了線程之間通信的可能。
Handler在創建的時候可以顯示指定Looper,這樣在Handler在調用sendMessage()投遞消息的時候會將消息添加到指定的Looper裡面的MessageQueue。如果不指定Looper,Handler默認綁定的是創建它的線程的Looper。


Handler異步通信機制工作流程圖

Handler異步通信傳遞機制流程圖


Handler、Looper、MessageQueue關系類圖

Handler、LoZ喎?/kf/ware/vc/vcGVyoaJNZXNzYWdlUXVldWW52M+1wODNvA==" src="/uploadfile/Collfiles/20160502/2016050211204979.png" title="\" />
Handler
- 提供sendMessage方法,將消息放置到隊列中
- 提供handleMessage方法,定義個各種消息的處理方式;

Looper
- Looper.prepare():實例化Looper對象;為當前線程生成一個消息隊列;
- Looper.loop() :循環從消息隊列中獲取消息,交給Handler處理;此時線程處於無限循環中,不停的從MessageQueue中獲取Message 消息 ;如果沒有消息就阻塞

MessageQueue
- 提供enqueueMessage 方法,將消息根據時間放置到隊列中;
- 提供next方法,從隊列中獲取消息,沒有消息的時候阻塞;

Handler工作流程解釋
異步通信傳遞機制步驟主要包括異步通信的准備、消息發送、消息循環和消息處理

異步通信的准備
包括Looper對象的創建&實例化、MessageQueue隊列的創建和Handler的實例化 消息發送
Handler將消息發送到消息隊列中 消息循環
Looper執行Looper.loop()進入消息循環,在這個循環過程中,不斷從該Message Queue取出消息,並將取出的消息派發給創建該消息的Handler 消息處理
調用該Handler的dispatchMessage(msg)方法,即回調handleMessage(msg)處理消息

好像很復雜?那就先看下這個簡圖了解下:


工作流程詳細講解

由上面可以得知,整個異步消息傳遞機制主要包括Handler、Looper和MessageQueue,接下來通過相應源碼來解析這三部分

第一部分:Looper

Looper主要負責: 1. 自身的創建&創建Message Queue 2. 消息循環(消息取出、派發) 對應職責我們來看下相應的源碼: 1. 自身的創建&創建Message Queue:prepare()方法
public static final void prepare() {
//判斷sThreadLocal是否為null,否則拋出異常
//即Looper.prepare()方法不能被調用兩次
//也就是說,一個線程中只能對應一個Looper實例
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
//sThreadLocal是一個ThreadLocal對象,用於在一個線程中存儲變量
//實例化Looper對象並存放在ThreadLocal
//這說明Looper是存放在Thread線程裡的
        sThreadLocal.set(new Looper(true));
}
//再來看下Looper的構造方法
private Looper(boolean quitAllowed) {
//創建了一個MessageQueue(消息隊列)
//這說明,當創建一個Looper實例時,會自動創建一個與之配對的MessageQueue(消息隊列)
        mQueue = new MessageQueue(quitAllowed);
        mRun = true;
        mThread = Thread.currentThread();
}
**2. 消息循環:loop()方法**

public static void loop() {
//myLooper()方法作用是返回sThreadLocal存儲的Looper實例,如果me為null,loop()則拋出異常
//也就是說loop方法的執行必須在prepare方法之後運行
//也就是說,消息循環必須要先在線程當中創建Looper實例
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
//獲取looper實例中的mQueue(消息隊列)
        final MessageQueue queue = me.mQueue;


        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();
//進入消息循環
        for (;;) {
//next()方法用於取出消息隊列裡的消息
//如果取出的消息為空,則線程阻塞
            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的target其實就是handler對象,下面會繼續分析
            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();
        }
}

總結Looper的作用: 1. 實例化本身、與當前線程綁定、創建與之相應的MessageQueue:prepare()方法

一個線程只會有一個Looper實例,同時一個Looper實例也只有一個MessageQueue

消息循環(消息取出、消息派發):loop()方法
不斷從MessageQueue中去取消息,派發給消息的target屬性的Handler,然後調用相應Handler的dispatchMessage()方法進行消息處理。

第二部分:Handler

主要負責: 1. 在子線程發送消息給MessageQueue 2. 處理Looper派發過來的消息 使用Handler之前,會初始化一個Handler實例

Handler是需要和線程綁定在一起的,在初始化Handler的時候一般通過指定Looper對象從而綁定相應線程,即給Handler指定Looper對象=綁定到了Looper對象所在的線程中,Handler的消息處理回調會在那個線程中執行。一般有兩種方法創建:
1. 通過Loop.myLooper()得到當前線程的Looper對象/通過Loop.getMainLooper()可以獲得當前進程的主線程的Looper對象。
2. 不指定Looper對象,那麼這個Handler綁定到了創建這個線程的線程上,消息處理回調也就在創建線程中執行.

首先看Handler的構造方法

public Handler() {
        this(null, false);
}
public Handler(Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class 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());
            }
        }
//通過Looper.myLooper()獲取了當前線程保存的Looper實例,如果線程沒有Looper實例那麼會拋出異常
//這說明在一個沒有創建Looper的線程中是無法創建一個Handler對象的
//所以說我們在子線程中創建一個Handler時首先需要創建Looper,並且開啟消息循環才能夠使用這個Handler。
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
//獲取了這個Looper實例中保存的MessageQueue(消息隊列)
//這樣就保證了handler的實例與我們Looper實例中MessageQueue關聯上了

        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

上述說明:當Handler初始化時,可通過構造方法自動關聯Looper和相應的MessageQueue

1. Handler向MessageQueue發送消息:對於Handler的發送方式可以分為post和send兩種方式。

send的發送方法: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
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
//調用了enqueueMessage方法
        return enqueueMessage(queue, msg, uptimeMillis);
    }

//調用sendMessage方法其實最後是調用了enqueueMessage方法
 private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
//為msg.target賦值為this,也就是把當前的handler作為msg的target屬性
//如果大家還記得Looper的loop()方法會取出每個msg然後執行msg.target.dispatchMessage(msg)去處理消息,其實就是派發給相應的Handler
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
//最終調用queue的enqueueMessage的方法,也就是說handler發出的消息,最終會保存到消息隊列中去。
        return queue.enqueueMessage(msg, uptimeMillis);
    }

Post的發送方法:sendMessage()

  showhandler.post(new Runnable() {
                @Override
                public void run() {
                    String line = "\n";
                    StringBuffer text = new StringBuffer(show.getText());
                            text.append(line).append("angelababy:Yes,I do");
                            show.setText(text);
                }
相比send方法,post方法最大的不同在於,更新UI操作可直接在重寫的run方法定義。 其實Runnable並沒有創建什麼線程,而是發送了一條消息,下面看源碼:
 public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

  private static Message getPostMessage(Runnable r) {
//創建了一個Message對象
//創建Message對象可以new,也可以使用Message.obtain()方法;
//但是更建議使用obtain方法,因為Message內部維護了一個Message池用於Message的復用,避免使用new 重新分配內存。
        Message m = Message.obtain();
//將我們創建的Runable對象作為callback屬性,賦值給了此message.
        m.callback = r;
//創建了一個Message對象
        return m;
    }

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

從上面的源碼發現了吧?和send中的handler.sendMessage是一樣的。

調用了sendMessageAtTime,然後調用了enqueueMessage方法,給msg.target賦值為handler,最終Handler將消息加入MessagQueue.

但是細心的你會發現,在使用Post方法時會將我們創建的Runable對象作為callback屬性賦值給了此message
那麼msg的callback和target都有值,那麼會執行哪個呢?
我們已知回調發送消息的方法是:dispatchMessage()

 public void dispatchMessage(Message msg) {
//一開始就會進行判斷
//如果msg.callback屬性不為null,則執行callback回調,也就是我們的Runnable對象
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

####2. 處理Looper派發過來的消息:dispathMessage()方法


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

    public void handleMessage(Message msg) {
    }
可以看到dispathMessage()方法裡調用了 handleMessage()方法,但handleMessage()是一個空方法 因為Handler發送消息過來是希望進行一定的處理,至於怎麼處理消息是該Handler最終控制的,所以我們在創建handler時需要通過復寫handleMessage()方法從而實現我們需要的消息處理方式,然後根據msg.what標識進行消息處理。

這就是為什麼我們在主線程中實例化Handler的時候需要重寫handleMessage()

特別注意

在一個Android應用啟動的時候,會創建一個主線程,即ActivityThread(也叫UI線程),在ActivityThread中有一個靜態的main方法:應用程序的入口點

//一個進程會默認生成一個主線程
public static void main(String[] args) {
 ......
//主線程生成時自動通過prepareMainLooper方法為主線程創建一個Looper
//prepare()方法是用於在子線程中創建一個Looper對象,在子線程中是可以退出消息循環的:調用消息隊列的quit方法
//Looper生成時會自動生成與之配套的消息隊列
Looper.prepareMainLooper(); 
ActivityThread thread = new ActivityThread(); 
thread.attach(false);
 ...... 
//loop()方法開啟消息循環
//主線程的消息循環是不允許被退出的
Looper.loop(); 
throw new RuntimeException("Main thread loop unexpectedly exited");
}

第三部分:MessageQueue

即消息隊列,用於存放Handler發送過來的消息

為了提高插入刪除的效率,采用單鏈表的方式實現。

對於MessageQueue,我們來看下入隊和出隊操作

MessageQueue入隊

boolean enqueueMessage(Message msg, long when) {

    ......

    synchronized (this) {

        ......

        msg.markInUse();
        msg.when = when;
        Message p = mMessages;
        boolean needWake;
        if (p == null || when == 0 || when < p.when) {
            // New head, wake up the event queue if blocked.
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked;
        } else {
            // Inserted within the middle of the queue.  Usually we don't have to wake
            // up the event queue unless there is a barrier at the head of the queue
            // and the message is the earliest asynchronous message in the queue.
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev;
            for (;;) {
                prev = p;
                p = p.next;
                if (p == null || when < p.when) {
                    break;
                }
                if (needWake && p.isAsynchronous()) {
                    needWake = false;
                }
            }
            msg.next = p; // invariant: p == prev.next
            prev.next = msg;
        }

        // We can assume mPtr != 0 because mQuitting is false.
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}

消息的入隊(插入)過程
- 首先判斷消息隊列裡有沒有消息,沒有的話則將當前插入的消息作為隊頭,並且這時消息隊列如果處於等待狀態的話則將其喚醒。
- 若是在中間插入,則根據Message創建的時間進行插入。

MessageQueue出隊

Message next() {

    ......

    int nextPollTimeoutMillis = 0;
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }
// nativePollOnce方法在native層,若是nextPollTimeoutMillis為-1,這時候消息隊列處於等待狀態。   
        nativePollOnce(ptr, nextPollTimeoutMillis);

        synchronized (this) {
            // Try to retrieve the next message.  Return if found.
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;

            if (msg != null && msg.target == null) {
                // Stalled by a barrier.  Find the next asynchronous message in the queue.
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
//按照我們設置的時間取出消息
            if (msg != null) {
                if (now < msg.when) {
                    // Next message is not ready.  Set a timeout to wake up when it is ready.
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    // Got a message.
                    mBlocked = false;
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next;
                    }
                    msg.next = null;
                    if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                    msg.markInUse();
                    return msg;
                }
            } else {
// 如果消息隊列中沒有消息,將nextPollTimeoutMillis設為-1,下次循環消息隊列則處於等待狀態
                nextPollTimeoutMillis = -1;
            }


//退出消息隊列,返回null,這時候Looper中的消息循環也會終止。 
            if (mQuitting) {
                dispose();
                return null;
            }

            ......
        }

        .....
    }
}

Thread、Looper、Handler之間的對應關系:

一個Thread(線程)只能有一個Looper,可以有多個Handler 一個Looper可以綁定多個Handler; 一個Handler只能綁定一個Looper;

\


回顧工作原理圖

把Handler工作原理都講解完了,我們再來看下一開始說的工作原理圖,你大概會有更深的理解了。
\


實例

沒有實際應用的博客都不是好博客!現在是時候看一下實際應用了
1. 布局文件:
activity_main




    

2. 1 send方法:MainActivity

package com.example.carson_ho.handlerdemo;

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


public class MainActivity extends AppCompatActivity {
    private TextView show;
    private Handler showhandler;

    @Override
    //主線程創建時便自動創建Looper和對應的MessageQueue,之前執行Loop()進入消息循環
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        show = (TextView) findViewById(R.id.show);
    //實例化Handler,這裡並無指定Looper,即自動綁定當前線程(主線程)的Looper和MessageQueue
        showhandler = new FHandler();
    //啟動子線程
        new Thread_1().start();
        new Thread_2().start();

    }

    class FHandler extends Handler{
        //通過復寫handlerMessage()從而決定如何進行更新UI操作
        @Override
        public void handleMessage(Message msg) {

            StringBuffer text = new StringBuffer();
            switch (msg.what) {
                case 1:
                    text.append("I love Carson_Ho");
                    show.setText(text);
                    break;
                case 2:
                    text.append("I hate Carson_Ho");
                    show.setText(text);
                    break;

            }
        }
    }

    class Thread_1 extends Thread {
        @Override
        public void run() {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //定義要發送的消息
            Message msg = Message.obtain();
            msg.what = 1;//用於消息的標識
            msg.obj = "AA";//用於消息的存放
            //傳入主線程的Handler並其MessageQueue發送消息
            showhandler.sendMessage(msg);
        }
    }

    class Thread_2 extends Thread {
        @Override
        public void run() {
            try {
                Thread.sleep(8000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Message msg = Message.obtain();
            msg.what = 2;
            msg.obj = "BB";
            showhandler.sendMessage(msg);
        }
    }



}

2. 2 Post方法:MainActivity



package com.example.carson_ho.handlerdemo;

import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;


public class MainActivity extends AppCompatActivity {
    public TextView show;
    public Handler showhandler;

    @Override
    //主線程創建時便自動創建Looper和對應的MessageQueue,之前執行Loop()進入消息循環
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        show = (TextView) findViewById(R.id.AA);
        StringBuffer text = new StringBuffer();
        text.append("Carson_Ho:Do you love me?");
        show.setText(text);
    //實例化Handler,這裡並無指定Looper,即自動綁定當前線程(主線程)的Looper和MessageQueue
        showhandler = new Handler();
    //啟動子線程
        new Thread_1().start();
        new Thread_2().start();

    }


    class Thread_1 extends Thread {
        @Override
        public void run() {
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            showhandler.post(new Runnable() {
                @Override
                public void run() {
                    String line = "\n";
                    StringBuffer text = new StringBuffer(show.getText());
                            text.append(line).append("angelababy:Yes,I do");
                            show.setText(text);
                }

                });
            }
        }



    class Thread_2 extends Thread {
        @Override
        public void run() {
            try {
                Thread.sleep(8000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            showhandler.post(new Runnable() {
                @Override
                public void run() {
                    String line = "\n";
                    StringBuffer text = new StringBuffer(show.getText());
                    text.append(line).append("黃曉明:what the fuck?");
                    show.setText(text);
                }

            });
        }
    }
}

總結

本文對Handler異步通信機制全面解析(包含Looper、Message Queue)進行了全面介紹和分析,接下來我會介紹繼續介紹Android開發中的相關知識。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved