1、Handler实现原理
Android应用是通过消息驱动运行,Android中一切皆消息,触发,绘制,刷新等一切偶都是消息。Handler就是消息机制的上层接口,平时开发种我们只会接触到Handler和Message,内部还是Messagequeue和Looper来共同实现消息循环系统。
1.1、Handler通过postXXX或者sendXX,post会将Runnable包装成一个Message.sendMessageAtTime,最终调用enqueueMessage
1.2、MessageQueue是一个优先级队列,核心方法是enqueueMessage和next,一个插入一个取出。之所以是优先级是因为enqueueMessage方法会根据Message的执行时间来对消息插入,越晚约在后面
1.3、Looper是一个消息泵,核心方法是loop。首先会通过mylooper来拿到当前线程的Looper,接着拿到Looper种的MessageQueue,开启死循环,不断通过MessageQueue的next方法将消息取出来并执行
2、一个线程有几个Handler?一个线程有几个Looper,如何保证?
2.1 Handler大的个数与所在线程无关,可以在线程种实例化任意多个Handler
2.2一个线程种只有一个Looper。
2.3因为Looper的构造方法被私有化,无法new来实例化,值开放了Looper.prepare(),通过ThreadLocal(一个线程内部存储类)来先获取看有没有,没有就创建,有 就抛出异常,依次来保证唯一性。
3、Handle线程是如何进行切换的
3.1 本质上是因为工作线程和主线程共享地址空间,也就是Handler实例对象mHandler位于线程间共享的内存堆空间上,主线程和工作线程都能够直接使用改对象。而不同进程拥有不同的地址空间,就不能用Handler来实现进程间通信。
4、Handler内存泄漏的原因是什么?如何解决?为什么其他的内部类没有这个问题。
4.1 Handler内存泄漏是因为内部类持有外部类的引用。举个例子,下面代码handler调用外部click()是因为省略了前面的MainActivity.this,虚拟机会加上。
Mainactivity及时onDestor依旧不会被释放,因为可达性分析,Java虚拟机认为Mainactivity被handler使用。
在实际使用中,Handler可能因为延时消息,导致message一直放在Messagequeue种未被使用,那么,handler.handleMessage持有activity,MessageQueue的message持有handler(target是handler),message被looper持有,looper被ThreadLocal持有,ThreadLocal被主线程持有。根据可达性分析,所以不会被回收。
4.2.1 解决方案第一是用静态内部类+弱引用。那么将外部类传入到静态内部类,用弱引用关联,如果垃圾回收,就直接回收了内部类。 一定是要静态内部类,因为内部类,JVM会默认传入外部类的引用,一定会持有
4.2.2 解决方案第二是在外部类快结束生命周期时,把handler执行中和未执行的消息都清空。handler.removeCallbackAndMessages(null)
4.3 其它的内部类主要是没有对内部内的一个引用,这个重点在handler引用的根可达,所以才引发了内存泄漏。
5、为什么主线程可以new Handler,子线程如果想要new Handler要怎么办?
因为在主线程中,默认ThreadActivity的main函数中调用了prepare()方法,将looper初始化,并且调用了loop,使looper滚动起来了。
子线程想要调用new Handler则需要自己调用looper的prepare和loop,如果想要退出则调用quit
6、多个Handler往MessageQueue中添加数据买入和保证线程安全?
enqueueMessage中使用了同步代码块
7、我们使用Message时如何去创建它
有1、new Message() 2、message.obtain和 handler.obtainMessage, 推荐用2 因为能够回收利用 3、最终还是调用的2
8、为什么Looper死循环不会导致应用卡死
这两者不是一回事,Looper死循环是为了保持主线程一直处于运行状态,通过looper便利MessageQueue,处理各个消息。 而卡死ANR是埋定时炸弹,某个任务在规定时间内没做完,
到点就炸。主要是为了避免主线程长时间做耗时操作而导致卡顿做的一个限定。
9、Handler的阻塞唤醒机制
Handler的阻塞唤醒机制基于LInux阻塞唤醒机制。叫epoll 本地创建一个文件描述符,需要等待的地方监听这个文件描述符,唤醒的一方修改这个文件,等待的一方就会打破唤醒。
通俗语言就是,等快递时你随便干啥,快递到了给你打电话。对应就是非阻塞轮询,就是一直打电话给快递员问他快递到了没。
eploo是linunx内核中一种可扩展IO事件处理机制。
10、如何提升消息的优先级?
将消息设置为异步消息 同步Handler会遵循与回执任务的顺序,设置同步屏障之后,会等待回执任务完成,才会执行同步任务; 而异步任务与绘制任务的先后顺序无法保证,等待VSYNC期间可能被执行,也可能会执结束后才执行。 如果保证与绘制任务的顺序,用同步,其它用异步
1、屏幕会在每次刷新的时候发出一个 VSYNC 信号,通知 CPU 进行绘制计算。 2、调用 requestLayout() 方法之后,并不会马上开始进行绘制任务,而是会给主线程设置一个同步屏障,并设置 ASYNC 信号监听。 3、当 ASYNC 信号的到来,会发送一个异步消息到主线程 Handler,执行我们上一步设置的绘制监听任务,并移除同步屏障