Handler面试题全解析,看完表示Handler彻底懂了
Posted 初一十五啊
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Handler面试题全解析,看完表示Handler彻底懂了相关的知识,希望对你有一定的参考价值。
前言
做android开发的肯定离不开跟Handler打交道,它通常被我们来做主线程与子线程之间的通信工具,而Handler作为Android中消息机制的重要医院也确实给我们的开发带来了极大的便利。
可以说只要有异步线程与主线程通信的地方就一定有Handler。在面试中Handler也是常常被问到的一个点,哪现在就介绍一下关于Handler的重要知识点。
1.一个线程有几个Looper
?几个Handler
?
一个Thread
只能有一个Looper
,一个MessageQueen
,可以有多个Handler
以一个线程为基准,他们的数量级关系是:Thread
(1)-Looper
(1)-MessageQueen
(1)-Handler
(1)
2.Handler
内存泄漏原因?以及解决方案
泄漏原因:
Handler
允许我们发送延迟消息,如果在延迟期间用户关闭了Activity
,那么改Activity
会泄漏。这个泄漏是因为Message
会持有Handler
,而又因为Java
的特性,内部类会持有外部类,使得Activity
会被Handler
持有,这样最终就会导致Activity
泄漏。
*解决方案:
- 最直接的思路就是避免使用非静态内部类,使用
Handler
的时候,放在一个新建的文件中来继承Handler
或者使用静态的内部类来替代。静态内部类不会隐含的持有外部类的引用,因为这个Activity
也就不会出现内存泄漏的问题。 - 如果你需要在
Handler
内部调用外部activity
的方法,你可以让这个Handler
持有这个activity
的弱引用,这样便不会出现内存泄漏的问题了。 - 另外,对于匿名类
Runnable
,我们同样可以设置成静态的,因为静态内部类不会持有外部类的引用。 - 注意:如果使用
Handler
发送循环消息,最好是在Activity
的OnDestroy
方法中调用meleakHandler.removecallbacksAndMessages(null)
;移除消息。(这不是解决内存泄漏的方案) - 两种解决方法如下:弱引用和静态
- 弱引用(
WeakReference
)
- 弱引用(
public class SampleActivity extends Activity
/**
*Instances of static inner classes do not hold an implicit
*reference to their outer class.
*弱引用的方法
*/
private static class MyHandler extends Handler
private final WeakReference <SamleActivity>
mActivity;Public MyHandler(SampActivity activity)
mActivity = new WeakReference<sampleActivity>(activity)
@Override
public void handleMessage(message msg)
SamleActivity activity = mActivity.get();
if(activity ! = null)
//to something
- 静态
//定义成static的,因为静态内部类不会持有外部类的引用private
final MyHandler mHandler = new MyHandler(this); private static
final Runnable sRunnable = new Runnable()
@Override
public void run()
//to something
@Override
protected void onCreate(Bundle savedINstanceState)
super.onCreate(savedInstancestate);
mHandler.postDelayed(sRunnable,1000*60*10);
finish();
3.为何主线程可以new Handler
如果想要在子线程中new Handler
要做些什么准备?
每一个Handler
必须要对应一个Looper
主线程就会自动创建Looper
对象,不需要我们手动创建,所以主线程可以直接创建Handler
在new Handler
的时候没有传入制定的Looper
就会默认绑定当前创建Handler
的线程的Looper
,如果没有Looper
就报错。
因为在主线程中,Activity
内部包含一个Looper
对象,它会自动管理Looper
处理子线程中发送过来的消息。而对于子线程而言,没有任何对象帮助我们维护Looper
对象,所以需要我们手动维护。
所以要在子线程开启Handler
要先创建Looper
,并开启Looper
循环
如果在子线程中创建一个Handler
,那么就必须做三个操作
1.Prepare();
2.loop();
3.quit();
4.子线程中维护的Looper
,消息队列无消息的时候的处理方案是什么?有什么用?
在Handler
机制里面有一个Looper
,在Looper
机制里面有一个函数,叫做quitsafely()
和quit()
函数,而这两个函数是调用的MessageQueue的quit()
/**
* Quits the looper.
* <P>
* Causes the(@link #loop)method to terminate without processing any
* more messages in the message queue
* </P><P>
*Any attempt to post message to the queue after the looper is asked to quit will fail.
* Four example,the@link Handler#sendMessage(Message) method will return false
* </P><p class = "note">
* using this method may be unsafe because some messages may not be delivered
* before thelooper terminates. Consider using @link #quiSafely instead to ensure
* that all pending work is completed in an orderly manner.
* </P>
* @see #quitSafely
*/
public void quit()
mQueue.quit(false);
/**
* Quits the looper safely.
* <P>
* Causes the @link #loop method to terminate as soon as all remaining messages
* in the messages queue that are already due to be delivered have been handled.
* However pending delayed messages with due times in the future will not be
* delivered before the loop terminates.
* </P><P>
* Any attempt to post messages to the queue after the looper is asked to quit will fail.
* For example,the @link Handler#sendMessage(Message) method will return false.
* </p>
*/
public void quitSafely()
mQueue.quit(true);
再进入到MessageQueue
的quit()
函数
void quit (boolean safe)
if (!mQuitAllowed)
throw new IlllegalstateException(''Main thread not allowed to quit.'');
synchronized (this)
if (mQuitting)
return
mQutting = true;
if (safe)
removeAllFutureMessagesLocked():
else
// we can assume mptr ! = 0 because mQuitting was previously false.
nativeWake(mptr);
它会remove
消息,把消息队列中的全部消息给干掉。把消息全部干掉,也就释放了内存
private void removeAllfutureMessagesLocked()
final long now = systemclock.uptimeMIllis();Message p =mMessages;
if (P ! = null)
if (p.when > now)
removeAllMessagesLocked()
else
Message n;
for ()
n = p.next;
if (n = =null)
ruturn
if (n.when > now)
break;
p = n;
p.next = null;
do
p = n;
n =p.next;
p.recycleUnchecked();
while (n ! =null);
而在quit()
函数的最后一行,有一个nativeWake()
函数
// we can assum mptr ! = 0 because mQuitting was previously false.
nativewake(mptr);
这个函数的调用,就会叫醒等待的地方,醒来之后,就接着往下执行。
// native的方法,在没有小的的时候回阻塞管道读取端,只有nativepollonce返回之后才能往下执行
//阻塞操作,等待nextpollTimeoutMillis时长
nativepollonce(ptr, nextpolltimeoutmillis);
往下执行后,发现Message msg = mMessages;
是空的,然后就执行了这个,就往下走。
if (msg ! = null)
......
else
// No more messages.
//没有消息,nextPollTimeoutMillis 复位
nextpollTimeoutmillis = - 1;
然后又调用了这个方法,并且return
了null
// process the quit message now that all pending messages have been handled.
//如果消息队列正在处于退出状态返回null,调用dispose();
释放改消息队列
if (mQuitting)
dispose()
return null;
5.既然可以存在多个Handler
往MessageQueue中添加数据(发消息时各
Handler`可能处于不同线程),哪它内部是如何确保线程安全的?
这里主要关注MessageQueue
的消息存取即可,看源码内部的话,在往消息队列里面存储消息时,会拿当前的MessageQueue
对象作为锁对象,这样通过加锁就可以确保操作的原子性和可见性了。
消息的读取也是同理,也会拿当前的MessageQueue
对象作为锁对象,来保证多线程读取的一个安全性。
6.我们使用Message
时应该如何创建它
创建的是方式有两种:
一种是直接new
一个message
对象,另一种是通过调用Message.obtain()
的方式去复用一个已经被回收的message
,当然日常使用者是推荐使用后来着拿到一个Message
因为不断的去创新对象的话,可能会导致垃圾回收区域中新生代被沾满,从而触发GC。
Message
中的spool
就是用来存放被回收的message
,当我们调用obtain
后,会先查看是否有可复用的对象,如果真的没有才回去创建一个新的Message
对象.
补充:主要的Message
回收时机是:
- 在MQ中
remove Message
后; - 单次
loop
结束后 - 我们主动调用
message
的recycle
方法后
7.Looper
死循环为什么不会导致应用致死?
launch
桌面的图标第一次启动Activity
时,最终走到ActivityThread
的main
方法,在main
方法里面创建Looper
和messageQueue
处理主线程的消息,然后Looper.loop()
方法进入死循环,我们的Activity
的生命周期都是通过Handler
机制处理的,包括oncreat
,onResume
方法,下面是loop
方法循环。
主线程的方法就是消息循环,一但退出消息循环,那么你的应用也就退出了,looper.loop()
方法可能会引起主线程的阻塞,但只要它的消息循环没有被阻塞,就能一直处理事件就不会产生ANR
异常。
造成ANR
的不是主线程阻塞,而是主线程的Looper
消息处理过程发生了任务阻塞,无法响应手势操作,不能及时刷新UI
阻塞与程序无响应没有必然关系,虽然主线程在没有消息可处理的时候是阻塞的,但是只要保证有消息的时候就能够立刻处理,程序是不会无响应的。
总结:应用卡死压根与这个Looper
没有关系,应用在没有消息需要处理的时候,他是在睡眠,释放线程,卡死是ANR
而Looper
是睡眠。
如果你觉得以上的内容,学完还有点意犹未尽?毕竟学无止境。那么还能即时领取一份,【(视频+文字)腾讯最全面Android高级学习笔记】,内容如下:
包含(视频+文字):Android基础-性能优化-Framework-compose开源项目-音视频初中高-架构-车载-Flutter-Kotlin-Harmony OS+音视频详细文档。
一丶T10级Android工程师必备基础技能
涉及: 注解、泛型、Retrofit
、架构动态编程技术原理、Butterknife
、JavaSSit
丶虚拟机丶并发内存丶Synchronize
丶并发原理之线程池丶数据序列化
二丶T10工程师核心优化能力
涉及: 启动优化、内存优化、启动优化速度、卡顿优化、布局优化、崩溃优化、应用启动全流程(源码深度解析)等内容.
三丶Framework
涉及:Framework
通信、Framework
底层服务、Framework
事件机制等内容.
四丶Compose(开源项目)
五.音视频开发
涉及:C和C++基础语法,H264
编码基础和进阶,H265
编码原理和应用MediaCodec
硬解码,Media
内核源码,WeChat
视频通话。初级-中-高
附带音视频开发预习资料文档
涉及:视频区,视频压缩,音视频同步,FFmpeg
,OPenGL
,OpenSL ES
,抖音美颜滤镜,交叉编译,视频变速,FFmpeg
实现音视频同步
六丶Android资深架构师
涉及:Arraylist
,Okhttp
,Retrofit
,图片加载,Dagger 2
,MVC
.MVP
.MVVM
,Jetpack Room
七丶Android车载工程师
涉及:Android Auto
,汽车媒体应用,构建Android Auto
即时通信应用,构建车载导航和地图注点应用,构建Android Automotive OS
视频应用,测试Android
车载应用,分发Android
汽车应用,适用于汽车的Google Play
服务,Android Automotive OS
的通知.
八丶Flutter高级工程师
涉及:Dart
语法,Flutter
动画丶组件丶网络请求以及Flutter3.0
简介。
九.Harmony OS
涉及:Ability
组件,分布式任务,事件总线,Harmony OS
线程,UI
自定义控件
十丶Kotlin相关
涉及:对象丶类丶继承丶变量丶常量丶拓展函数等20多个内容**
以上是关于Handler面试题全解析,看完表示Handler彻底懂了的主要内容,如果未能解决你的问题,请参考以下文章