Android----Handler(HandlerA发送的消息
一.前言
在之前的两篇文章
中介绍了 Android 中的 Handler ,Looper 和 MessageQueue 的整体工作流程,其中可以知道对于一个 Looper ,可以有多个 Handler 与之绑定(通过 Hanlder 的构造器指定 Looper),那么就产生一个疑问,在同一个线程下,即针对绑定同一个 Looper 的 HanlderA 和 HanlderB ,Hanlder A 发送的消息可以让 HanlderB 接收吗?
答案是:可以的
二.消息的发送
在 Hanlder 消息机制发送消息时指定 Hanlder 主要有下面几种情况。
1.Hanlder 直接发送
Message message = Message.obtain();
mHandler1.sendMessage(message);
//mHandler1.sendMessageAtTime(message,0);
//mHandler1.sendEmptyMessage(0);
//mHandler1.sendEmptyMessageAtTime(0,0);
//mHandler1.sendEmptyMessageDelayed(0,0);
//mHandler1.sendMessageAtFrontOfQueue(message);
对于类似上面这种方式,从源码中可以看到其实最后都调用了同一个方法
enqueueMessage
public final boolean sendMessage(Message msg)
{
//调用下面的
return sendMessageDelayed(msg, 0);
}
public final boolean sendEmptyMessage(int what)
{ //调用下面的
return sendEmptyMessageDelayed(what, 0);
}
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
//调用下面的 sendMessageDelayed
return sendMessageDelayed(msg, delayMillis);
}
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
Message msg = Message.obtain();
msg.what = what;
//调用下面的 sendMessageAtTime
return sendMessageAtTime(msg, uptimeMillis);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
//也是调用 sendMessageAtTime
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;
}
//最后都是调用 enqueueMessage
return enqueueMessage(queue, msg, uptimeMillis);
}
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;
}
//这里也是调用 enqueueMessage
return enqueueMessage(queue, msg, 0);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
//注意这里的
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
//在 enqueueMessage 中调用 queue.enqueueMessage
return queue.enqueueMessage(msg, uptimeMillis);
}
从源码中可以看到,对于类是 Hanlder 直接发送的那种方式,最后都会调用 enqueueMessage 方法,而在 这个方法里 ** msg.target = this; ** 这段就是为 消息指定 Hanlder ,这个this ,就是说明 如果 HandlerA 发送了消息,那么 处理这个消息的就是 HanlderA, (这似乎和开头的答案不符,且先保留疑问,继续往下看。)
2.Message sendToTarget
//第一种,在 obtain 的时候指定 Handler。
Message message = Message.obtain(mHandler1);
//Message message = Message.obtain(mHandler1,....);
message.sendToTarget();
//或者 使用message.setTarget 指定。
Message message = Message.obtain();
message.setTarget(mHandler1);
message.sendToTarget();
在这种方式下,看 obtain 指定 Hanlder 的情况或者setTarget 的情况。
public static Message obtain(Handler h) {
Message m = obtain();
//注意这里
m.target = h;
return m;
}
public void setTarget(Handler target) {
this.target = target;
}
public void sendToTarget() {
target.sendMessage(this);
}
也是直接将 Hanlder 赋值给 Message 的 Target.
然后再 sendToTarget 中直接使用 Hander.sendMessage 发送这个消息,那么就回到了第一种情况。
从这两种方式可以看出,最后都是会调用 enqueueMessage 中的方法,并指定 HandlerA 发送 HanlderA 就接收。
可以从下面的例子验证
public class Main2Activity extends AppCompatActivity {
private static final String TAG = "Main2Activity";
private Handler1 mHandler1 = new Handler1();
private Hanlder2 mHanlder2 = new Hanlder2();
class Handler1 extends android.os.Handler {
@Override
public void handleMessage(Message msg) {
Log.e(TAG, msg.what + "Handler1+handleMessage: " + msg.getTarget());
}
}
class Hanlder2 extends Handler {
@Override
public void handleMessage(Message msg) {
Log.e(TAG, msg.what + "Hanlder2+handleMessage: " + msg.getTarget());
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
findViewById(R.id.btn_1).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
Message message = Message.obtain(mHandler1);
message.setTarget(mHanlder2);//这里只是将 target 换作 hander2 ,实质上还是 Hander2 发送
message.sendToTarget();
// Message message = Message.obtain();
// message.setTarget(mHandler1);
// message.sendToTarget();
}
}).start();
}
});
findViewById(R.id.btn_2).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Message message = Message.obtain();
message.what = 2;
message.setTarget(mHandler1);//这里没有用,因为下面发送的代码最后还是会重指定 为 handler2
mHanlder2.sendMessage(message);
}
});
}
}
//点击 Button1
01-01 17:16:54.440 19403-19403 E/Main2Activity: 0Hanlder2+handleMessage: Handler (.Main2Activity$Hanlder2) {ba26787}
//点击 Button2
01-01 17:17:22.303 19403-19403 E/Main2Activity: 2Hanlder2+handleMessage: Handler (.Main2Activity$Hanlder2) {ba26787}
从结果可以看出,不管之前如何设置 target 或则 obtain 方法参数,最后都是只要是 hander2 发送 就 Hander2 接收。
那么答案的可以是如何实现的?其实很简单
findViewById(R.id.btn_2).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Message message = Message.obtain();
message.what = 2;
//和上面的代码不一样的是这两段互换了位置
//先发送
mHanlder2.sendMessage(message);
//后指定 target
message.setTarget(mHandler1);
}
});
点击 Button2
01-01 17:23:03.157 20678-20678 E/Main2Activity: 2Handler1+handleMessage: Handler (.Main2Activity$Handler1) {b226bc6}
可以看到对于 Hander2 发送的消息,最后回到了 Handler1 这里去执行。
这是为什么?在 enqueueMessage 中最后是调用了 MessageQueue 的 enququMessage
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;
//mBlocked 为true 的时候是当消息队列中没有消息的时候
}
//唤醒 JNI 中的线程
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
我们知道在 Looper 循环获取消息的时候,都是从 MessageQueue 中的next 方法去获取一个消息
Message next() {
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//当没有消息的时候,这里就会阻塞
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 {
// No more messages.
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
//如果没有消息了 pendingIdleHandlerCount 就不会改动,为 -1
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
//那个就会执行这里
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
//Loop 陷入等待。
mBlocked = true;
continue;
}
}
}
从上面可以看出,当执行到 enqueueMessage 这里的时候,会先去执行 native 方法的唤醒操作,而这个时候,已经不是在 java 这里的主线程,因此主线程继续执行,所以就更改了 Message 的 Target ,
然后在分发的时候,因为 target 已经变了,所以就换到 Hander2
try {
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
这里的时间间隔其实很短,之后过一段时间再执行 setTarget 就会失效。
Message message = Message.obtain();
message.what = 2;
mHanlder2.sendMessage(message);
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
//这里就无效了
message.setTarget(mHandler1);
因此,其实 得出结论 HandlerA发送的消息HandlerB 能接收的。