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利用率;
即当管道中有内容可读时,就唤醒当前正在等待管道中的内容的线程。