Handler
一个线程有几个Handler
new
几个 有几个,每个activity
都可以new
,activity
跑在主线程
一个线程有几个Looper,如何保证
- 1个,再创建会抛运行时异常。
Looper
类中有成员ThreadLocal
,ThreadLocalMap
存的 key是ThreadLocal
,value
是Looper
对象
Handler
内存泄露原因?为什么其他的内部类没有说过有这个问题?- (静态?)内部类持有了外部类对象的引用,
handler
匿名内部类持有activity
的引用,可以调用activity的方法。activity
走了onDestroy
后,jvm
的可达性分析认为activity
的引用任然被其他地方持有,就不能回收释放。有所有的内存泄露都是生命周期不一致 RecyclerView
里面的内部类ViewHolder为什么没有这个问题,要从handler
的发消息原理分析,handler有延迟发送message的方法(比如延迟1分钟),message
没处理完,将一直在内存中,而message持有handler
的引用,handler
持有messagequeue
和activity
的引用. 所有内部类都持有外部类的引用,但其他类一般没有耗时的东西,没有另外一个东西持有这个内部类,所以其他的没有这个问题。怎么解决:软引用,弱引用。
- (静态?)内部类持有了外部类对象的引用,
为何主线程可以
new Handler
?如果想要在子线程中new Handler要做哪些准备?- ActivityThread的main中调用了Looper.prepareMainLooper() 和 Looper.loop(),主线程(所有activity,service生命周期的函数)的所有代码运行于loop函数中,且都以消息的方式存在
- 子线程中要new Handler怎么做?创建Looper,1.prepare() 2.loop()
子线程中维护的Looper,消息队列无消息的时候处理方案是什么?有什么用?
调用Looper的quitsafely(),实际调用MessageQueue的quit,会调用nativeWake,会remove所有消息,释放内存的作用;但是loop死循环仍在跑,任然在阻塞,实际不会,nativeWake会叫醒,loop中的去消息会返回null,死循环退出。主线程不允许调消息队列的quit,会抛非常状态异常。
多个线程共享handler,都在里面发消息给主线程,如何保证线程安全 ?
handler的消息入队和Looper的loop中取消息方法加锁了,锁的是handler对象 ;
handler的消息delay 时间准确吗,不完全准确。因为多线程访问时,加了锁,可能会延时。
使用Message时如何创建它?
new 就玩了,应该用obtain():
sPool,享元设计模式
先讲内存共享(内存复用,内存抖动)
sd
- MessageQueue中没有消息,则不会执行,计算等待多长时间,wait,
Looper死循环为什么不会导致应用卡死,不会anr?
这个问题是最难的。loop中取消息阻塞block状态(主线程睡眠,睡眠不是卡死)10S是不会ANR(ANR是卡死)的。
9.1 什么时候会ANR?
- 在5s没有响应输入事件(按键按下,屏幕触摸),eg:某个事件或者oncreate中有个非常耗时的操作,那么点击其他按钮就没有响应,loop无法处理其他消息就ANR
- BroadcastReceiver在10s内没有执行完毕
9.2 主线程唤醒的方式
- 输入的事件(每一个事件都是Looper里面的一个message,所有的事件都是在activity的生命周期执行的,界面的所有操作执行在loop方法中,没有任何操作时就阻塞在取消息那里)
- 往Looper添加消息
post + send 共14个方法
Handler工作流程
Handler
中有MessageQueue
类的成员变量
Message中有Handler类成员变量,持有Handler的引用
MessageQueue :(传送带)单链表+ 执行时刻when小到大实现的优先级队列
mMessages:表示执行时刻小的消息,表示队头?还是对尾
next():如果当前时间毫秒值now 比队头消息的when还小,等待;
否则,
Looper
Looper.loop():传送带的动力,开关的作用,死循环不断调用MessageQueue的next()
loop()中取消息基本不会为null,如果消息队列为null,nativePollOnce(ptr, nextPollTimeoutMillis);会使线程一直处于block状态,next()不会返回Null
handler消息传送是子线程发送消息到主线程的过程
新浪面试题
所谓的入队其实就是将所有的消息按时间来进行排序
所谓的入队其实就是将所有的消息按时间来进行排序
1.概念(作用)
Handler的主要作用是发送和处理消息;
MessageQueue称作消息队列,采用单链表的数据结构存储Message;
Looper是一个循环,会在一个无限循环中不断从MessageQueue中获取Message,如果有Message,就交给对应的Handler去处理;每个Handler
都关联着一个Looper
Message是传递的消息,它的target参数是发送它的Handler对象。(Message中有Handler类成员变量,持有Handler的引用)
2.dispatchMessage(Message msg)是如何处理的。
msg.callback(Runnable类型)->Handler中的mCallback(interface Callback,只有1个handlerMessage方法)->handleMessage方法
3.Handler如何切换线程
我们都是在线程内创建Handler
之后,就调用Looper.loop()
的。当消息循环执行到我们发送的那个消息时,它自然就在调用Looper.loop()
方法的线程内执行了,这就是跨线程的原因,就这么简单,有没有ThreadLocal
它都能跨。
4.更新UI的方式
1.使用Handler的post(Runnable r)方法
调用了sendMessageDelayed->sendMessageAtTime-≥enqueueMessage
和send有什么区别?post的Message的callback不空
2.Handler的sendMessage()
3.在Activity中使用runOnUiThread 方法,实际也是调用了Handler的post方法(不是UI线程时)
4.view.post(Runnable action)方法 也是调用了Handler的post
5.AsyncTask
5.ThreadLocal使得每个线程只有一个Looper
6.消息怎么入队 怎么出队
主线程创建的成员变量handler, 因为子线程匿名内部类持有外部activity的引用.
所以能够访问它的成员变量,调用handler的方法,而handler的sendMessage方法,是往消息队列中插入消息,该消息入队的方法是同步的,锁的对象是MessageQueue对象,这个对象1个线程只有1个,是在主线程中创建looper时创建的,looper取消息锁的对象也是MessageQueue对象。
也就是Handler对象在堆中是这2个线程共享的,子线程调用入队方法,MessageQueue对象也是共享的。
总结:
1.子线程中调用堆中共享对象MessageQueue的入队方法,链表插入节点
2.发送完消息,操作系统线程调度(比如时间片,先进先出)时间片到主线程了。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!