Looper.prepare ()
既然是研究Handler,我们先看看我们常用的Handler的构造函数,平时我们都是使用的第一个构造,而第一个构造会再调用下面的构造函数,我们看到里面有一个Looper.myLooper()方法,并赋值给成为Handler的成员变量
public Handler(Callback callback) {
this(callback, false);
}
public Handler(Callback callback, boolean async) {
...
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;
}
Looper.myLooper()非常简单,仅仅是从ThreadLocal类中去get一个Looper对象,我们继续找是在哪里设置进去的
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
还记得我们在使用HandlerThread的时候需要调用Looper.prepare吗?就是为了初始化一个looper保存到当前线程中,方便Handler的构造能使用Looper.myLooper()获取到当前线程的Looper对象,并且从上面的构造函数来看,Looper.prepare()的调用时机应该是优先于Handler的初始化时机
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
// set方法会将当前ThreadLocal对象作为key,looper作为value保存到当前线程中,
// 由于当前的ThreadLocal是Thread的成员变量,我们只需要在set的线程调用
// Looper.myLooper就可以获取到我们之前已经初始化好的looper对象
sThreadLocal.set(new Looper(quitAllowed));
}
当应用初始化完成后,已经帮我们在主线程中保存好了一个Looper的事例,所以,由于是在同一个线程,Handler能调用Looper.myLooper获取到之前保存的Looper对象并且绑定到我们自己的的Handler中
那么问题来了,这个主线程的Looper对象是什么时候设置进去的呢???
思考:activity的生命周期以及ui更新都是依赖于handler通知,那么主线程Looper的初始化时机一定非常非常早,一个app的入口位于ActivityThread.java的main方法,我们看看是不是在这里面初始化的呢?
public static void main(String[] args) {
...
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"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
我们看到了一个非常可疑的函数Looper.prepareMainLooper(),这个就是初始化主线程Looper的方法,我们在点进去看这个方法
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的prepare方法去初始化了一个Looper并保存了起来,而且,还把这个Looper赋值给了sMainLooper变量,Looper中有个getMainLooper返回的就是这个sMainLooper,这也就是使用 Looper.getMainLooper() == Looper.myLooper()来判断是否是主线程的由来
总结
在app启动时,系统会调用Looper.prepareMainLooper()以sThreaLocal为key,Looper为value保存到主线程中,而我们新建主线程Handler的时候,Handler的构造函数会调用Looper.myLooper从sThreadLocal中get出一个Looper,因为为同一个线程,而sThreadLocal又是同一个变量,自然能正确的获取到在ActivityThread.java中初始化好的全局唯一的Looper对象了,而在希望创建一个跑在其他线程的Handler的时候,我们并没有事先将这个线程的Looper给保存到当前线程中中,所以需要调用Looper.prepare来初始化
网友评论