handler訊息機制原始碼級深入全解析
首先我們來看看Handler更新UI執行緒一般使用
首先要進行Handler 申明,複寫handleMessage方法( 放在主執行緒中)
private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { // TODO 接收訊息並且去更新UI執行緒上的控制元件內容 if (msg.what == UPDATE) { // 更新介面上的textview tv.setText(String.valueOf(msg.obj)); } super.handleMessage(msg); } };
子執行緒傳送Message給ui執行緒表示自己任務已經執行完成,主執行緒可以做相應的操作了。
new Thread() { @Override public void run() { // TODO 子執行緒中通過handler傳送訊息給handler接收,由handler去更新TextView的值 try { //do something Message msg = new Message(); msg.what = UPDATE; msg.obj = "更新後的值" ; handler.sendMessage(msg); } } catch (InterruptedException e) { e.printStackTrace(); } } }.start();
我們通過畫圖來了大體解一下handler的訊息機制
先看左側我們通過handler來發訊息,這個實在子執行緒中的。handler可以建立多個,假設我們建立兩個handler為handler1和handler2.每個handler都可以向訊息佇列發訊息。handler1傳送訊息後到訊息佇列,假設訊息為msg1.handler2也會發送訊息排在訊息佇列的msg。但是訊息佇列是唯一的,訊息佇列會按照時間排序。訊息佇列依次從訊息佇列取訊息。之後通過輪詢器來取出訊息,輪詢器實在主執行緒中的。每個訊息通過target屬性來找到自己的訊息。然後再呼叫handleMessage()方法中處理結果。
下面我們通過四個類來將handler訊息機制弄清楚。Message,MesageQueue,Handler,Looper。
Message
1, new Message()
2, Message.obtain();
3, handler.obtainMessage();
MesageQueue
存放Message的容器,單鏈表
預設:msg的when都是0 後加的放到最前面
如果when不等於0 按when的值大小進入排序:when的值小的在佇列的前面
Handler
訊息處理類
handleMessage(Message msg)方法,覆蓋此方法,做ui的處理
Looper
輪詢器 : 輪詢取訊息,linux底層管道通訊
Message類
**//代表message值是什麼**
public int what;
public int arg1;
public int arg2;
**//如果資訊較多用Obj來作為資訊的載體。**
public Object obj;
public Messenger replyTo;
**// /*package*/為私有的訪問許可權並不對外提供。**
/*package*/ static final int FLAG_ASYNCHRONOUS = 1 << 1;
/*package*/ static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAG_IN_USE;
/*package*/ int flags;
**//發訊息的時間,預設情況下是0。寫1000即為等待1000毫秒中再發**
/*package*/ long when;
/*package*/ Bundle data;
**//target也就是用來區分message的**
/*package*/ Handler target;
**//一個回撥介面**
/*package*/ Runnable callback;
**//next其實就是一個單鏈表**
/*package*/ Message next;
我們來看一下Message.obtain();中程式碼
//為了滿足語法來new Object();
private static final Object sPoolSync = new Object();
private static Message sPool;
public static Message obtain() {
synchronized (sPoolSync) {
//sPool是一個訊息池,上面定義了,也就還是一個訊息。
if (sPool != null) {
//從訊息池中取訊息。
Message m = sPool;
//然後再指向下一個。
sPool = m.next;
m.next = null;
sPoolSize--;
return m;
}
}
//如果訊息池中沒有訊息也就是直接建立了一個新的訊息。
return new Message();
}
我們再來看一下clearForRecycle()這個方法。
//這個方法將所有的屬性進行了清零。
void clearForRecycle() {
flags = 0;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
when = 0;
target = null;
callback = null;
data = null;
}
這也就是我們在如下程式碼中常犯的錯誤的解釋:
//將target=null。msg.what也就不知道到底是哪個target了。發訊息也就會報錯了。
Message msg = Message.obtain();
msg.what = 1;
msg.what = 2;
為什麼會呼叫clearForRecycle()這個方法呢?我們看一下下面的方法。也就是在recycle()這個方法中呼叫了clearForRecycle()方法。
public void recycle() {
clearForRecycle();
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
//將靜態變數sPool賦值給了next
next = sPool;
//這個又指向了sPool
sPool = this;
sPoolSize++;
}
}
}
通過上面的方法中的兩句、
//將靜態變數sPool賦值給了next
next = sPool;
//這個又指向了sPool
sPool = this;
並且結合obtain()中的
sPool = m.next;
m.next = null;
我們就可以將兩個訊息重用為一個訊息了。綜上 Message類中的方法都跟這個Obtain()方法一樣。將訊息重用。
ActivityThread執行main();
//1,
Looper.prepareMainLooper();
// 2,
public static final void prepareMainLooper() {
//3
prepare();
setMainLooper(myLooper());
if (Process.supportsProcesses()) {
myLooper().mQueue.mQuitAllowed = false;
}
}
// 4
public static final void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//5,,呼叫的時候Looper.myLooper()-->
sThreadLocal.set(new Looper());
}
// 6,在handler的預設建構函式中去呼叫取值
public static final Looper myLooper() {
return (Looper)sThreadLocal.get();
}
Handler()的建構函式
public Handler() {
//輪詢器的初始化,主執行緒
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
// 從輪詢器中獲取訊息佇列
mQueue = mLooper.mQueue;
//回撥初始化為null
mCallback = null;
}
通過上面我們需要看兩個程式碼
1, Looper.myLooper();
public static final Looper myLooper() {
//從sThreadLocal取值 ,賦值看 上面##### ActivityThread執行main()
//ThreadLocal 執行緒變數。開啟多個執行緒的時候,為每個執行緒設定一個值。當前執行緒再去取值的時候,他會取出自己執行緒的值。而不會取到別的執行緒設定的值。
return (Looper)sThreadLocal.get();
}
2, mQueue = mLooper.mQueue;//獲取輪詢器中的訊息佇列
Handler傳送Message
1,handler.sendMessage(msg);//傳送訊息
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
2,sendMessageDelayed(msg, 0);
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
3,sendMessageAtTime()
public boolean sendMessageAtTime(Message msg, long uptimeMillis)
{
boolean sent = false;
//獲取訊息佇列,mQueue的賦值請看Handler的預設建構函式
MessageQueue queue = mQueue;
if (queue != null) {
//把自己的handler和Message繫結,this就是Handler
msg.target = this;
//把msg放到訊息佇列並且完成排序(按時間和順序)
sent = queue.enqueueMessage(msg, uptimeMillis);
}
else {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
}
return sent;
}
4,sent = queue.enqueueMessage(msg, uptimeMillis);
訊息佇列裡enqueueMessage方法,
1,放訊息
2,排序(按訊息的時間排序,如果時間一樣,後加的在前面
final boolean enqueueMessage(Message msg, long when) {
if (msg.when != 0) {
throw new AndroidRuntimeException(msg
+ " This message is already in use.");
}
if (msg.target == null && !mQuitAllowed) {
throw new RuntimeException("Main thread not allowed to quit");
}
final boolean needWake;
synchronized (this) {
if (mQuiting) {
RuntimeException e = new RuntimeException(
msg.target + " sending message to a Handler on a dead thread");
Log.w("MessageQueue", e.getMessage(), e);
return false;
} else if (msg.target == null) {
mQuiting = true;
}
msg.when = when;
//Log.d("MessageQueue", "Enqueing: " + msg);
Message p = mMessages;
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
needWake = mBlocked; // new head, might need to wake up
} else {
Message prev = null;
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
msg.next = prev.next;
prev.next = msg;
needWake = false; // still waiting on head, no need to wake up
}
}
if (needWake) {
nativeWake(mPtr);
}
return true;
}
Looper取訊息
在應用程式啟動之前
ActivityThread的main已經執行好,並且開始等待接收訊息
呼叫 Looper.loop();//輪詢取訊息
1,Looper的loop方法
while (true) {
//訊息佇列
Message msg = queue.next(); // might block
//if (!me.mRun) {
// break;
//}
if (msg != null) {
//找不到對應的handler
if (msg.target == null) {
// No target is a magic identifier for the quit message.
return;
}
if (me.mLogging!= null) me.mLogging.println(
">>>>> Dispatching to " + msg.target + " "
+ msg.callback + ": " + msg.what
);
//處理訊息
msg.target.dispatchMessage(msg);
if (me.mLogging!= null) me.mLogging.println(
"<<<<< Finished to " + msg.target + " "
+ msg.callback);
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf("Looper", "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
//4,
msg.recycle();
}
}
}
2, msg.target.dispatchMessage(msg);
呼叫Handler的dispatchMessage分發訊息
public void dispatchMessage(Message msg) {
if (msg.callback != null) {// runOnUiThread
//2.5
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//3handleMessage
handleMessage(msg);
}
}
2.5 handleCallback(msg); 使用者呼叫了runOnUiThread方法呼叫
a,
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
b,
mHandler.post(action);
public final boolean post(Runnable r)
{
c,
return sendMessageDelayed(getPostMessage(r), 0);
}
d,
private final Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
e,
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
//把handler發出去 msg.callback= Runnable
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
//////////上面abcde是通過runOnUiThread方法傳送訊息的邏輯
取訊息
handleCallback(msg);
private final void handleCallback(Message message) {
message.callback.run();//runOnUiThread裡寫的功能,在主執行緒執行
}
3, 在主執行緒中呼叫Handler的handleMessage(msg)使用者覆蓋的方法處理訊息
處理ui相關的操作
4, msg.recycle();
msg的回收
public void recycle() {
synchronized (mPoolSync) {
if (mPoolSize < MAX_POOL_SIZE) {
clearForRecycle();
next = mPool;
mPool = this;
}
}
}