行为的英文译语怎么说-水分的拼音
2023年4月19日发(作者:assign是什么意思)Android⾯试笔记之Handler详解
Android ⾯试之Handler详解
1. ⼀个线程有⼏个Handler?
可以有多个Handler 发送赤壁赋翻译简短版本 消息
2. ⼀个线程有⼏个Looper ? 如何保证?
2.1 ⼀个线程有⼏个Looper ?
只能有⼀个,在创建Handler的时候指定Looper,该Looper创建的线程,就是处理消息的线程
mMyHandler = new MyHandler(per());
2.元稹诗词最佳十首 2 如何保证?
每⼀个线程 都有⼀个ThreadLocal ( 运⽤了HashMap),⽤来保存 线程的 状态,标志位等 上下⽂环境 (⼤量的key-value 键值对)
2.2.1 如何保证⼀个key,只有⼀个value?
Hash算法 -- Hashcode 保证唯⼀性
2.2.2 如何保证 Looper 不可修改?
在setLooper之前会检查,对应key的值是否为空,如果不为空,则抛出异常,保证唯⼀性
...
// sThreadLocal 是static final 修饰的,整个app中只有⼀个,只会初始化⼀次
static final ThreadLocal
...
e() {
if (() != null) {
throw new RuntimeException(\"Only one Looper may be created per thread\");
}
(new Looper(quitAllowed));
}
(new Looper(quitAllowed)); {
...
(ThreadLocal, looper);
...
}
因为线程只有 ThreadLocal,所以保证了只能有⼀个Looper,也保证了
2.3 Looper 和 MessageQueue 什么关系?
MessageQueue是在Looper⾥进⾏初始化的,并且是Final修饰的,⼀旦初始化不可改变 两者⼀⼀对应
...
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = tThread();
}
⼩结
Thread -- ThreadLocalMap --
==> ⼀个Thread 只有⼀个MessageQueue
==> ⼀个Thread 只有⼀个Looper
3. Handler内存泄漏原因? 为什么其他的内部类没有说过这个问题?
3.1 Handler内存泄漏原因?
内部类持有外部类对象
3.1.1 为什么Handler会持有外部类对象?
1. 因为 Handler是通过匿名内部类创建的,⽽在java中 ⾮静态内部类 和 匿名内部类 都默认持有外部类的引⽤,所以 匿名内部类 可以随意使⽤外部类的⽅法和
属性。
2. 其次 在eMessage 时, = this; 使message持有了Handler对象引⽤,⽽Handler默认持有了 引⽤,如果message做⼀
些耗时操作,此时 Activity 销毁了,那么队列中的 等待执⾏的消息依然持有 Activity的引⽤。
3. 最后 Activity ⽆法被gc回收,从⽽导致了内存泄漏。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
= this;
if (mAsynchronous) {
nchronous(true);
}
return eMessage(msg, uptimeMillis);
}
Msg == > 持有 Handler 对象 ==> 持有Activity 对象
eg:如果这个消息⼀个⼩时候后执⾏,那么msg 会⼀直持有Activity对象,不能被JVM回收
3.2 如何解决泄漏问题?
1. 在 Actvitity. onDestroy的时候,将消息队列中的消息清空即可
@Override
protected void onDestroy() {
roy();
CallbacksAndMessages(null);
}
2. 使⽤static修饰Handler,软引⽤ 弱引⽤
private static MyHandler mMyHandler;
class MyHandler extends Handler {
WeakReference
public MyHandler() {
mContext = new WeakReference<>();
}
}
3.2.1 static关键字作⽤?为什么能够解决泄露问题?
静态内部类,是不会持有外部类对象的
3.3 为什么其他的内部类没有说过这个问题?
因为其他内部类不会持有Activity对象
4. 为韩愈的诗词全集 何主线程可以new Handler? 如果想要在⼦线程中new Handler要做些什么准备?
4.1 为何主线程可以new Handler?
1. 因为 Android 应⽤在启动流程中会经过 ⽅法,在main ⽅法中会调⽤ eMainLooper(); 以及 ();⽅法对主线程Ha
ndler的Looper进⾏初始化。
2. 所以我们在 主线程中可以直接创建 Handler使⽤,⽽不需要在进⾏初始化Looper的操作。
4.2 如果想要在⼦线程中 new Handler 要做些什么准备?
需要 进⾏ e 以及 (); 的调⽤,并且在初始化Handler的时候,将Looper传⼊
不过不建议在⼦线程中进⾏ new Handler,建议使⽤ HandlerThread,已经封装好了
4.2.1 锁
wait and notifyAll:wait--释放锁,并且等待被唤醒
notifyALl--通知之前wait的线程就绪,但不会释放锁,等synchronized代码块执⾏完,进⾏释放
内置锁(系统内置的锁):执⾏完synchronized代码块,由JVM来解锁
synchronized(object)
synchronized(this)
eg1:
HandlerThread1中两个函数func1,func2
handlerThread1 = new HandlerThread1
两个线程中,分别执⾏1 和 2
结论:当线程1执⾏synchronized(this) 代码块时,线程2 等待
func1() {
synchronized(this)
{
///
}
}
func2() {
synchronized(this)
{
///
}
}
eg2:同上,将两个函数中的synchronized(this) 替换成 synchronized(object)
结论:如果两个线程锁的是同⼀个object,则相互关联,否则,不影响执⾏顺序
5. ⼦线程中维护的Looper,消息队列⽆消息的时候的处理⽅案是什么?有什么⽤? 主线程呢?
⼦线程中消息队列⽆消息时,睡眠等待,如没有消息,可调⽤,清空数据
5.1 主线程中不允许调⽤函数,为什么?
因为主线程中⼤量⽤到了Handler消息处理,⽐如Activity的启动,UI交互,通信
5.2 MessageQueue 源码分析
数据结构:链表构成的 优先级队列
1. ⼊队的时候,根据执⾏时间排序,队列满的时候,阻塞,直到⽤户通过next取消息,通知MessageQueue可以进⾏⼊队
2. 出队的时候,启动轮询机制,当MessageQueue为空时,队列阻塞,等消息队列调⽤enqueueMessage时,通知队列可以取出消息,停⽌阻塞
6. 既然可以存在多个 Handler 往 MessageQueue 中添加数据(发消息时各个Handler可能处于不同线程),那
它内部是如何确保线程安全的?
因为⼀个线程中 MessageQueue 只有⼀个,在 en’queueMessage 中运⽤了锁:synchronized(this),保证每次只能往队列中放
⼀个,保证了线程安全
6.1 next 取消息时,为什么也要⽤到锁?不是会降低取消息速度吗?
因为要保证,在取消息时,没有其他消息插⼊,取消息 跟 插⼊消息 不能 同时进⾏
6.2 性能优化–线程
线程并不是越多越好,⼀般最⼤是 cpu数量的2倍 + 1
Glide Okhttp 都有⾃⼰的线程池?如何优化?-- 公⽤同⼀个线程池(使⽤反射机制,重新对框架的线程池赋值)
7. 我们使⽤ Message 时应该如何创建它?
使⽤obtain,复⽤--⽤到了享元设计模式
最⼤不要超过50个消息
如果不使⽤obtain,⼀直 new message,会造成 内存抖动 ==》GC == 》 卡顿
在⾃定义View中Draw(),也不能使⽤new,也会造成同样的内存问题
8. Looper 死循环为什么不会导致应⽤卡死
8.1 既然 Handler 消息全是 Loop 来的,为什么没有ANR问题?
之前不是说五秒钟不响应就会出现ANR问题吗?为什么休眠好长时间也不会ANR?
产⽣ANR的原因不是因为主线程休眠时,⽽是因为输⼊事件没有响应,输⼊事件没有响应就没法唤醒Looper,才加⼊了五秒限制
8.2 应⽤在没有消息的时候,是在休眠,释放线程,不会导致应⽤卡死,卡死是ANR,⽽Looper是睡眠
思考? 传送带模式
1. Handler如何实现线程间的跨越?
⼦线程发送消息,主线程取出消息
⼦线程:ssage-- enqueueMessage-- eMessage(管理内存的)
主线程:ActivityThread 中调⽤ -- 取出消息 -- dispatchMessage分发消息 --rMessage
2. Activity启动流程
Launcher 应⽤⾥点击 我们的应⽤图⽚,启动 zygote,创建⼀个JVM,JVM会去调⽤ActivityThread中的main函数
2.1 ActivityThread启动Activity
2.2 根Activity启动时序图
3. Handler通信机制,就是线程中管理内存的机制
4. 消息机制之同步屏障
4.1 如果有⼀个事件(message)必须尽快执⾏,那么Handler是如何通过消息屏障保证事件顺利执⾏的?
消息是根据执⾏时间进⾏先后排序,然后消息是保存在队列中,因⽽消息只能从队列的队头取出来。那么问题来了!需要紧急处理的
消息怎么办?
其实是通过设置 同步屏障来解决的
4.2 那么同步屏障是⼀个什么东西呢?
4.2.1 先来解释⼀下 ,Handler 中 Message 分为 同步消息 跟 异步消息,⼀般来说 ,两种消息没什么区别,只有在设置 同步屏障的时候才
会出现差异
4.2.2 ⼀般 Message 都会有target,但是 设置同步屏障时的 Message 没有target,简⽽⾔之,同步屏障是⼀个 target为空的 Message
4.2.3 这种差异最终体现在 的时候,上⼀段代码:
Message next() {
//...
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
//...
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = Millis();
Message pre后会无期歌词 vMsg = null;
Message msg = mMessages;
if (msg != null && == nu敕勒歌的意思全解二年级 ll) { //碰到同步屏障
// Stalled by a barrier. Find the 江州司马青衫湿的上一句 next asynchronous message in the queue.
// do while循环遍历消息链表
// 跳出循环时,msg指向离表头最近的⼀个异步消息
do {
prevMsg = msg;
msg = ;
} while (msg != null && !chronous());
}
if (msg != null) {
if (now < ) {
//...
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
//将msg从消息链表中移除
= ;
} else {
乡音无改鬓毛衰上一句 mMessages = ;
}
= null;
子非鱼安知鱼之乐下一句 if (DEBUG) Log.v(TAG, \"Returning message: \" + msg);
Use();
//返回异步消息
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
//...
}
//...
}
}
可以看到,如果设置了同步屏障,会优先处理 target为空的消息(异步消息),这算是Handler消息的⼀种优先级机制
4.3.4 设置同步屏障
per().getQueue().postSyncBarrier();
4.3.5 同步屏障应⽤
Android应⽤框架中为了更快的响应UI刷新事件在leTraversals中使⽤了同步屏障
参考资料
1. 在腾讯课堂中,享学提供的课程
2. 同步屏障:/asdgbc/article/details/79148180
广播站的英文翻译英语怎么说-51talk少儿英语
更多推荐
handler是什么意思dler在线翻译读音例句
发布评论