我的全部文章整理在此篇,將陸續更新收錄:知無涯,行者之路莫言終(個人編程之路)java
哥發誓,這是我最後一次分析Handler(這是我第三次說這句話,但願不會有下次了)
提及Handler新手的小噩夢,用起來不難,理解起來煩神
android
你覺得我會這麼俗氣地從:
非主線程禁止更新UI
開始說Handler嗎?--是的
這個異常定義在ViewRootImpl
裏,更新TextView的UI爲何ViewRootImpl報異常?
子不教,父之過,教不嚴,師之惰唄。在framework的源碼裏看一下吧,編程
---->[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();
}
}
複製代碼
而後發現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();
});
}
}
複製代碼
本身翻譯的,僅供參考,有不恰之處敬請指出
其中提到了MessageQueue
和message
以及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).
複製代碼
final Looper mLooper;
final MessageQueue mQueue;
final Callback mCallback;
final boolean mAsynchronous;
IMessenger mMessenger;
複製代碼
看這七個葫蘆娃,核心就只有兩個構造方法(三個hide的,外面不能用),其餘四個能夠用async
|--對具備指定回調接口的[當前線程]使用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類
複製代碼
花了點時間看了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());
}
}
複製代碼
能夠看出,在新線程中對sThreadLocal.set(),並不會影響main線程的get()
因此共享成員變量count放在sThreadLocal這個籃子裏,能夠保證線程間共享變量的獨立函數
looper:/'luːpə/ 循環者
)類名:Looper 父類:Object 修飾:public final
實現的接口:[]
包名:android.os 依賴類個數:7
內部類/接口個數:0
源碼行數:344 源碼行數(除註釋):158
屬性個數:8 方法個數:20 public方法個數:18
複製代碼
---->[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
//公共靜態方法:準備
---->[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)的呢?
複製代碼
到framework層的源碼去看一下,程序的入口main方法在ActivityThread中post
---->[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的做用
之前是直接在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();
});
}
}
複製代碼
/**
* 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);
}
}
複製代碼
感受很不是很經常使用,瞭解一下便可,
下面代碼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();
});
}
}
複製代碼
如今將建立Handler放在子線程進行,不出所料,崩了
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()爲空,因此會崩掉
複製代碼
Handler的構造函數中有Looper參數,咱們能夠在外面獲取主線程的Looper對象,給Handler構造
除此以外Context也給咱們提供了獲取main線程Looper的方法,debug能夠看出,二者是同一對象
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();
});
}
}
複製代碼
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();
});
}
}
複製代碼
一連串的
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#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方法
}
複製代碼
如今咱們面前出現了兩個類:
Message
和MessageQueue
Message的成員變量有點多
what----int 型,用來表識該信息
arg1----int型,儲存簡單的int數據
arg2----int型,儲存簡單的int數據
obj --- 任意類型,儲存任意數據
replyTo ---- Messenger類型 可選的信使,在那裏回覆這條消息能夠發送。具體如何使用它的語義取決於發送方和接收方。
複製代碼
先從一個例子開始引入Message吧
new Message()
與Message.obtain()
與Handler.obtain()
可見三種方式都能運做,那麼有什麼區別呢?
/**
* 做者:張風捷特烈<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();
});
}
}
複製代碼
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,
|-- 同時再爲其設置一些屬性,本質上也沒有什麼太大的區別,本身看一下就好了
複製代碼
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;
}
>看文字估計挺暈的,以上面三個消息爲例,分析一下他們加入的流程
複製代碼
當第二條消息入隊時: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
|-- 因而可知,每添加一條消息都是添加到隊尾
複製代碼
將消息加入隊列以後是如何取出的?又如何觸發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消息循環處理機制
Handler#postXXX
與Message的callback
不知道你有沒有注意到,msg裏有的callback,爲了強調dispatchMessage,這裏再看一次
---->[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();
}
複製代碼
不幸的事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方法分析圖,應該足以說明
複製代碼
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背後的消息機制:
消息承載單體: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穿上,這樣就實現了子宇宙資源對宇宙的傳送
有些常年在子宇宙工做的人,也常用這種機制,寄一封回家,傳達思念與祝福
而這些,就是當時他們的平常生活...
複製代碼
每一個子宇宙均可以擁有僅屬於本身的
一個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機制就不在話下了
一句話:在main線程中使用線程2的looper建立Handler,在線程1中經過Handler發送消息
結果消息還是在線程2中執行的,看日誌並未改變:(只有looper在哪一個線程,消息就會發到哪一個線程)
/**
* 做者:張風捷特烈<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();
}
}
}
複製代碼