面试官还问Handler?那我要给你讲个故事

2021-12-25  本文已影响0人  要早点睡

来吧小兄弟,说说Handler怎么回事

Handler的相关博客太多了,随便一搜都一大把,但是基本都是上来就贴源码,讲姿势,短时间不太好弄明白整体的关系,和流程.

面试官,你坐好,听听我这个故事吹的怎么样?

本文就以生活点餐的例子再结合源码原理进行解析。希望对你有一点帮助。 来,咱们进入角色。

Handler,Looper,MessageQueue,Message的全程协作的关系就好比一个餐厅的整体运作关系


接下来我们回顾下我们餐厅点餐的场景,餐厅点餐分为标准点餐和特殊点餐,我们分解来看。

标准流程

特殊流程

总结

一家店可以有多个点餐员,但是厨师长只能有一个。打单机也只能有一个。

映射到以上场景中


面试官,我差不多吹完了,你要还不信,那就不好意思了?

根据以上的例子我们类比看下源码,充分研究下整个机制的流程,和实现原理。

Looper的工作流程

ActivityThread.main();//初始化入口
    1. Looper.prepareMainLooper(); //初始化
          Looper.prepare(false); //设置不可关闭
              Looper.sThreadLocal.set(new Looper(quitAllowed)); //跟线程绑定
                    1.1.Looper.mQueue = new MessageQueue(quitAllowed); //Looper和MessageQueue绑定
                    1.2.Looper.mThread = Thread.currentThread();
    2. Looper.loop();
        2.1.myLooper().mQueue.next(); //循环获取MessageQueue中的消息
              nativePollOnce(); //阻塞队列
                  native -> pollInner() //底层阻塞实现
                        native -> epoll_wait();
        2.2.Handler.dispatchMessage(msg);//消息分发

myLooper().mQueue.next()实现原理

Handler.dispatchMessage(msg)实现原理


Hander发送消息的流程

1.Handler handler = new Handler();//初始化Handler
        1.Handler.mLooper = Looper.myLooper();//获取当前线程Looper。
        2.Handler.mQueue = mLooper.mQueue;//获取Looper绑定的MessageQueue对象。

2.handler.post(Runnable);//发送消息
        sendMessageDelayed(Message msg, long delayMillis);
            sendMessageAtTime(Message msg, long uptimeMillis);
                Handler.enqueueMessage();//Message.target 赋值为this。
                    Handler.mQueue.enqueueMessage();//添加消息到MessageQueue。


MessageQueue.enqueueMessage()方法实现原理

经常有人问为什么主线程的Looper阻塞不会导致ANR?

以上的所有内容均围绕原理,源码,接下来我们举几个特殊场景的例子

       new Thread(new Runnable() {
           @Override
           public void run() {
              Handler handler = new Handler();
           }
       }).start();


       以上代码会报以下下错误

java.lang.RuntimeException: Can't create handler inside thread Thread[Thread-2,5,main] that has not called Looper.prepare()
       at android.os.Handler.<init>(Handler.java:207)
       at android.os.Handler.<init>(Handler.java:119)
       at com.example.test.MainActivity$2.run(MainActivity.java:21)
       at java.lang.Thread.run(Thread.java:919)


 public Handler(Callback callback, boolean async) {
        ...省略若干代码

       //通过 Looper.myLooper()获取了mLooper 对象,如果mLooper ==null则抛异常
        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;
    }

 public static @Nullable Looper myLooper() {
        //而myLooper()是通过sThreadLocal.get()获取的,那sThreadLocal又是个什么鬼?
        return sThreadLocal.get();
    }

 //可以看到sThreadLocal 是一个ThreadLocal<Looper>对象,那ThreadLocal值从哪赋值的?
 static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

//sThreadLocal 的值就是在这个方法里赋值的
 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));
    }

那子线程怎么创建Handler呢?只需在new Handler()之前调用下Looper.prepare()即可。


    //我们看下ActivityMain的入口main方法,调用了 Looper.prepareMainLooper();
    public static void main(String[] args) {
       ...
        Looper.prepareMainLooper();
        ...
    }

  //看到这一下就明白了,原来主线程在启动的时候默认就调用了prepareMainLooper(),而在这个方法中调用了prepare()。  
 //提前将sThreadLocal 进行赋值了。
  public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }


public class HandlerActivity extends AppCompatActivity {
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler);
        handler.sendEmptyMessageDelayed(1,5000);
    }
}

大致意思就说 “由于这个处理程序被声明为一个内部类,它可以防止外部类被垃圾回收。如果处理程序正在对主线程以外的线程使用Looper或MessageQueue,则不存在问题。如果处理程序正在使用主线程的Looper或MessageQueue,则需要修复处理程序声明,如下所示:将处理程序声明为静态类;并且通过WeakReference引用外部类”。

我们来写一种正确的写法

public class HandlerActivity extends AppCompatActivity {
      MyHandler handler = new MyHandler(this);

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler);
        handler.sendEmptyMessageDelayed(1,5000);
    }
    private static class MyHandler extends Handler{
        private WeakReference<HandlerActivity> activityWeakReference;

        public MyHandler(HandlerActivity activity) {
            activityWeakReference = new WeakReference<>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
        }
    }
}


4. 补充个小知识点,啥是隐式引用?

垃圾回收.png

注意 : 不是所有内部类都建议使用静态内部类,只有在该内部类中的生命周期不可控的情况下,建议采用静态内部类。其他情况还是可以使用非静态内部类的。

好了Handler的介绍到此结束了,篇幅略长,如果给你带来了一点帮助,麻烦给个点赞。

视频:Android程序员备战2022FrameWork必问:handler原理
原文: https://juejin.cn/post/6995854886386532388

上一篇下一篇

猜你喜欢

热点阅读