handler的最简单用法:
new Handler().sendMessage(Message msg);发消息
handleMessage(){
//todo 处理消息,刷新UI
}
其他简单知识:
looper轮询器
MessageQueue消息队列
Message消息
looper作用使得queue一直被轮询,handler可以发送msg到queue,完成handler消息机制
那么!疑问来了:
1.他们工作的原理
2.handler为什么能刷新主线程(UI线程)
3.handleMessage为为什么能接收到消息,为什么能接收到对应handler发的消息
等
源码:
1.handler的实例
public Handler() {
this(null, false);
}
public Handler(Callback callback, boolean async) {
....
//静态方法获取轮询器对象
mLooper = Looper.myLooper();
//轮询器的检测,可以看出来使用轮询器之前一定要Looper.prepare(),准备工作
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
//handler内的成员消息队列mQueue, 赋值源轮询器的消息队列mQueue
mQueue = mLooper.mQueue;
//暂时不看
mCallback = callback;
mAsynchronous = async;
}
通过handler对象的创建过程,可以发现
》1.handler内有消息队列对象由Looper的消息队列对象赋值
》2.使用Looper之前,要Looper.prepare(),准备工作
2.发送消息
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
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);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
//接收目标,即把当前的handler作为了消息的target目标属性
msg.target = this;
...
//将消息加入消息队列
return queue.enqueueMessage(msg, uptimeMillis);
}
3.MessageQueue消息队列进行添加消息,并进行排序
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) {
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when; //消息延时
Message p = mMessages; //mMessages下一个消息(当前要执行的消息)
boolean needWake;
//消息队列内消息以类似列表的方式保持顺序
if (p == null || when == 0 || when < p.when) {
//瞬发消息,置为第一个(即队列中下一个要执行的消息)
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
//否者有延时的消息,根据时间进行排序消息队列
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;
}
消息队列存储消息并不是以list来存储和控制顺序的,而是每一个消息都有next指针
以链式存储的方式排序和控制
至此发现,逻辑线断了,msg已经被发到了queue中了,但是怎么就被handleMessage()接收了呢?
前面说looper是轮询器,保证queue中的每个消息被执行,那么接下来就看looper
4.looper
前面提到,使用Looper之前,要Looper.prepare(),准备工作
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
//非常重要:一个线程只能有一个Looper!
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
获取looper,根据线程信息,去获取
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
给我们一个信息就是:looper和线程有关系,一个线程只有一个looper,在使用looper之前要 prepare()创建
并且,这个创建的过程一般都会是线程启动的时候。
那么就结合实际来看
我们知道一个应用启动的时候会启动UI线程,那么什么是这个ui线程呢?
当应用进程被zygote进程分裂出来,会启动ActivityThread的main方法
ActivityThread.main(){
...
//创建looper
Looper.prepareMainLooper();
...
//开始轮询
Looper.loop();
...
}
looper的创建前面说过了,看来玄机应该是Looper.loop()了
public static void loop() {
//获取当前线程的looper
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;
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;
}
final Printer logging = me.mLogging;
final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
final long end;
//**分发消息给,消息的接收目标,前面说过,这个目标就是我们new的handler
try {
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
...
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
msg.recycleUnchecked();
}
}
5.分发消息
public void dispatchMessage(Message msg) {
//post方式发送任务runnable
if (msg.callback != null) {
handleCallback(msg);
} else {
//new handler传递接收callback的情况
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//最终被我们重写,用来接收消息的方法
handleMessage(msg);
}
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
ok!
在应用启动的时候,启动了ui线程,
创建了ui线程的Looper,并且一直轮询执行着所有ui线程queue中的消息
按照消息的接收目标分发给各个handleMessage()处理,由于此过程的looper是主线程(ActivityThread)的
所以可以刷新UI
其他:
其实不是子线程刷新不了主线程UI,而是a线程刷新不了b线程ui
原因和viewRoot有关
其实还有很多要注意或者需要学习的东东:
比如:Message的创建优化原则
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
比如:native方法获取当前线程的原理
public static native Thread currentThread();