一言沐、Handler用處
Handler有兩個主要用法:
- 計劃在將來某個時間點處理Message和Runnable
- 在不同線程里將一個動作加入Handler所對應的隊列去執(zhí)行
二、成員變量
Handler有4個不可變成員變量:消息隊列mQueue
漠其、消息隊列所屬mLooper
狠鸳、可選Handler回調(diào)mCallback
耸别、可選異步標志mAsynchronous
final MessageQueue mQueue;
final Looper mLooper;
final Callback mCallback;
final boolean mAsynchronous;
三涮因、構(gòu)造方法
如果線程已經(jīng)有Looper,那么Handler可以使用下面的構(gòu)造方法坷虑。
public Handler() {
this(null, false);
}
public Handler(Callback callback) {
this(callback, false);
}
// 不知道這個異步是不是和指令重排序有關(guān)
public Handler(boolean async) {
this(null, async);
}
public Handler(Callback callback, boolean async) {
// 判斷是否匿名類甲馋、本地類、成員類迄损,并判斷修飾符是否是static摔刁,不是打出就警告信息,避免內(nèi)存泄漏
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
// 主動獲取Handler所在線程的Looper
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;
}
帶Looper形參的構(gòu)造方法海蔽,傳入的Looper不能為空共屈。通常和Looper.getMainLooper()
合用。
public Handler(Looper looper) {
this(looper, null, false);
}
public Handler(Looper looper, Callback callback) {
this(looper, callback, false);
}
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
四党窜、把Runnable封裝成Message
這主要作用是把r
賦值給msg.callback
拗引,把token
賦值給m.obj
。因為下一個代碼塊就使用到這個方法幌衣,所以拿到前面先說矾削。
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
private static Message getPostMessage(Runnable r, Object token) {
Message m = Message.obtain();
m.obj = token;
m.callback = r;
return m;
}
五、Post和Send
注意下面4個方法:
- 前兩個方法封裝形參
Runnable
豁护,方法名組成是post()
- 后兩個方法形參是
msg
或msg.what
哼凯,方法名組成是sendMessage()
- 沒有方法形參既有
Runnable
,又有msg
- 方法名帶
Delayed
可設置延遲時間楚里,帶EmptyMessage
為創(chuàng)建空消息 - 這4個方法的共同點是都調(diào)用了
sendMessageDelayed()
断部,返回這個調(diào)用的結(jié)果
public final boolean post(Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0); //delayMillis = 0
}
// 時間單位毫秒,如:delayMillis = 1000
public final boolean postDelayed(Runnable r, long delayMillis) {
return sendMessageDelayed(getPostMessage(r), delayMillis);
}
public final boolean sendMessage(Message msg) {
return sendMessageDelayed(msg, 0);
}
// msg.what用16進制班缎,如:0x01
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
SystemClock.uptimeMillis()
是從開機到現(xiàn)在的毫秒數(shù)蝴光,不包括手機睡眠的時間。
postAtTime()
重載方法調(diào)用了sendMessageAtTime()
达址。
public final boolean postAtTime(Runnable r, long uptimeMillis){
return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}
public final boolean postAtTime(Runnable r, Object token, long uptimeMillis){
return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
}
sendEmptyMessage()
調(diào)sendEmptyMessageDelayed()
sendEmptyMessageDelayed()
又和sendEmptyMessageAtTime
最終調(diào)用sendMessageAtTime()
蔑祟。
public final boolean sendEmptyMessage(int what) {
return sendEmptyMessageDelayed(what, 0);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageAtTime(msg, uptimeMillis);
}
總而言之,上面所有post和send都終結(jié)在sendMessageAtTime()
沉唠,而sendMessageAtTime()
僅負責把消息送進消息隊列中疆虚,然后給一個具體執(zhí)行時間點梭冠。
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);
}
消息默認是放在消息隊列的隊尾處蓝丙。返回成功代表成功進入隊列,不代表消息會被調(diào)度荷愕。
一般情況下纱扭,消息隊列都會等待所有消息完成才退出牍帚。但如果手動關(guān)閉消息隊列,那滯留在消息隊列的消息不會得到處理乳蛾,然后消息被丟棄暗赶,這是進入消息隊列卻不一定能調(diào)度的主要原因鄙币。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
消息也可以放在消息隊列的對頭優(yōu)先執(zhí)行,不過這兩個方法只能在非常特殊的情況下采取用蹂随,因為順序問題和未知副作用很容易導致隊列后方消息的饑餓十嘿。
public final boolean postAtFrontOfQueue(Runnable r){
return sendMessageAtFrontOfQueue(getPostMessage(r));
}
public final boolean sendMessageAtFrontOfQueue(Message msg) {
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, 0);
}
六、調(diào)度和三種消息回調(diào)
6.1 消息調(diào)度
當消息到達預定執(zhí)行時間岳锁,消息所在的Looper就會調(diào)用msg.target.dispatchMessage(msg)
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
6.2 三種消息回調(diào)方式
(1) 首先dispatchMessage(msg)
嘗試執(zhí)行消息體的msg.callback
绩衷。不過由于上面有EmptyMessage
一類方法的存在,所以msg.callback
可能為空而不執(zhí)行激率。
private static void handleCallback(Message message) {
message.callback.run();
}
(2) msg.callback
不行就看看Handler自己有沒有mCallback
咳燕。
public interface Callback {
public boolean handleMessage(Message msg);
}
例子: 創(chuàng)建Handler時可以實現(xiàn)這個回調(diào),支持操作主線程UI
Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
return false;
Toast.makeText(Activity.this,"handleMessage override",Toast.LENGTH_SHORT).show();
}
});
(3) 如果上兩個回調(diào)都不存在乒躺,就只能寄托于我們自己重寫的方法招盲。舉個例子:
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch(msg.what){
case START_ACTIVITY:
Intent intent = new Intent(Activity.this, MainActivity.class);
Activity.this.startActivity(intent);
Activity.this.finish();
break;
case TOAST_SHORT_SHOW:
Toast.makeText(Activity.this, "Toast", Toast.LENGTH_SHORT).show();
break;
}
}
七、移除隊列消息
根據(jù)消息身份what
嘉冒、消息Runnable
或msg.obj
移除隊列中對應的消息曹货。調(diào)用那個要根據(jù)你選用什么而定。例如發(fā)送msg
讳推,就用同一個msg.what
作為參數(shù)顶籽。都調(diào)用MessageQueue.removeMessages
,具體在MessageQueue
的源碼閱讀里面說银觅。
public final void removeCallbacks(Runnable r) {
mQueue.removeMessages(this, r, null);
}
public final void removeCallbacks(Runnable r, Object token) {
mQueue.removeMessages(this, r, token);
}
public final void removeMessages(int what) {
mQueue.removeMessages(this, what, null);
}
public final void removeMessages(int what, Object object) {
mQueue.removeMessages(this, what, object);
}
public final void removeCallbacksAndMessages(Object token) {
mQueue.removeCallbacksAndMessages(this, token);
}
八礼饱、查找對應消息
上面是移除,這里是查看有沒有對應的消息
public final boolean hasMessages(int what) {
return mQueue.hasMessages(this, what, null);
}
public final boolean hasMessages(int what, Object object) {
return mQueue.hasMessages(this, what, object);
}
public final boolean hasCallbacks(Runnable r) {
return mQueue.hasMessages(this, r, null);
}
九设拟、阻塞非安全執(zhí)行
如果當前執(zhí)行線程是Handler的線程慨仿,Runnable會被立刻執(zhí)行久脯。否則把它放在消息隊列中一直等待執(zhí)行完畢或者超時纳胧。超時后這個任務還是在隊列中,在后面的某個時刻它仍然會執(zhí)行帘撰,很有可能造成死鎖跑慕,所以盡量不要用它。
這個方法使用場景是Android初始化一個WindowManagerService摧找,因為WindowManagerService不成功核行,其他組件就不允許繼續(xù),所以使用阻塞的方式直到完成蹬耘。
public final boolean runWithScissors(final Runnable r, long timeout) {
if (r == null) {
throw new IllegalArgumentException("runnable must not be null");
}
if (timeout < 0) {
throw new IllegalArgumentException("timeout must be non-negative");
}
if (Looper.myLooper() == mLooper) {
r.run();
return true;
}
// 一個阻塞的隊列
BlockingRunnable br = new BlockingRunnable(r);
return br.postAndWait(this, timeout);
}
IMessenger mMessenger; // IPC
private static final class BlockingRunnable implements Runnable {
private final Runnable mTask;
private boolean mDone;
public BlockingRunnable(Runnable task) {
mTask = task;
}
@Override
public void run() {
try {
mTask.run();
} finally {
synchronized (this) {
mDone = true;
notifyAll();
}
}
}
public boolean postAndWait(Handler handler, long timeout) {
if (!handler.post(this)) {
return false;
}
synchronized (this) {
if (timeout > 0) {
final long expirationTime = SystemClock.uptimeMillis() + timeout;
while (!mDone) {
long delay = expirationTime - SystemClock.uptimeMillis();
if (delay <= 0) {
return false; // timeout
}
try {
wait(delay);
} catch (InterruptedException ex) {
}
}
} else {
while (!mDone) {
try {
wait();
} catch (InterruptedException ex) {
}
}
}
}
return true;
}
}
十芝雪、獲取消息名
獲取消息里Handler的類名,或消息msg.what的16進制值
public String getMessageName(Message message) {
if (message.callback != null) {
return message.callback.getClass().getName();
}
return "0x" + Integer.toHexString(message.what);
}
獲取空消息體的重載方法
public final Message obtainMessage() {
return Message.obtain(this);
}
public final Message obtainMessage(int what) {
return Message.obtain(this, what);
}
public final Message obtainMessage(int what, Object obj) {
return Message.obtain(this, what, obj);
}
public final Message obtainMessage(int what, int arg1, int arg2) {
return Message.obtain(this, what, arg1, arg2);
}
public final Message obtainMessage(int what, int arg1, int arg2, Object obj) {
return Message.obtain(this, what, arg1, arg2, obj);
}
十一综苔、其他
剩下這個方法關(guān)于跨進程通訊的Messager惩系,在AIDL中使用位岔。
private final class MessengerImpl extends IMessenger.Stub {
public void send(Message msg) {
msg.sendingUid = Binder.getCallingUid();
Handler.this.sendMessage(msg);
}
}