Android开发Handler、Looper、MessageQueue是啥玩意呀?

Android开发Handler、Looper、MessageQueue是啥玩意呀?
我们常用Handler来干嘛呢?
我们在子线程中请求到数据以后,把数据给到主线程,对吧。
所以我们有
- 先new一个Handler
- 覆写一个handlerMessage方法处理消息
- 子线程发送消息
我们也用来执行一些runnable,比如说可以用于安全地更新UI
你就可以mHandler.post(更新UI的任务)
我们也可以做一些无限轮播,loading转圈圈是吧
这些都可以post runnable来完成。
下面我们来看看handler的源码吧。
先从文档开始吧
Handler是什么
官方的说话
- A Handler allows you to send and process {@link Message} and Runnable
- objects associated with a thread's {@link MessageQueue}. Each Handler
- instance is associated with a single thread and that thread's message
- queue. When you create a new Handler, it is bound to the thread /
- message queue of the thread that is creating it -- from that point on,
- it will deliver messages and runnables to that message queue and execute
- them as they come out of the message queue.
Handler它可以通过消息队列发送消息或者runable对象,也可以处理消息或者runable对象。每一个handler实例,都持有一个线程以及此线程的消息队列。当你创建一个新的halder,绑定到一个线程上,那么这个线程的消息队列就会创建。从这里开始,handler就会发送消息或者runnable到消息队列里。当消息或者runnalbe从队列中取出时,handler就会执行这些runnable或者处理这些消息。
那这样就好理解了。
Handler就像一个银行,有人存消息,有人取消息。
Handler有什么用
There are two main uses for a Handler: (1) to schedule messages and runnables to be executed at some point in the future; and (2) to enqueue an action to be performed on a different thread than your own.
handler有两个用处
- 在某些功能上我们需要计划任务,也就是通过handler我们可以定时地去执行一些任务;
- 在不同的线程间执行任务
计划任务
你可以通过post方法来执行一个runable,也可以通过postAtTime(Runnable task,Long time),当time的时候,就执行这个任务,还可以等待一定的时间执行同某个任务postDelayed,也就是延时执行。
如果是消息的话,还有这些方法
- sendEmptyMessage发送空消息
- sendMessageAtTime 某个时间发送消息
- sendMessageDelayed 延时发送消息
如何处理消息呢?
我们前面说了,在使用的时候,我们实现HandlerMessage方法
对吧
所以就这样
mHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message msg) {
//处理消息
if(msg.what == 1) {
Log.d("test","object is -- > " + msg.obj);
}
return false;
}
});
这里我们就可以处理消息了
这个what可以用作标识
判断这个消息是谁发的,怎么处理。
消息发送
Message message = mHandler.obtainMessage();
message.what = 1;
message.obj = "我是一条消息...";
mHandler.sendMessage(message);
这样子一发,前面的handlerMessage就会收到了。
发送空消息
定一个what就可以了。
前面处理消息的地方,收到消息后,判断what进行对应的逻辑处理。
发送消息的源码
从sendMessage进去,我们可以看到
public final boolean sendMessage(@NonNull Message msg) {
return sendMessageDelayed(msg, 0);
}
它调用了
sendMessageDelayed
第二个参数为0,也就是不延时执行嘛。马上发送消息。
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
这里做了一个安全判断
往下就是这里了
public boolean sendMessageAtTime(@NonNull 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);
}
这里面大家要注意的是这个queue的判断
MessageQueue queue = mQueue;
然后判空,也就是说,我们得知道一下,这个mQueue是什么时候创建的
因为我们一创建Handler就使用这玩意了。虽然前面英文文档里有说到,绑定线程时会创建。我们还是看看什么时候创建的吧。
所以同学们看到这句话的时候
sendMessageAtTime() called with no mQueue
就知道是消息对象没有创建
mQueue创建/赋值的地方有两个
public Handler(@Nullable 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 " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
另外一个
@UnsupportedAppUsage
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
这两个都是构造方法
而且都是@hide注解的,也就是外部无法直接调用创建的。
那么,我们就看Looper从何而来
这个时候,我们逆回去看我们的创建了
public Handler(@Nullable Callback callback) {
this(callback, false);
}
我们是从这个进来的
也就是这个
public Handler(@Nullable 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 " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
mLooper = Looper.myLooper();
我们的looper是从Looper.mylooper里拿到的
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
这是一个 ThreadLocal
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
这个集合有什么特点呢?
它的key其实就是线程,它会根据线程去拿对应的内容。
存放的地方只有一个
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
我们并没有调用,哪到底是谁调用的呢?
/**
* Initialize the current thread as a looper, marking it as an
* application's main looper. The main looper for your application
* is created by the Android environment, so you should never need
* to call this function yourself. See also: {@link #prepare()}
*/
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
这里就有说明了
在ActivityThread里调用了,所以我们直接new的Handler,拿到的是主线程的消息队列。
重点:
如果不是主线程,我们后面再说其他的创建方式
我们继续往下看消息发送吧
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
到这里就进队列了。
进队列调用了queue.enqueueMessage(msg, uptimeMillis);这个方法
这个方法做了件重要的事情,就是对消息进行排序。
队列里的消息进行排序
如何实现消息延时发送呢?就是通过这个排序和取消息的判断来实现的。
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (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;
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;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
代码不是很多,前面部分就是判断,这些判断一定过的,因为在创建的时候已经准备好了。除非是非法使用。
后面则是排序,消息队列是一个单链表数据结构,通过修改指向就可以完成排序。
那消息如何处理呢?
消息处理源码
前面我们发送了消息,消息怎么样走到我们覆写的handler方法的呢?
其实前面我们已经把源码扒得差不多了
在我们的ActivityThread里,有这么一个入口
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
......
Looper.prepareMainLooper();
......
Looper.loop();
......
}
这里面做了两件事情
prepareMainLooper();
它的内容是什么呢?
/**
* Initialize the current thread as a looper, marking it as an
* application's main looper. The main looper for your application
* is created by the Android environment, so you should never need
* to call this function yourself. See also: {@link #prepare()}
*/
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
其实就是确保looper的创建和保存到ThreadLocal里
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
前面我们说了,ThreadLocal其实就是一个以线程为key的集合,哪个线程去拿数据就会拿到对应线程保存的内容。比如说我们此处代码是主线线程,当主线程去拿数据的时候,通过get方法,都不用传key的,直接拿到保存进去的looper
创建完Looper以后,保存到了ThreadLocal里,接着就调用了
Looper.loop();
这个方法
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {
final Looper me = myLooper();
......
//开始轮询,这个轮询可能阻塞
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
......
long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
try {
msg.target.dispatchMessage(msg);
if (observer != null) {
observer.messageDispatched(token, msg);
}
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} catch (Exception exception) {
if (observer != null) {
observer.dispatchingThrewException(token, msg, exception);
}
throw exception;
} finally {
ThreadLocalWorkSource.restore(origWorkSource);
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
......
msg.recycleUnchecked();
}
}
拿队列里拿出消息来,然后进行分发
分发就一个点
msg.target.dispatchMessage(msg);
这个target不就是handler吗?
在消息封装的时候,就是已经设置了吗?
也就是说,不管我们使用哪种方式创建/获取Message,它的target都是对应的handler
- 新创建
Handler handler = new Handler();
Message msg = new Message();
msg.what = 1;
msg.obj = "haha...";
handler.sendMessage(msg);
这种创建方式,当我们调用
handler.sendMessage(msg);
的时候
就会有这么一断代码
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
这里的msg.target = this,this不就是handler吗?
- 通过obtain获取的消息
Message message = handler.obtainMessage();
从队列里拿出来,传了this进去,就是传了当前的Handler进去
/**
* Returns a new {@link android.os.Message Message} from the global message pool. More efficient than
* creating and allocating new instances. The retrieved message has its handler set to this instance (Message.target == this).
* If you don't want that facility, just call Message.obtain() instead.
*/
@NonNull
public final Message obtainMessage()
{
return Message.obtain(this);
}
你看,设置了target是
public static Message obtain(Handler h) {
Message m = obtain();
m.target = h;
return m;
}
okay到此,我们只是为了说明target是handler
那么
msg.target.dispatchMessage(msg);
/**
* Handle system messages here.
*/
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
不就调用到了我们最开始覆写Handler的handleMessage了么?
当然,还有一个
if (msg.callback != null) {
handleCallback(msg);
}
如果这个case,就会调用callback
其实,这个前面最开始也提到了,handler除了可以发送消息,还可以postRunnable
对吧,比如说我们在Activity里调用的RunOnUiThread方法,比如说我们的View里有postRunnable方法
都是走这个实现的。
Handler、Looper、MessageQueue,Message到底是什么玩意?
到这里我们功能随便看了一下,这些东西我们也知道了。
- MessageQueue和Looper
当我们的App运行,ActivityThread是App的入口,在Main方法里,它做了两个事情
第一个是创建Looper,也就是轮训器
Looper创建的同时,创建了一个消息队列MessageQueue
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
接着开始轮询
Looper.loop();
okay,到此准备工作完成,注意的是:轮询时可能会被阻塞。这个阻塞不是Java的线程阻塞,是系统的中断。
private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/
private native static void nativeWake(long ptr);
知道这些前期工作就够了:创建---->轮询
- Handler和Message
前面是准备工作,到这里消息和Handler就到了 程序员的使用阶段了
Message是载体,包含着target,obj,args...
Handler是工厂
当我们发送消息的时候
- 创建消息
- 通过handler发送
- handler把消息放到消息队列里,并且排序,有必要时唤醒轮训器
- 轮训器从消息队列里取消息
- 轮训期取出消息分发到handler的处理方法里
假设我们是从子线程发送消息,怎么执行就在主线程了呢?
我们默认的Handler创建不指定线程的话,拿是是主线程的Looper。
我们发送消息在子线程,发送到队列里,但是处理消息在主线程。
这个拿消息是拿最前面的,虽然说是一个消息队列,但是类似于栈,排序后:在后面的先出队列。
RunOnUiThread和view.post
这两个方法相信大家都很熟悉,它是怎么实现的呢?
知道了前面的源码,我们就可以去稍加分析一下了
runOnUiThread
runOnUiThread是Activity里的方法
/**
* Runs the specified action on the UI thread. If the current thread is the UI
* thread, then the action is executed immediately. If the current thread is
* not the UI thread, the action is posted to the event queue of the UI thread.
*
* @param action the action to run on the UI thread
*/
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
如果action是主线程,直接调用run方法,如果不是,则通过post到消息队列里,让主线程的Looper去调用run方法。
// we must have a handler before the FragmentController is constructed
@UnsupportedAppUsage
final Handler mHandler = new Handler();
Activity里默认有一个成员为mHandler,没有指定线程,也就是默认是使用主线程的Looper。
post方法如下:
public final boolean post(@NonNull Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
这个方法重要:
getPostMessage(r)
对传进来的runnable封装到msg中
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
走呀,走,又走到这里了
public boolean sendMessageAtTime(@NonNull 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);
}
这不就是一个普通 的msg进消息队列了么?
再去回顾我们取消息的时候,有一个判断
/**
* Handle system messages here.
*/
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
这个判断
if (msg.callback != null) {
handleCallback(msg);
}
此时的Looper已经是主线程了
private static void handleCallback(Message message) {
message.callback.run();
}
直接调用即可。
Okay,到这里我们的RunOnUiThread就分析完了。
view.post
在所有的View里,我们都可以post一个Runnable
它是View里的方法
public boolean post(Runnable action) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
return attachInfo.mHandler.post(action);
}
// Postpone the runnable until we know on which thread it needs to run.
// Assume that the runnable will be successfully placed after attach.
getRunQueue().post(action);
return true;
}
这里面呢就不展开去说了,大家按这个套路跟下去就好。
okay,到这里的话,我们的Handler的相关源码就看完了,相信你有不少收获的。