【转载请注明出处:走进源码,Android面试最常见Handler、Looper、Message问题总结与解答 CSDN 王智博】
今天楼主以面试的角度,走进源码,去探索Handler、Handler、Looper、Message的奥秘。
随着各种网络框架的普及,线程之间的通信再也不需要我们手动的创建Handler,然后handleMessage了,但是当我们想要创建一个主线程延时任务的时候,还是避免不了使用Handler发送一个延时任务,比如下面操作。
如果还没怎么使用过Handler的同学,可以看最简单的Handler、Looper、Message使用指南一(附github源码)
Handler mHandler = new Handler(Looper.getMainLooper());
Handler mHandler2 = new Handler();
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
//延时一秒的任务
}
}, 1000);
那么请问1.mHandler和mHandler2的声明区别是什么呢?2.为什么postDelay方法能够实现延时,延时底层是怎么实现的呢?你看,这是不是我们面试常问的问题吗?
然后我们再看下面的问题,在子线程使用Handler sendMessage发送消息,主线程便能获取数据并且展示。3.那么为什么Handler发送Message,主线程便能接收到Message并获取数据?4.为什么Handler对象能够子线程和主线程都能访问了?(这是java内存模型的知识,后面我会单独写一篇)
Handler mainHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
//主线程处理
switch (msg.what) {
case WHAT_MODIFY_TV:
tvReciver.setText("接受子线程传递的参数:" + msg.obj);
break;
default:
}
}
};
//子线程发送消息
mainHandler.sendMessage(Message.obtain(mainHandler, WHAT_MODIFY_TV, num++));
问题1 new Handler(Looper.getMainLooper())和new Handler()有区别吗?
我们首先看new Handler(),实际调用Handler(callback,async),可以看到这里会初始化一个Looper,然后判断如果Looper不存在,说明你所在的线程还没有调用Looper的prepare方法。也就是说Looper的prepare方法里面是looper的初始化?
我们可以看到Looper的myLooper方法,实际上是获取ThreadLocal的Looper对象
那么sThreadLocal这个对象又是啥?
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
实际上是一个ThreadLocal类型的Looper对象,那么它的初始化地方是哪呢?
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));
}
所以如果你没有调用Looper的prepare方法,ThreadLocal的Looper对象就不会创建,所以handler报错说你需要prepare。
所以new handler()和new Handler(Looper.getMainLooper())的区别在于new handler()是自己调用Looper.myLooper()获取到的looper,也就是获取你当前所在线程的Looper,一般我们在主线程new handler()获取到的就是主线程的Looper,也就是主线程的ThreadLocal<Looper>中get出来的Looper对象。
那么Looper.getMainLooper()为什么就能获取到主线程的Looper,而且就算是在子线程中,我们还是能获取到主线程的Looper。
这里其实还是内存模型的知识,后面会单独介绍下。
首先我们来看下Looper.getMainLooper()方法,可以看到这里是一个类锁,是线程安全的,返回的是sMainLooper这个对象,这是一个静态对象,Looper.getMainLooper是一个静态方法,所以所有的线程都能调用这个方法获取sMainLooper这个对象。
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}
那么主线程这个sMainLooper对象又是什么时候初始化的呢? 可以看到是prepareMainLooper这个方法中初始化的,那么我们来看下这个prepareMainLooper的调用时机。
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的main主函数里面调用的
ActivityThread
public static void main(String[] args) {
...
Looper.prepareMainLooper();
}
小结:
1.new handler()持有的是当前线程的Looper,而new handler(Looper.getMainLooper())持有的是主线程的Looper
2.Looper其实是一个ThreadLocal类型的,myLooper()通过ThreadLocal.get获取当前线程的Looper
3.,Looper.getMainLooper()获取的是sMainLooper这个静态对象,这个对象是在ActivityThread主线程的main主函数入口中,调用prepareMainLooper()是初始化的
问题二 为什么Handler对象能够子线程和主线程都能访问了
对象是存在java虚拟机的内存模型中的堆区域,这个区域是所有线程都能够访问的