2023-02-24

handler 机制

2.5 子线程能否更新UI?为什么

子线程是不能直接更新UI的

注意这句话,是不能直接更新,不是不能更新(极端情况下可更新)

绘制过程要保持同步(否则也没不流畅),而我们的主线程负责绘制UI,极端情况就是,

在Activity的onResume(含)之前的生命周期中子线程都可以进行更新UI,onCreate,onStart和onResume,此时主线程还没有开始绘制。

2.6 Handler 机制和原理?

  • 首先创建一个Handler对象,重写handlerMessage方法,handlerMessage可以通过参数msg来写接受消息后UI线程的逻辑处理
  • 接着创建子线程,在子线程中需要更新UI的时候,新建Message对象,并将消息的数据记录在这个消息对象Message的内部,如arg1,obj;
    然后通过前面的handler对象调用sendMessage方法吧这个Message实例发送出去,之后这个消息会被放入MessageQueue队列中等待被处理。
  • 此时messageQueue的管家looper正在不停的吧MessageQueue的消息取出并通过dispatchMessage方法将消息传递给Handler的 handleMessage方法。

2.7 为什么在子线程中创建handler会抛异常?

不能在还没有调用Looper.prepare()方法的线程中创建Handler.

抛出异常的地方在mLooper对象为null的时候,说明这里的Looper.myLooper()的返回值为null。要先调用looper.prepare()方法才能

构建Looper对象。

2.8 Handler的post和senMessage的区别和场景?

post和senMessage最后都会调用sendMessageAtTime方法进行发送。

但是在post方法中 message是通过getPostMessage(Runnable r)这个方法获取message,在这个方法中有这样一句代码m.callback = r;给
message的callback赋值的是runnable对象。

而在dispatchMessage这个方法中对消息进行分发的时候,如果msg.callback!=null 则在传递过来的runnable内的run方法处理消息。如果为null则
吧消息交给handler的handleMessage处理。

  • post:调用post方法的消息在post传递的runnable对象的run方法中处理。
  • sendMessage:在重写的handlerMessage方法中处理并返回true

2.9 Loop是死循环,为什么没有阻塞主线程?

主线程挂起 (主线程是随时挂起随时阻塞的)

Looper是一个死循环,不断的读取MessageQueue中的消息,Looper调用MessageQueue的next方法来获取新的消息。
Next操作是一个阻塞操作,当没有消息的时候,next方法会一直阻塞,进而导致loop一直阻塞。

messageQueue.nativePollOnce 会让线程挂起-阻塞-block住,另一个方法nativeWake是实现线程唤醒的,所以主线程是可以随时挂起和阻塞的。

系统这么实现阻塞和唤醒

这种机制是通过管道(pipe)机制实现的。

简单来说,管道就是一个文件,在管道的两端,有两个文件描述符(一个读一个写),分别对应同一个文件。

一般的使用方式就是:一个线程通过读文件描述符,来读管道的内容,当管道没有内容时,这个线程就会进入等待状态,
另一个线程通过写文件描述符,来向管道中写入内容,写入内容的时候,如果另一端有线程正在等待管道内容,那么就会别唤醒。

那么这个等待和唤醒的操作是如何进行的呢?这需要借助Linux系统中的epoll机制,Linux系统中的epoll机制为处理大批量句柄而作了改进的poll,
是Linux下多路复用IO接口,select/poll的增强版本,它能显著减少程序,在大量并发连接中,只有少量活跃的情况下的系统cup利用率;

即当管道中有内容可读时,就唤醒当前正在等待管道中的内容的线程。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容