走进源码,Android面试最常见Handler、Looper、Message问题总结与解答

【转载请注明出处:走进源码,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这个对象又是啥?

扫描二维码关注公众号,回复: 5081526 查看本文章
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虚拟机的内存模型中的堆区域,这个区域是所有线程都能够访问的

问题三 为什么postDelay方法能够实现延时,延时底层是怎么实现的呢

猜你喜欢

转载自blog.csdn.net/wangzhibo666/article/details/86514518