Android點將臺:烽火狼煙[-Handler-]

我的全部文章整理在此篇,將陸續更新收錄:知無涯,行者之路莫言終(個人編程之路)java

前言

哥發誓,這是我最後一次分析Handler(這是我第三次說這句話,但願不會有下次了)
提及Handler新手的小噩夢,用起來不難,理解起來煩神android

Handler總覽

Handler.png


1、引入Handler的通常套路

1.說Handler通常套路是從一個異常開始

你覺得我會這麼俗氣地從:非主線程禁止更新UI開始說Handler嗎?--是的
這個異常定義在ViewRootImpl裏,更新TextView的UI爲何ViewRootImpl報異常?
子不教,父之過,教不嚴,師之惰唄。在framework的源碼裏看一下吧,編程

非主線程禁止更新UI.png

---->[ViewRootImpl#checkThread]-------
void checkThread() {
    if (mThread != Thread.currentThread()) {
        throw new CalledFromWrongThreadException(
                "Only the original thread that created a view hierarchy can touch its views.");
    }
}
|--可見在checkThread時會判斷mThread是否是等於當前線程,若是不等於,就異常
|---那mThread又是什麼線程呢?

---->[ViewRootImpl#ViewRootImpl]-------
public ViewRootImpl(Context context, Display display) {
    mThread = Thread.currentThread();
|--在構造方法中被賦值的,也就是說是建立ViewRootImpl時所在的線程
|---ViewRootImpl又是在哪裏被建立的呢?這裏不深刻講了,是在main線程
|---從異常來看是在進行requestLayout方法時崩的

---->[ViewRootImpl#requestLayout]-------
@Override
public void requestLayout() {
    if (!mHandlingLayoutInLayoutRequest) {
        checkThread();
        mLayoutRequested = true;
        scheduleTraversals();
    }
}
複製代碼

2.而後再俗套的說一下怎麼用Handler解決

而後發現Handler好神奇啊,至於那裏神奇也說不出個道道,也就是神祕
Handler的面具之下隱藏着一個有點小複雜的消息機制,這篇就來理一下bash

public class HandlerActivity extends AppCompatActivity {
    private TextView msgTv;
    private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            msgTv.setText("hello");
        }
    };
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.ac_handler);
        msgTv = findViewById(R.id.id_tv_handler);
        msgTv.setOnClickListener(v->{
            new Thread(new Runnable() {
                @Override
                public void run() {
                    mHandler.sendEmptyMessage(0x01);
                }
            }).start();
        });
    }
}
複製代碼

3.源碼中對Handler的描述

本身翻譯的,僅供參考,有不恰之處敬請指出
其中提到了MessageQueuemessage以及runnables異步

|--Handler容許您發送和處理與線程的MessageQueue關聯的消息和可運行對象。
|--每一個Handler實例都與單個線程和該線程的消息隊列相關聯。
|--當您建立一個新的Handler時,它會被綁定到正在建立它的線程的線程/消息隊列上,
|--從那時起,它將向該消息隊列傳遞消息和可運行項,並在它們從消息隊列發出時執行它們。

|--Handler有兩大主要的用處:
|--(1) 安排將消息和可運行項在未來的某個點執行
|--(2) 將在不一樣線程上執行的操做加入隊列。

|--調度消息是經過方法:
|--post(Runnable), postAtTime(Runnable, long), postDelayed(Runnable, long),
|--sendEmptyMessage(int), sendMessage(Message),sendMessageAtTime(Message, long),
|--sendMessageDelayed(Message, long). 
複製代碼

4.主要成員變量

Handler主要成員變量.png

final Looper mLooper;
final MessageQueue mQueue;
final Callback mCallback;
final boolean mAsynchronous;
IMessenger mMessenger;
複製代碼

5.構造方法

看這七個葫蘆娃,核心就只有兩個構造方法(三個hide的,外面不能用),其餘四個能夠用async

Handler的七個構造函數.png

|--對具備指定回調接口的[當前線程]使用Looper,並設置handler是否應該是異步的。
|--默認狀況下,handler是同步的,除非使用此構造函數建立一個嚴格異步的handler。
|--(插句話,此方法是隱藏的,說明外部調用者是沒法建立異步的handler)

|--異步消息表示:不須要對同步消息進行全局排序的中斷或事件。
|--異步消息不受MessageQueue#enqueueSyncBarrier(long)引入的同步屏障的限制。

* @param callback 處理消息的回調接口,或null。
* @param async 若是爲true,handler將爲[發送到它那的每一個Message或Runnable]調用
* Message#setAsynchronous(boolean)
---->[Handler#Handler(Callback,boolean)]-------------------------------
public Handler(Callback callback, boolean async) {
    if (FIND_POTENTIAL_LEAKS) {//final常量---false,因此無論他  
        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();//經過Looper的myLooper方法返回值,爲mLooper賦值
    if (mLooper == null) {//若是mLooper拿不到,報異常
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;//mQueue經過mLooper獲取
    mCallback = callback;//入參
    mAsynchronous = async;//入參
}
|--貌似也沒有作什麼東西,只是將mLooper、mQueue、mCallback、mAsynchronous賦值
|--焦點在Looper.myLooper()上

---->[Handler#Handler(Callback,boolean)]-------------------------------
public Handler(Looper looper, Callback callback, boolean async) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}
|--三參的大佬挺任性,直接賦值,因此Handler的構造函數沒有什麼太特別的
|--下面看一下Looper類
複製代碼

2、赤膽忠心:Looper

1:ThreadLocal

花了點時間看了ThreadLocal,已單獨成文,詳情見:java點將臺:多重影分身[-ThreadLocal-]
這裏不過多探討ThreadLocal,給個小例子,看它的功能ide

public class ThreadLocalTest {
    //實例化一個裝載Integer類型的ThreadLocal靜態變量
    static final ThreadLocal<Integer> sThreadLocal = new ThreadLocal<>();
    //用來測試的共享成員變量
    static int count = 10;
    public static void main(String[] args) throws InterruptedException {
        sThreadLocal.set(count);
        Thread thread = new Thread() {
            @Override
            public void run() {//新建線程
                count++;
                sThreadLocal.set(count);
                System.out.println("new :" + sThreadLocal.get());
            }
        };
        thread.start();
        thread.join();//爲避免歧義,這裏新線程join,讓main線程的打印在新線程執行以後
        System.out.println("main:"+sThreadLocal.get());

    }
}
複製代碼

結果2.png

能夠看出,在新線程中對sThreadLocal.set(),並不會影響main線程的get()
因此共享成員變量count放在sThreadLocal這個籃子裏,能夠保證線程間共享變量的獨立函數

ThreadLocal基本使用2.png


2.Looper類(looper:/'luːpə/ 循環者)
類名:Looper      父類:Object      修飾:public final
實現的接口:[]
包名:android.os   依賴類個數:7
內部類/接口個數:0
源碼行數:344       源碼行數(除註釋):158
屬性個數:8       方法個數:20       public方法個數:18
複製代碼

Looper構造函數+主要成員變量.png

---->[Looper#成員變量]------------
//實例化一個裝載Looper類型的ThreadLocal靜態變量
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
//似有靜態內部成員變量sMainLooper
private static Looper sMainLooper; 
//MessageQueue對象
final MessageQueue mQueue;
//線程對象
final Thread mThread;

---->[Looper#Looper]------------
private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}
|--在Looper的構造函數中,分別對mQueue,mThread進行初始化
|--注意構造函數是私有的,因此沒法直接構造Looper對象

複製代碼

因此本類中必定存在new Looper,搜索一下:oop

搜索.png

//公共靜態方法:準備
---->[Looper#prepare]------------
public static void prepare() {
    prepare(true);
}

//私有靜態方法:準備
---->[Looper#prepare(boolean)]------------
private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) {
    //若是sThreadLocal可與獲取到Looper,拋異常
    //也就是一個線程只可以建立一個Looper
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    //爲空時建立Looper對象,並以sThreadLocal爲鍵,new Looper(quitAllowed)爲值
    //設置到當前線程的threadLocals(ThreadLocalMap對象)上
    sThreadLocal.set(new Looper(quitAllowed));
}

---->[Looper#prepare(boolean)]------------
 public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
}
|--Looper會在因ThreadLocal在每一個線程中獨立,非Looper生成的線程
|--sThreadLocal.get()會獲得null,那prepare()是framework層的那裏初始化(即prepare)的呢?
複製代碼

3.ActivityThread中對Looper的初始化

到framework層的源碼去看一下,程序的入口main方法在ActivityThread中post

ActivityThread中關於Looper.png

---->[ActivityThread#成員變量]---------
final Looper mLooper = Looper.myLooper();

---->[ActivityThread#main]---------
public static void main(String[] args) {
    //略...
    Looper.prepareMainLooper();
    ActivityThread thread = new ActivityThread();
    thread.attach(false);
    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }
    if (false) {
        Looper.myLooper().setMessageLogging(new
                LogPrinter(Log.DEBUG, "ActivityThread"));
    }
    // End of event ActivityThreadMain.
    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    Looper.loop();//開啓loop
    throw new RuntimeException("Main thread loop unexpectedly exited");
    
---->[Looper#prepareMainLooper]---------
public static void prepareMainLooper() {
    //這裏調用了prepare方法,爲sThreadLocal設置了Looper值
    並且在main函數中調用,所在線程爲main線程,即主線程
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {//這裏看出只能prepared一次
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();//在這裏初始化成員變量sMainLooper
    }
}
複製代碼

在這裏爲了方便說明,debug來測試一下:
在main線程和子線程分別進行斷點調試,看一下兩個線程中的Looper.myLooper()
因爲Looper.prepare在main線程中執行,即:sThreadLocal.set在main線程
因此Looper.myLooper(),即sThreadLocal.get()在子線程沒法獲取到Looper,這就是ThreadLocal的做用

Looper.myLooper()測試.png


3、再看Handler

1.使用含Callback的構造函數

之前是直接在Handler中覆寫handleMessage方法,AndroidStudio飄橙色,看起來很礙眼
咱們徹底能夠傳回調來構建Handler,加上Java8的簡化書寫看着爽多了,運行無誤

public class HandlerActivity extends AppCompatActivity {
    private TextView msgTv;
    private Handler mHandler ;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mHandler = new Handler(msg -> {
            msgTv.setText("hello");
            return true;
        });
        setContentView(R.layout.ac_handler);
        msgTv = findViewById(R.id.id_tv_handler);
        msgTv.setOnClickListener(v->{
            Thread thread = new Thread(() ->
                    mHandler.sendEmptyMessage(0x01));
            thread.start();
        });
    }
}
複製代碼

2.看一下CallBack接口
/**
 * Callback interface you can use when instantiating a Handler to avoid
 * having to implement your own subclass of Handler.
  在實例化處理程序時可使用回調接口,以免必須實現本身的處理Handler子類。
 */
  ---->[Handler$Callback]----------------------
public interface Callback {
    /**
     * @param msg A {@link android.os.Message Message} object
     * @return True 若是不須要進一步處理
     */
    public boolean handleMessage(Message msg);
}


/**
 * Handle system messages here.(在這裏處理系統消息)
 */
 ---->[Handler#dispatchMessage]----------------------
public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {//若是mCallback不爲空
        //先回調用 mCallback.handleMessage(msg),返回true的話,以後就return了  
        //這有什麼用? 若是你重寫Handler的handleMessage又有Callback都有的話
        // true就不會再去執行Handler的handleMessage 的方法了,例子以下
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}
複製代碼

3.即便用Callback又覆寫handleMessage

感受很不是很經常使用,瞭解一下便可,
下面代碼return true;結果是hello,return false;結果是hello2

public class HandlerActivity extends AppCompatActivity {
    private TextView msgTv;
    private Handler mHandler;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mHandler = new Handler(msg -> {
            msgTv.setText("hello");
            return true;
        }) {
            @Override
            public void handleMessage(Message msg) {
                msgTv.setText("hello2");
            }
        };
        setContentView(R.layout.ac_handler);
        msgTv = findViewById(R.id.id_tv_handler);
        msgTv.setOnClickListener(v -> {
            Thread thread = new Thread(() -> {
                mHandler.sendEmptyMessage(0x01);
            });
            thread.start();
        });
    }
}
複製代碼

4.在子線程中建立Handler對象是否有用?

如今將建立Handler放在子線程進行,不出所料,崩了

子線程中初始化Handler.png

public class HandlerActivity extends AppCompatActivity {
    private TextView msgTv;
    private Handler mHandler;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Handler.Callback callback = msg -> {
            msgTv.setText("hello");
            return true;
        };
        setContentView(R.layout.ac_handler);
        msgTv = findViewById(R.id.id_tv_handler);
        msgTv.setOnClickListener(v -> {
            Thread thread = new Thread(() -> {
                mHandler = new Handler(callback);//<--建立Handler
                mHandler.sendEmptyMessage(0x01);
            });
            thread.start();
        });
    }
}

---->[看一下崩的緣由]----------------
---->[Handler#Handler(Callback, boolean)]----------------
public Handler(Callback callback, boolean async) {
    //略...
    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;
}
|---前面咱們已經debug過,在子線程中Looper.myLooper()爲空,因此會崩掉
複製代碼

5.如何在子線程中建立Handler

Handler的構造函數中有Looper參數,咱們能夠在外面獲取主線程的Looper對象,給Handler構造
除此以外Context也給咱們提供了獲取main線程Looper的方法,debug能夠看出,二者是同一對象

如何在子線程中建立Handler.png

public class HandlerActivity extends AppCompatActivity {
    private TextView msgTv;
    private Handler mHandler;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Handler.Callback callback = msg -> {
            msgTv.setText("hello");
            return true;
        };
        Looper looper = Looper.myLooper();//獲取main線程的looper
        setContentView(R.layout.ac_handler);
        msgTv = findViewById(R.id.id_tv_handler);
        msgTv.setOnClickListener(v -> {
            Thread thread = new Thread(() -> {
                mHandler = new Handler(looper,callback);
                mHandler.sendEmptyMessage(0x01);
            });
            thread.start();
        });
    }
}
複製代碼

4、消息隊列(難點,核心)

1.回到這個最簡單的消息發送
public class HandlerActivity extends AppCompatActivity {
    private TextView msgTv;
    private Handler mHandler;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Handler.Callback callback = msg -> {
            msgTv.setText("hello");
            return true;
        };
        mHandler = new Handler(callback);

        setContentView(R.layout.ac_handler);
        msgTv = findViewById(R.id.id_tv_handler);

        msgTv.setOnClickListener(v -> {
            Thread thread = new Thread(() -> {
                mHandler.sendEmptyMessage(0x01);
            });
            thread.start();
        });
    }
}
複製代碼

2.走一下Handler源碼

一連串的sendMessageXXX基本上把發送消息的方法走了一遍,
但萬劍歸一,最終調用的是enqueueMessage(MessageQueue queue,Message msg, long uptimeMillis)

sendEmptyMessage(int what) : 發送空消息,
sendEmptyMessageDelayed(int what, long delayMillis),發送延遲空消息
sendMessageDelayed(Message msg, long delayMillis) 發送延遲消息
sendMessageAtTime(Message msg, long uptimeMillis) 定時發送消息
enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) 消息入隊
複製代碼

Handler發送消息.png

---->[Handler#sendEmptyMessage]-----------------
|--只有消息的what(用來表識消息),調用:sendEmptyMessageDelayed
public final boolean sendEmptyMessage(int what){
    return sendEmptyMessageDelayed(what, 0);
}

---->[Handler#sendEmptyMessageDelayed]-----------------
|--只有消息的what(用來表識消息),延遲毫秒數,調用:sendMessageDelayed
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
    Message msg = Message.obtain();
    msg.what = what;
    return sendMessageDelayed(msg, delayMillis);
}

---->[Handler#sendMessageDelayed]-----------------
|--接收一個消息對象,延遲毫秒數,調用 sendMessageAtTime
public final boolean sendMessageDelayed(Message msg, long delayMillis){
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

---->[Handler#sendMessageAtTime]-----------------
|--接收一個消息對象,延遲毫秒數,調用 enqueueMessage
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);
}

---->[Handler#enqueueMessage]-----------------
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;//注意這裏講當前Handler做爲消息的target
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);//調用了MessageQueue的enqueueMessage方法
}
複製代碼

如今咱們面前出現了兩個類:MessageMessageQueue


3.先看Message(消息)類

Message的成員變量有點多

what----int 型,用來表識該信息
arg1----int型,儲存簡單的int數據
arg2----int型,儲存簡單的int數據
obj --- 任意類型,儲存任意數據
replyTo ---- Messenger類型 可選的信使,在那裏回覆這條消息能夠發送。具體如何使用它的語義取決於發送方和接收方。
複製代碼

Message部分紅員變量.png

先從一個例子開始引入Message吧


3.1:new Message()Message.obtain()Handler.obtain()

可見三種方式都能運做,那麼有什麼區別呢?

三種建立消息對象的方式.png

/**
 * 做者:張風捷特烈<br/>
 * 時間:2019/1/25/025:14:24<br/>
 * 郵箱:1981462002@qq.com<br/>
 * 說明:三種建立消息對象的方式
 */
public class HandlerActivity extends AppCompatActivity {
    private TextView msgTv;
    private Handler mHandler;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Handler.Callback callback = msg -> {
            String txt = (String) msg.obj;
            switch (msg.what) {
                case 0x01:
                    txt += "----第一條";
                    break;
                case 0x02:
                    txt += "----第二條";
                    break;
                case 0x03:
                    txt += "----第三條";
                    break;
            }
            msgTv.setText(txt);
            return true;
        };
        mHandler = new Handler(callback);
        setContentView(R.layout.ac_handler);
        msgTv = findViewById(R.id.id_tv_handler);
        msgTv.setOnClickListener(v -> {
            Thread thread = new Thread(() -> {
                //new Message()建立消息
                Message newMsg = new Message();
                newMsg.what = 0x01;
                newMsg.obj = "玉面奕星龍";
                mHandler.sendMessage(newMsg);
                //Message.obtain()建立消息
                Message msgObtain = Message.obtain();
                msgObtain.what = 0x02;
                msgObtain.obj = "張風捷特烈";
                mHandler.sendMessageDelayed(msgObtain, 3000);
                //mHandler.obtainMessage()建立消息
                Message handlerObtain = mHandler.obtainMessage();
                handlerObtain.what = 0x03;
                handlerObtain.obj = "千里巫纓";
                mHandler.sendMessageDelayed(handlerObtain, 6000);
            });
            thread.start();
        });
    }
}
複製代碼

3.2:三者的區別
1. new Message() 直接建立對象,沒什麼好說的


2.---->[Message]------------------
private static final Object sPoolSync = new Object();//鎖對象
private static Message sPool;//消息池的鏈表首
private static int sPoolSize = 0;//當前消息池的大小
private static final int MAX_POOL_SIZE = 50;//消息池的最大尺寸

---->[Message#obtain]------------------
public static Message obtain() {
    synchronized (sPoolSync) {//同步鎖
        if (sPool != null) {//若是消息池不爲空
            Message m = sPool;//將sPool對象賦值給m對象
            sPool = m.next;//m的next賦值給sPool對象
            m.next = null;//將m的next置空
            m.flags = 0; // clear in-use flag
            sPoolSize--;//消息池的大小-1
            return m;//將消息返回
        }
    }
    return new Message();//若是消息池爲空時,返回新的Message對象
}
|-- 這裏很明顯使用了單鏈表,將一個消息從消息池中出列。
|-- 維護消息池的好處不用多說,避免頻繁建立和銷燬Message,
|-- 好比頻繁地發送消息(輪播圖),每次切換一下發一個消息,使用消息池維護會更好

3.---->[Handler#obtainMessage]------------------
public final Message obtainMessage(){
    return Message.obtain(this);
}

public static Message obtain(Handler h) {
    Message m = obtain();
    m.target = h;
    return m;
}

|-- 獲取Message後將target設置成當前handler,這一步enqueueMessage中已經有了,因此二者同樣
|-- 除此以外Message還有不少重載的 `Message obtain`,都是基於Message.obtain,
|-- 同時再爲其設置一些屬性,本質上也沒有什麼太大的區別,本身看一下就好了
複製代碼

4.MessageQueue消息隊列

Message中並無爲消息池中添加消息的方法,那麼消息池是怎麼實現的?
Handler#enqueueMessage爲切入點,看一下這個消息是如何加入消息隊列中的

---->[Handler#enqueueMessage]------------------------
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;//設置當前消息的target爲本Handler
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

---->[MessageQueue#enqueueMessage]------------------------
boolean enqueueMessage(Message msg, long when) {
    if (msg.target == null) {//target爲null,即沒有對應的Handler,拋異常
        throw new IllegalArgumentException("Message must have a target.");
    }
    if (msg.isInUse()) {//消息已經在使用,拋異常
        throw new IllegalStateException(msg + " This message is already in use.");
    }
    synchronized (this) {//this同步鎖
        if (mQuitting) {
            IllegalStateException e = new IllegalStateException(
                    msg.target + " sending message to a Handler on a dead thread");
            Log.w(TAG, e.getMessage(), e);
            msg.recycle();
            return false;
        }
        msg.markInUse();
        msg.when = when;//設置入隊時間
        Message p = mMessages;//將mMessages,即隊首賦給p
        boolean needWake;
        if (p == null || when == 0 || when < p.when) {//p爲空
            //新建隊列頭,若是阻塞,喚醒事件隊列
            msg.next = p;//當前消息做爲隊列首
            mMessages = msg;//維護隊首
            needWake = mBlocked;
        } else {//表示當前消息隊列中有消息了
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev;//聲明前一個消息
            for (;;) {//至關於while(true)的做用
                prev = p;//將原來的隊首元素賦給prev變量
                p = p.next;//將p的下一個消息賦值給p
                if (p == null || when < p.when) {
                    break;
                }
                if (needWake && p.isAsynchronous()) {
                    needWake = false;
                }
            }
            msg.next = p; //將當前消息的下一指向到p
            prev.next = msg;//prev指向當前消息
        }
        // We can assume mPtr != 0 because mQuitting is false.
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}

>看文字估計挺暈的,以上面三個消息爲例,分析一下他們加入的流程
複製代碼

第一條消息入隊.png

當第二條消息入隊時:msg:張風捷特烈
Message p = mMessages  p:玉面奕星龍 不爲空,走下面
Message prev;//聲明前一個消息
for (;;) {//至關於while(true)的做用
    prev = p;//prev:玉面奕星龍
    p = p.next;//p.next =null,執行後 p=null
    if (p == null || when < p.when) {
        break;
    }
    if (needWake && p.isAsynchronous()) {
        needWake = false;
    }
}
msg.next = p; //張風捷特烈-->null
prev.next = msg;//玉面奕星龍-->張風捷特烈-->null


當第三條消息入隊時:msg:百里巫纓
Message p = mMessages  p:玉面奕星龍 不爲空,走下面
Message prev;//聲明前一個消息
for (;;) {//至關於while(true)的做用
    //第一次循環--prev:玉面奕星龍
    //第二次循環--prev:張風捷特烈
    prev = p;
    //第一次循環--p.next = 張風捷特烈,執行後 p=張風捷特烈
    //第二次循環--p.next = null,執行後 p=null
    p = p.next;
    if (p == null || when < p.when) {
        break;
    }
    if (needWake && p.isAsynchronous()) {
        needWake = false;
    }
}
msg.next = p; //百里巫纓-->null,此時prev:張風捷特烈
prev.next = msg;//玉面奕星龍-->張風捷特烈-->百里巫纓-->null

|-- 因而可知,每添加一條消息都是添加到隊尾
複製代碼

5.Looper的loop方法

將消息加入隊列以後是如何取出的?又如何觸發Handler的dispatchMessage回調方法?
這裏略去了打日誌的一些語句,可見loop方法一直將調用queue.next()直到msg == null
在拿到消息後出發msg.target.dispatchMessage(msg);而後就豁然開朗了
但當沒有消息時MessageQueue#next()會被阻塞,而致使loop阻塞。因此next沒法讓輪循中止
要關閉循環使用MessageQueue#quit。

---->[Looper#loop]---------
public static void loop() {
    //獲取當前線程的Looper對象
    final Looper me = myLooper();
    if (me == null) {//若是爲空,報異常。說明當前線程沒有Looper對象
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.")
    }
    //queue使用的是Looper中的mQueue(在構造函數中被初始化)
    final MessageQueue queue = me.mQueue;
    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();
    for (;;) {//至關於while(true)的做用
        Message msg = queue.next(); // 消息
        if (msg == null) {
            return;
        }
        //略...
        final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
        //略...
        final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
        final long end;
        try {
            //在這裏調用了msg.target即該handler的dispatchMessage方法
            msg.target.dispatchMessage(msg);
            end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
         //略...
        msg.recycleUnchecked();
    }
}

---->[MessageQueue#next]---------------
Message next() {
    final long ptr = mPtr;
    if (ptr == 0) {
        return null;
    }
    int pendingIdleHandlerCount = -1; 
    int nextPollTimeoutMillis = 0;
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }
        nativePollOnce(ptr, nextPollTimeoutMillis);//這裏用來一個native方法
        synchronized (this) {
            //嘗試檢索下一條消息。若是發現返回。
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;//前一個msg
            Message msg = mMessages;//當前msg爲隊首
            if (msg != null && msg.target == null) {//通常都有target
                // 被障礙物擋住了。在隊列中查找下一個異步消息
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            if (msg != null) {
                if (now < msg.when) {
                    // 下一條消息沒有準備好。設置超時以在什麼時候喚醒
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integ
                } else {
                    //獲取一個msg-------
                    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;
            }
            // 如今全部掛起的消息都已完成,請處理退出消息
            if (mQuitting) {
                dispose();
                return null;
            }
   
            if (pendingIdleHandlerCount < 0
                    && (mMessages == null || now < mMessages.when)) {
                pendingIdleHandlerCount = mIdleHandlers.size();
            }
            if (pendingIdleHandlerCount <= 0) {
                // No idle handlers to run.  Loop and wait some more.
                mBlocked = true;
                continue;
            }
            if (mPendingIdleHandlers == null) {
                mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandl
            }
            mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
        }
        for (int i = 0; i < pendingIdleHandlerCount; i++) {
            final IdleHandler idler = mPendingIdleHandlers[i];
            mPendingIdleHandlers[i] = null; // release the reference to the hand
            boolean keep = false;
            try {
                keep = idler.queueIdle();
            } catch (Throwable t) {
                Log.wtf(TAG, "IdleHandler threw exception", t);
            }
            if (!keep) {
                synchronized (this) {
                    mIdleHandlers.remove(idler);
                }
            }
        }
        // Reset the idle handler count to 0 so we do not run them again.
        pendingIdleHandlerCount = 0;
        // While calling an idle handler, a new message could have been delivere
        // so go back and look again for a pending message without waiting.
        nextPollTimeoutMillis = 0;
    }
}

---->[Handler#dispatchMessage]---------------
public void dispatchMessage(Message msg) {
    if (msg.callback != null) {//若是msg有回調
        handleCallback(msg);//處理msg的callback 
    } else {
        if (mCallback != null) {//這個上面舉例說明過,不說了
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);//回調覆寫的handleMessage方法
    }
}

---->[Handler#handleCallback]---------------
private static void handleCallback(Message message) {
    message.callback.run();
}
複製代碼

關於MessageQueue#next方法,無非就是讓消息出隊,沒有消息時next會一直阻塞
這裏涉及了native的方法,以及控制next的阻塞來完成延遲觸發,這裏native不展開,
想深刻的,這裏推薦一篇文章Android MessageQueue消息循環處理機制


5、Handler#postXXX與Message的callback

1.dispatchMessage方法分析

不知道你有沒有注意到,msg裏有的callback,爲了強調dispatchMessage,這裏再看一次

dispatchMessage的流程.png

---->[Handler#dispatchMessage]---------------
public void dispatchMessage(Message msg) {
    if (msg.callback != null) {//若是msg有回調
        handleCallback(msg);//處理msg的callback 
    } else {
        if (mCallback != null) {//這個上面舉例說明過,不說了
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);//回調覆寫的handleMessage方法
    }
}

---->[Handler#handleCallback]---------------
private static void handleCallback(Message message) {
    message.callback.run();
}
複製代碼

2.既然msg能夠添加一個Runnable的callback那試一下唄

不幸的事callback是包訪問級別的,沒有提供set方法,因此用不了
可是提供了一個obtain的重載,能夠放置callback

---->[Message#obtain(Handler, Runnable)]
public static Message obtain(Handler h, Runnable callback) {
    Message m = obtain();
    m.target = h;
    m.callback = callback;
    return m;
}
------------------------------------------------------
/**
 * 做者:張風捷特烈<br/>
 * 時間:2019/1/25/025:14:24<br/>
 * 郵箱:1981462002@qq.com<br/>
 * 說明:當msg自身有Runnable回調時
 */
public class HandlerMsgWithCbkActivity extends AppCompatActivity {
    private TextView msgTv;
    private Handler mHandler;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Handler.Callback callback = msg -> {
            msgTv.setText("回調的handleMessage");
            return true;
        };

        mHandler = new Handler(callback){
            @Override
            public void handleMessage(Message msg) {
                msgTv.setText("覆寫的handleMessage");

            }
        };

        setContentView(R.layout.ac_handler);
        msgTv = findViewById(R.id.id_tv_handler);

        msgTv.setOnClickListener(v -> {
            Thread thread = new Thread(() -> {

                Message msgObtain = Message.obtain(mHandler, new Runnable() {
                    @Override
                    public void run() {
                        msgTv.setText("Message + Runnable");
                    }
                });
                msgObtain.what = 0x02;
                msgObtain.obj = "張風捷特烈";
                mHandler.sendMessageDelayed(msgObtain, 3000);
            });
            thread.start();
        });
    }
}

|--運行結果以下,結合dispatchMessage方法分析圖,應該足以說明
複製代碼

jieguo.png


3.Handler的幾個postXXX方法

post(Runnable r).png

boolean post(Runnable r) post一個Runnable
boolean postAtTime(Runnable r, long uptimeMillis) 定時
boolean postAtTime(Runnable r, Object token, long uptimeMillis) 加token
boolean postDelayed(Runnable r, long delayMillis) 演示
boolean postAtFrontOfQueue(Runnable r) post到隊首

---->[Handler#post]-----------------
public final boolean post(Runnable r){
   return  sendMessageDelayed(getPostMessage(r), 0);
}

---->[Handler#getPostMessage]-----------------
private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;
    return m;
}

|-- 可見post也沒有多麼高大上,只是調用了sendMessageDelayed而已
|-- 其中的Message經過getPostMessage獲取,在getPostMessage中
|-- 經過Message.obtain()從消息池中取出一個消息,並添加callback而已
|-- 後面幾個方法差很少,只是給Message多添一點信息而已,Message理解了,就不成問題
|-- 這幾個方法至關於Handler給咱們封裝了一下,要完成上面的測試,能夠:

---->[HandlerMsgWithCbkActivity]-----------------
msgTv.setOnClickListener(v -> {
    Thread thread = new Thread(() -> {
        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                msgTv.setText("Message + Runnable");
            }
        }, 3000);
    });
    thread.start();
});

|-- 注意,使用postXXX,會讓Handler.callback和覆寫的handleMessage失效  
|-- 由於其本質上是經過Message的callback,前面已經講了Message.callback一旦存在  
|-- 是不會再走上二者的。
複製代碼

終曲、回看Handler:

這時候回頭看一下Handler背後的消息機制:

消息承載單體:Message  
消息管理器:MessageQueue
消息機制驅動力:Looper  
消息處理器:Handler

---->[爲告終尾點題,編個故事吧]---注:本故事純屬虛構-------------
傳說在100億年前,宇宙不止一個,人們生活的宇宙被稱爲main宇宙
除了main宇宙以外,稱爲子宇宙,因爲宇宙衆多,被人們稱爲三千宇宙
有些人試圖在子宇宙改變本身的樣子(包括服飾),開始新的生活,
但無一例外,所有灰飛煙滅,非main宇宙沒法改變容飾成了平行宇宙法則
但main宇宙中人員衆多,資源相對匱乏,不少人只能去子宇宙去獲取資源

一個叫TextView的女生想要一件漂亮的衣服,做爲男朋友的Handler義無反顧獨闖子宇宙,
他爆出了3件很是漂亮的衣服,知道讓TextView直接來到子宇宙穿上的話,她便會馬上灰飛煙滅  
因而他用3個空間立方(Message)將3件衣服分別裝入其中,並標識message的target是本身  
而後3個空間立方被依次放入了[平行宇宙傳送臺(MessageQueue)],
main宇宙中的強大Looper能源驅動着[平行宇宙傳送臺],自從三千宇宙誕生的那刻就開啓了(Looper.loop)  
當空間立方傳遞到主宇宙時,空間立方傳根據target找到本身的主人曾經的囑託(handleMessage) 
而後將三件美麗的服飾依次給TextView穿上,這樣就實現了子宇宙資源對宇宙的傳送  

有些常年在子宇宙工做的人,也常用這種機制,寄一封回家,傳達思念與祝福
而這些,就是當時他們的平常生活...
複製代碼
最後一個問題:Handler只是能在主宇宙和子宇宙間傳遞資源嗎?

每一個子宇宙均可以擁有僅屬於本身的一個Looper,子宇宙間也能夠經過Handler進行通訊

/**
 * 做者:張風捷特烈<br/>
 * 時間:2019/1/25/025:14:24<br/>
 * 郵箱:1981462002@qq.com<br/>
 * 說明:子線程間通訊
 */
public class HandlerOtherActivity extends AppCompatActivity {
    private TextView msgTv;
    private Handler mHandler;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.ac_handler);
        msgTv = findViewById(R.id.id_tv_handler);
        msgTv.setOnClickListener(v -> {
            new Thread("第一子宇宙") {//第一子宇宙
                @Override
                public void run() {
                    Message newMsg = new Message();
                    newMsg.what = 0x01;
                    newMsg.obj = "玉面奕星龍";
                    mHandler.sendMessage(newMsg);
                    Log.e("HandlerOtherActivity", "當前線程名稱: " + Thread.currentThread().getName() + " 發送:" + ne
                }
            }.start();
        });
        new Thread("第二子宇宙") {//第二子宇宙
            @Override
            public void run() {
                Looper.prepare();//讓當前子宇宙生成--looper能源
                //Handler經過第二子宇宙的looper能源能源構造
                mHandler = new Handler(msg -> {
                    Log.e("HandlerOtherActivity", "當前線程名稱: " + Thread.currentThread().getName() + " 接收:" + ms
                    return false;
                });
                Log.e("HandlerOtherActivity", "run: ");
                Looper.loop();//looper能源啓動--此時該線程會阻塞------下面的方法沒法執行
                Log.e("HandlerOtherActivity", "run:-------- ");
            }
        }.start();
    }
}

|-- 注意點:當第一線程要向第二線程傳遞數據,mHandler要擁有第二線程的looper
|-- 也就是讓Handler在第二線程中建立,下圖已經很明確讓的表達出:
|-- 第一線程向第二線程傳遞了一條信息,並且第二線程會阻塞在Looper.loop()是,下一句沒法打印
複製代碼

子線程經過handler通訊.png


爲了讓你對Looper有更深的認識,換一種寫法

下面的寫法若是你看懂了,handler機制就不在話下了
一句話:在main線程中使用線程2的looper建立Handler,在線程1中經過Handler發送消息
結果消息還是在線程2中執行的,看日誌並未改變:(只有looper在哪一個線程,消息就會發到哪一個線程)

子線程經過handler通訊.png

/**
 * 做者:張風捷特烈<br/>
 * 時間:2019/1/25/025:14:24<br/>
 * 郵箱:1981462002@qq.com<br/>
 * 說明:子線程間通訊
 */
public class HandlerOtherActivity extends AppCompatActivity {
    private TextView msgTv;
    private Handler mHandler;

    private Looper mOtherLooper;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.ac_handler);
        msgTv = findViewById(R.id.id_tv_handler);

        msgTv.setOnClickListener(v -> {

            new Thread("第一子宇宙") {//第一子宇宙
                @Override
                public void run() {
                    Message newMsg = new Message();
                    newMsg.what = 0x01;
                    newMsg.obj = "玉面奕星龍";
                    mHandler.sendMessage(newMsg);
                    Log.e("HandlerOtherActivity", "當前線程名稱: " + Thread.currentThread().getName() + " 發送:" + newMsg.obj);
                }
            }.start();
        });

        new Thread("第二子宇宙") {//第二子宇宙
            @Override
            public void run() {
                Looper.prepare();//讓當前子宇宙生成--looper能源
                mOtherLooper = Looper.myLooper();

                Log.e("HandlerOtherActivity", "run: ");
                Looper.loop();//looper能源啓動--此時該線程會阻塞------下面的方法沒法執行
                Log.e("HandlerOtherActivity", "run:-------- ");
            }
        }.start();

        try {
            Thread.sleep(10);//睡10ms讓mOtherLooper能夠初始化
//            Handler經過第二子宇宙的looper能源能源構造
            mHandler = new Handler(mOtherLooper, msg -> {
                Log.e("HandlerOtherActivity", "當前線程名稱: " + Thread.currentThread().getName() + " 接收:" + msg.obj);
                return false;
            });

        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}
複製代碼
相關文章
相關標籤/搜索