- 什么是异步消息处理线程
异步消息处理线程启动会进入一个无限的循环体之中,每循环一次,从其内部的消息队列中会取出一个消息,然后回调给相应的消息处理函数,执行完成一个消息后则继续循环。若消息队列为空,线程则会阻塞等待。
这个消息处理过程有哪些角色
- 无限的循环体
- 内部的消息队列
- 相应的消息处理函数
Looper
对于 Looper这个角色,主要是 prepare() 和 loop( )两个方法。
public static final void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(true));
}
sThreadLocal 是一个 ThreadLocal对象,可以在一个线程中存储变量。在最后面进行了 Looper 实例的保存。
在 prepare() 方法中,首先进行了 Looper 对象的判空处理,如果不为空(之前已经有了一个 Looper 实例),就会抛出异常,说明了 prepare 不能被调用两次,同时也保证了 一个线程里面只能有一个 Looper 实例。
那 new Looper(true)
里面做了什么呢?
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mRun = true;
mThread = Thread.currentThread();
}
在 Looper 的构造方法中,有新增一个 MessageQuene 实例,这个是一个消息队列。
然后看看第二个主要方法 loop()
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
msg.target.dispatchMessage(msg);
if (logging != null) {
logging.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(TAG, "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);
}
msg.recycle();
}
}
还有一个 myLooper() 方法
public static Looper myLooper() {
return sThreadLocal.get();
}
从 threadLocal 里面去的 looper 实例,如果为空则抛出异常,所以 loop() 方法的调用一定要在 prepare() 之后。
后续的大概意思就是 异步消息处理线程的流程:进入一个无限的循环体for(;;)
之中,每循环一次,从其内部的消息队列中final MessageQueue queue = me.mQueue;
会取出一个消息Message msg = queue.next();
,然后回调给相应的消息处理函数msg.target.dispatchMessage(msg);
,执行完成一个消息后则继续循环。若消息队列为空,线程则会阻塞等待。
Looper的主要作用就是:
- prepar() 与当前线程绑定,保证一个线程只有一个 Looper 实例,同时一个 Looper实例也只有一个 MessageQuene
- loop() 消息循环处理机制
理清一下异步消息处理流程的角色 出场了哪些?
- 无限的循环体
- 内部的消息队列
- 相应的消息处理函数
前两个都清楚来路,还剩一个 msg.target 从哪里来?要到哪里去?贫僧从东土大唐而来,去往西天拜佛求经
于是:Handler 登场。
一般 Activity 中使用 Hander 有两种方式
####先说第一种handler方式:
private Handler mHandler = new Handler()
{
public void handleMessage(android.os.Message msg)
{
switch (msg.what)
{
case value:
break;
default:
break;
}
};
};
需要看两个方法:Handler() 的构造函数干了什么?handleMessage 是谁通过什么方式调用的?
- Handler 是怎么与 消息队列(MessageQueue)联系上的?他在子线程发送的消息是怎么发送到 消息队列(MessageQueue)的?
public Handler() {
this(null, false);
}
public Handler(Callback callback, boolean async) {
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());
}
}
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.myLooper 获得 当前线程的 Looper 实例如果没有,则无法创建 Handler
在 非UI线程 创建 Handler 的时候 通常都会 抛出异常:Can’t create handler inside thread that has not called Looper.prepare()。
这里有两个知识点:
- UI 线程 (Activity)并没有显式地调用 Looper.prepare() 和 Looper.loop() 方法,是因为在 启动代码中,已经在当前UI线程中调用的 这两个方法,所以直接在 UI线程创建 Handler 时 Looper.myLooper()是会返回当前线程(UI线程)的唯一 Looper实例的。
- 在 非UI 线程中创建 Handler 时,应该要进行 Looper方法的显式调用
通过得到的 Looper 实例,进一步获取 Looper 的 MessageQueue(消息队列),这样Handler 就得到了 MessageQueue 的联系方式。接下来就是 联系MessageQueue 的具体做法了打电话还是wechat?
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
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;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
终于走到了能看到MessageQueue的地方 sendMessageAtTime()
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
msg.target 很眼熟,不玩初心才行,这上面一大串转接就是 为了找到 msg.target 的来路。
终于找到了,this!!!是谁?~~这个属于java的基础知识
public static void loop() {
...
for (;;) {
...
msg.target.dispatchMessage(msg);
...
}
}
这句就是在做 消息处理 这件事情,至此,案情水落石出,真相只有一个犯人target 就是 handler
wait a second. 还有一个疑点。 handler处理的消息是从哪里来的?
queue.enqueueMessage(msg, uptimeMillis);
这么明显,handler 发送的消息被送到 MessageQueue 消息队列里面的,然后交给 target 的 dispatchMessage 去处理了。看一看 dispatchMessage
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) {
}
这是个空方法。
msg.callback 是什么?我发送的消息只有 msg 跟 what ,并没有callback啊,所以一般都是 null 的,else 必然会调用的方法是 handleMessage(msg),这个也很眼熟,不就是 new Handler() 之后需要重写的 消息处理函数吗?
到这里就比较明白了。
总结陈词:
- Looper 会在 当前线程中 保存唯一一个 Looper 实例,然后唯一的Looper 实例会在 Looper.prepare() 方法新建一个 同样在一个线程里唯一的 MessageQueue消息队列。
- Looper.loop() 会让当前线程进入一个无限循环,然后不断地从MessageQueue 的实例中读取消息,然后回调 msg.target.dispatchMessage(msg)方法。
- Handler 构造方法中通过当前线程的 Looper 实例,从而得到 MessageQueue消息队列的联系方式
- Handler 通过 sendMessage 方法 发送消息 Message 给 MessageQueue,并且在 Message 中标明自己的身份 msg.target = this, 从而在联系MessageQueue 的过程中暴露自己的身份,完成后续的消息处理。
- 构造 Handler 实例时,重写 handleMessage 方法,也就是 msg.targte.dispatchMessage 最终调用的方法。
- 至此,整个 Looper 与 handler 的 联系过程完成。
####再说第二种handler方式
mHandler.post(new Runnable()
{
@Override
public void run()
{
Log.e("TAG", Thread.currentThread().getName());
tv_text.setText("message received.");
}
});
需要看两个方法 handler.post 干了什么? 谁调用了 run 方法?
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
在上面方法中,看到了 message 的 callback 被赋值了,之前在 msg.target.dispatchMessage() 方法中有见到。
Tips: 产生一个Message对象,可以new ,也可以使用Message.obtain()方法;两者都可以,但是更建议使用obtain方法,因为Message内部维护了一个Message池用于Message的复用,避免使用new 重新分配内存
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
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;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
又回到了这里 sendMessageAtTime() 跟 handler.sendMessage 殊途同归。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
不过,这次的 callback 可不为null 了,所以会走 handlerCallbace(msg) 分支
private static void handleCallback(Message message) {
message.callback.run();
}
也就是调用 Runnable 的 run 方法。
关于 Looper , Handler, Message 的关系已经很清楚了。
Handler 不仅可以更新UI, 我们完全可以在一个子线程中去创建一个 Handler,然后使用这个handler实例在任何其他线程中发送消息,最终处理消息的代码都会在创建 Handler 实例的线程中执行。
####想到一个问题
- 一个 Activity 可以有多个 handler吗*