声明:个人笔记,无参考学习价值
1.为什么不能在子线程中创建Handler
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());
}
}
//1.在Handler的构造函数里面,会调用Looper.myLooper(),来获取Looper对象,如果Looper对象为null,就会抛出异常
//Handler需绑定 线程才能使用;绑定后,Handler的消息处理会在绑定的线程中执行, 先指定Looper对象,从而绑定了 Looper对象所绑定的线程(因为Looper对象本已绑定了对应线程)
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
//Looper对象,是和消息队列绑定的可以在Looper()的构造参数中看到消息队列的构建
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
至此,Handler的构造参数中,指定了Looper对象,从而也就绑定了线程和消息队列
---------------------------------------------------------------------------------------------------------------------
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
}
下面看一看Looper.myLooper(),我们跳转到Looper类中:
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
//返回与当前线程关联的Looper对象,如果正在调用的线程没有关联一个Looper,那么返回null
//拿到Looper对象,就等于绑定了线程
public static @Nullable Looper myLooper() {
//我们再去看一下sThreadLocal
return sThreadLocal.get();
}
除非你已调用了prepare(),否则sThreadLocal.get()将会返回null
// sThreadLocal.get() will return null unless you've called prepare(). @UnsupportedAppUsage
static final ThreadLocal sThreadLocal = new ThreadLocal();
来看下prepare()
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
//如果sThreadLocal.get() != null,根据上面的代码可以知道,不为null,说明了已经调用了prepare,那么就不能再次重复调用了,所以抛出异常
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//,然后调用这句创建Looper
sThreadLocal.set(new Looper(quitAllowed));
}
小结: 那么看到这里,就稍微有点眉目了,要调用Handler对象,就必须拿到Looper对象,要拿到Looper对象呢,又必须调用prepare()方法,这也就印证在在Handler构造参数中的这句抛出的异常 "Can't create handler inside thread " + Thread.currentThread() + " that has not called Looper.prepare()":
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
我们没有手动调用prepare().那就是说我们需要在子线程中的代码里手动调用了
Looper.prepare()
2.那么问题来了,我们在主线程中调用Handler也没有去调用Looper.prepare()呀?
首先看一下在主线程中Looper调用的方法:
/**
* 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()}
*/
//皇家塑料翻译:
//将当前的线程初始化为一个looper对象,并将它标记为应用的主looper.这个应用的主looper已经在android环境被创建,因此你绝对不需要自己去调用这个函数
public static void prepareMainLooper() {
//prepare(false)这一句稍后分析
prepare(false);
synchronized (Looper.class) {
//这个判断和prepare()的一样,只能创建一个
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
//这里和prepare()不一样,直接从myLooper()中自信拿到sMainLooper,而且之前的myLooper()我们也看过, return sThreadLocal.get(); 那么也就是说必然会返回一个sMainLooper对象
sMainLooper = myLooper();
}
}
那么我们再把myLooper()源码上的注解塑料翻译一遍,懂得自行跳过:
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
返回一个和当前线程关联的looper对象,如果调用的线程没有关联一个Looper返回null
我们知道,一个android程序中默认会有一个主线程
直接看 Return the Looper object associated with the current thread,这个current thread在prepareMainLooper()中,当然是主线程了,
所以它一定为我们做了一些事情,所以这里才会自信的直接调用 sMainLooper = myLooper();
而子线程是我们自己创建的,它并没有关联一个Looper,所以需要手动去Looper.prepare()(禁止套娃,又回到上面去了)
那么去看看主线程中怎么调用的吧 去ActivityThread中,有一个main()方法
public static void main(String[] args) {
....
//在这里调用了prepareMainLooper(),那么这个方法中就调用了prepare(false),那么sThreadLocal.get()中必然有值,那么myLooper(),就必然有值,那么sMainLooper,就成功赋值拉
Looper.prepareMainLooper();
.....
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
....
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
好了,再来说一下prepare(false),直接跟进源码 prepare(boolean quitAllowed)
//quitAllowed这个boolean值就传进了Looper的构造参数
sThreadLocal.set(new Looper(quitAllowed));
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
//传进了MessageQueue的构造参数
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
并成为一个成员变量,供全局调用
mQuitAllowed在MessageQueue中只有一次调用
void quit(boolean safe) {
//就是这里了, 如果设置了false,那么就抛出这个异常,表示prepareMainLooper()执行后,再执行quit,就会抛出"主线程不允许退出的错误"
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) {
return;
}
mQuitting = true;
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}
顺带一看,至于quit(boolean safe),是在Looper的quit()和quitSafely()分别被调用,这两个方法都是退出looper的方法,不过更建议使用quitSafely()安全退出
public void quit() {
mQueue.quit(false);
}
/**
* Quits the looper safely.
*
* Causes the {@link #loop} method to terminate as soon as all remaining messages
* in the message queue that are already due to be delivered have been handled.
* However pending delayed messages with due times in the future will not be
* delivered before the loop terminates.
*
* Any attempt to post messages to the queue after the looper is asked to quit will fail.
* For example, the {@link Handler#sendMessage(Message)} method will return false.
*
*/
//在looper被要求退出之后,任何企图发送消息到队列的行为都将失败,举例:Handler的sendMessage(Message)将会返回失败
public void quitSafely() {
mQueue.quit(true);
}
3.既然说了Looper.prepare(),那么顺带也把Looper.loop()也说了,毕竟Looper要执行还得用这个方法
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {
//拿Looper对象,没有的话就去调用Looper.prepare,就不用多说了
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
//有looper对象,就取出他的消息队列
final MessageQueue queue = me.mQueue;
.........
//循环,取出消息
for (;;) {
//------->MessageQueue.next()单独再看
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
.................
try {
// msg.target,从Message中可以看到是Handler对象,那么后面调用的就是Handler中的dispatchMessage()方法
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进入Message对象回收复用的环节,可见下面第四点中的Message对象创建
msg.recycleUnchecked();
}
}
看下msg.target.dispatchMessage(msg),这里是我们处理消息中比较关心的内容
/**
* Handle system messages here.
*/
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null)
// 若msg.callback属性不为空,则代表使用了post(Runnable r)发送消息,则执行 handleCallback(msg),即回调Runnable对象里复写的run()
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg); //这个handleMessage(msg),就是我们在调用时复写的handleMessage,而处理UI的操作就是在这里完成的,他是一个空方法
}
}
然后,说一下MessageQueue中的next()
* 分析1:queue.next()
* 定义:属于消息队列类(MessageQueue)中的方法
* 作用:出队消息,即从 消息队列中 移出该消息
*/
Message next() {
...
// 该参数用于确定消息队列中是否还有消息
// 从而决定消息队列应处于出队消息状态 or 等待状态
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
// nativePollOnce方法在native层,若是nextPollTimeoutMillis为-1,此时消息队列处于等待状态
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
// 出队消息,即 从消息队列中取出消息:按创建Message对象的时间顺序
if (msg != null) {
if (now < msg.when) {
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 取出了消息
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 {
// 若 消息队列中已无消息,则将nextPollTimeoutMillis参数设为-1
// 下次循环时,消息队列则处于等待状态
nextPollTimeoutMillis = -1;
}
......
}
.....
}
}//
4.为什么使用Messag.obtain(),而不是new Message()来创建消息对象
首先看源码中Message的构造方法 : 最好的方式是通过调用Message.obtain()来获取一个消息对象
/** Constructor (but the preferred way to get a Message is to call {@link #obtain() Message.obtain()}).
*/
public Message() {
}
有八个不同参数的静态obtain()方法
/**
* Return a new Message instance from the global pool. Allows us to
* avoid allocating new objects in many cases.
*/
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();
}
可以看到,obtain()时,先判断sPoo中不为null的情况下,从sPool中取出一个Message返回,达到复用的效果,如果sPool为null的情况下就new Message()来创建一个新的
5. 那么来了解一下Message的Pool是怎么实现的
来看一下Message中的这两个方法:
首先recycle()的注释第一句什么意思,返回一个Message实例到全局的pool中
/**
* Return a Message instance to the global pool.
*
* You MUST NOT touch the Message after calling this function because it has
* effectively been freed. It is an error to recycle a message that is currently
* enqueued or that is in the process of being delivered to a Handler.
*
*/
public void recycle() {
if (isInUse()) { //return ((flags & FLAG_IN_USE) == FLAG_IN_USE); isInUser()返回一个判断标识符
if (gCheckRecycle) { //这个全局变量赋值为true,还有个判断是5.0系统一下的为false
throw new IllegalStateException("This message cannot be recycled because it "
+ "is still in use."); //这个消息不能被回收,因为他仍然在被使用
}
return;
}
recycleUnchecked();
}
/**
* Recycles a Message that may be in-use.
* Used internally by the MessageQueue and Looper when disposing of queued Messages.
*/
// 回收一个可能正在被使用的Message 当处理排队的信息时,在消息队列和Looper内部调用
@UnsupportedAppUsage
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = UID_NONE;
workSourceUid = UID_NONE;
when = 0;
target = null;
callback = null;
data = null;
//以上 flags = FLAG_IN_USE为确保消息不被回收,剩余的都是将消息置空
synchronized (sPoolSync) {
//如果池里的数量<设置的最大值50,sPool赋值给next,当前的消息对象赋值给sPool,sPoolSize用来记录数值
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
可以看出Message在调用recycle()回收消息的时候,对Message做出存储,当下次调用obtain()时,从sPool中取出复用
6.那么Message的recycle()是什么时候调用的呢,不急,我们先看Handler的sendMessage(),毕竟创建的消息是要先干活的嘛
通过sendMessage(msg) 方法,一路跟进了
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
//将当前这个Handler对象赋值给msg.target
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
//Message携带这三个参数,和uptimeMills被MessageQueue的enqueueMessage执行
//而这个MessagQueue,如果你还记得之前Handler构造参数是在做什么操作,那你就应该知道,它是跟着Looper来的
return queue.enqueueMessage(msg, uptimeMillis);
}
接下来,去MessagQueue中去
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;
// 判断消息队列里有无消息
// a. 若无,则将当前插入的消息 作为队头 & 若此时消息队列处于等待状态,则唤醒
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;
// b. 判断消息队列里有消息,则根据 消息(Message)创建的时间 插入到队列中
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;
}
// 之后,随着Looper对象的无限消息循环
// 不断从消息队列中取出Handler发送的消息 & 分发到对应Handler
// 最终回调Handler.handleMessage()处理消息
注:文中所有图片来源https://www.jianshu.com/p/b4d745c7ff7a
总结
- 根据操作步骤的源码分析总结
- 工作流程总结