[Android答答答]Handler是什么?
Handler是什么?
- 前言
- 1. 为什么要用Handler
- 概念
- 1.什么是Handler
- 2.什么是MessageQueque
- 3.Message是什么
- 4.Looper又是啥
- 原理
- 1. UI线程是如何创建Looper的
- 2. Handler是如何发送消息的
- 3. Looper没有消息死循环的时候,为什么不会卡死应用
- 4. 一个线程有几个handler,又有几个looper
- 5. handler线程是如何切换的
- 6. 为什么handler会有内存泄漏的风险,如何规避
- 7.如果队列中已存在消息,但需要一些消息紧急提前,该怎么做
- 未完待续
前言
1. 为什么要用Handler
我们的都知道,在Android中我们无法在子线程中去更新UI,为什么不能更新呢?我们可以在源码中可以看到
void checkThread() {...if (mThread != Thread.currentThread()) {throw new CalledFromWrongThreadException("Only the original thread that created a view hierarchy can touch its views.");}
}
我们如果同时多个线程都需要去更新UI,那么绘制UI就是一个不可靠的非安全操作。如果选择通过加锁的方式,UI绘制的效率就会变低,影响到界面的显示。所以谷歌就限制了只能主线程才能更新UI,通过handler去更新UI。
概念
1.什么是Handler
handler是通过消息队列,发送传递和处理消息的工具。
主要用于异步消息的处理:当发出一个消息之后,首先进入一个消息队列,发送消息的函数即刻返回,而另外一个部分在消息队列中逐一将消息取出,然后对消息进行处理,也就是发送消息和接收消息不是同步的处理。 这种机制通常用来处理相对耗时比较长的操作。
说白了,handler就是个运输消息的工具,将消息发送到需要处理的线程中。
2.什么是MessageQueque
MessageQueue字面意思是消息队列。
队列是一种数据结构,有着先进先出的特点。其特性很适用于像消息生产消费依次顺序的场景。
3.Message是什么
Message就是handler发送与接收消息的实体,承载了数据。
4.Looper又是啥
Looper字面意思是循环的意思,在Android中它就是一个不断循环的线程,消息在这个线程中存储和发送。
原理
1. UI线程是如何创建Looper的
Looper的创建一般是Looper.prepare(),但是像Activity中我们一直没有看到创建的方法就可以直接使用Handler发送消息了,原因是系统已经在ActivityThread的main方法为我们创建Looper了。
public static void main(String[] args) {Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");...//创建LooperLooper.prepareMainLooper();ActivityThread thread = new ActivityThread();thread.attach(false, startSeq);if (sMainThreadHandler == null) {sMainThreadHandler = thread.getHandler();}if (false) {Looper.myLooper().setMessageLogging(newLogPrinter(Log.DEBUG, "ActivityThread"));}// End of event ActivityThreadMain.Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);//开启循环Looper.loop();throw new RuntimeException("Main thread loop unexpectedly exited");}
2. Handler是如何发送消息的
我们直接看sendMessage源码,从sendMessage开始,依次调用sendMessageDelayed,sendMessageAtTime,再到队列的enqueueMessage方法。
public final boolean sendMessage(@NonNull Message msg) {return sendMessageDelayed(msg, 0);}
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {if (delayMillis < 0) {delayMillis = 0;}return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);}
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {MessageQueue queue = mQueue;if (queue == null) {RuntimeException e = new RuntimeException(this + " sendMessageAtTime() called with no mQueue");Log.w("Looper", e.getMessage(), e);return false;}return enqueueMessage(queue, msg, uptimeMillis);}
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,long uptimeMillis) {//handler与message绑定msg.target = this;msg.workSourceUid = ThreadLocalWorkSource.getUid();if (mAsynchronous) {msg.setAsynchronous(true);}return queue.enqueueMessage(msg, uptimeMillis);}
最后看队列Queue中的enqueue方法
boolean enqueueMessage(Message msg, long when) {...synchronized (this) {...msg.markInUse();//标记message加入queue的时间msg.when = when;//当前待处理的消息Message p = mMessages;boolean needWake;if (p == null || when == 0 || when < p.when) {// New head, wake up the event queue if blocked.//添加新的消息msg.next = p;mMessages = msg;needWake = mBlocked;} else {...}}return true;}
这个过程就是入队,将消息根据时间顺序指定下一个消息。
出队的过程还是看源码,在Looper的loop循环中,最终找到dispatchMessage方法。
public void dispatchMessage(@NonNull Message msg) {if (msg.callback != null) {//callback在message的构造方法中初始化或者使用handler.post(Runnable)时候不为空handleCallback(msg);} else {mCallback是一个Callback对象,通过无参的构造方法创建出来的handlerif (mCallback != null) {if (mCallback.handleMessage(msg)) {return;}}//当mCallback为空时,直接执行handleMessage将msg发送出去handleMessage(msg);}}
3. Looper没有消息死循环的时候,为什么不会卡死应用
当主线程的MessageQueue没有消息的时候,代码会阻塞在Looper的mQueue的next
方法那里,这个时候主线程会释放cpu资源,进入睡眠模式,直到下个消息到达会再次唤醒主线程。这套机制采用的是linux的pipe/epoll机制。通过往pipe管道中写入数据来唤醒主线程工作。原理类似于I/O,读写是阻塞的,不占用CPU资源。
4. 一个线程有几个handler,又有几个looper
一个线程可以有N个handler,因为可以不断的new出来新的。
一个线程中只有一个Looper。
Looper类创建时在源码中对内存中的Looper进行非空判断,仅限制一个Looper存在。
public static void prepare() {prepare(true);}private static void prepare(boolean quitAllowed) {if (sThreadLocal.get() != null) {throw new RuntimeException("Only one Looper may be created per thread");}sThreadLocal.set(new Looper(quitAllowed));}
ThreadLocal是用来存储Looper的,在Looper这个类创建的时候就new出来了。ThreadLocal 提供了针对于单独的线程的局部变量,并能够使用 set()、get() 方法对这些变量进行设置和获取,并且能够保证这些变量与其他线程相隔离。通过使用 threadLocal 存储对象,线程和线程之间的彼此的数据就会隔离起来,从而保证了彼此线程的数据安全和独立性。
5. handler线程是如何切换的
线程之间的资源是共享的,也就是任何变量在任何线程都是可以修改的。切换线程的过程就是整个handler的运行过程。
总结来说就是三个步骤
a. 创建一个Looper对象保存在ThreadLocal中,同时持有一个MessageQueue对象
b. 创建Handler对象,获取到Looper和MessageQueue对象。在sendMessage的时候,在不同的线程(子线程)将消息发送到MessageQueue。
c. 主线程(UI线程)通过Looper.loop()无限循环获取MessageQueue中的消息,如果存在就将消息通过dispatchMessage方法最终调用到我们写的handleMessage方法中。这样一整个线程切换的过程就完成了。
6. 为什么handler会有内存泄漏的风险,如何规避
先看张截图,我们一般在Acitivty中new Handler的时候,会出现一个黄色的背景提示。
大致翻译过来就是handler是内部类,会引起内存泄漏。如果是handler的MessagQueue和Looper是子线程就没问题。主线程的话需要定义成静态内部类和使用弱引用。
那么为什么要这样使用呢,原因就是如果handler在主线程创建,如果发送的消息还在队列中,若此时Activity突然finish了,但是handler中的消息依然需要处理。最关键的就是非静态内部类天然持有外部类的引用,导致activity无法回收,引起内存泄漏。
定义成静态内部类,就不会持有外部类的引用,所以也不会有问题。
除此之外,由于是静态类,在handler中就无法操作activity对象,所以需要在handler中增加一个activty的弱引用。
static class MyHandler extends Handler {WeakReference<Activity > mActivityReference;MyHandler(Activity activity) {mActivityReference= new WeakReference<Activity>(activity);}@Overridepublic void handleMessage(Message msg) {final Activity activity = mActivityReference.get();if (activity != null) {text.setText(result);}}
}
7.如果队列中已存在消息,但需要一些消息紧急提前,该怎么做
这里需要引出一个概念叫同步屏障。线程的消息都是放到同一个MessageQueue中的,取消息是互斥取消息,而且只能从头部取消息,添加消息是按照消息的执行的先后顺序进行的排序,同一时间内的消息如果想要立马执行,就需要一个紧急通道,哪个通道就是同步屏障的概念。
同步屏障顾名思义就是阻碍同步消息,只让异步消息进入。
开启同步屏障的方法: MessageQueue#postSyncBarrier()
未完待续
[Android答答答]Handler是什么?相关推荐
- 滴答滴答滴答滴答滴答滴答滴答滴答滴答
滴答滴答滴答滴答滴答滴答滴答滴答滴答滴答滴答滴答滴答滴答滴答滴答滴答滴答答滴答滴答滴答滴答滴答滴答滴答滴答滴答滴答滴答滴答滴答滴答滴答滴答滴答滴答答滴答滴答滴答滴答滴答滴答滴答滴答滴答滴答滴答滴答滴答 ...
- Android开发之通过Handler的post方法更新UI
在Android中可以通过handler方法完成数据的线程间的传递,但一定要将handler得到的数据通过loop传递到主线程再更新UI吗?其实也可以直接使用handler设计的post方法进行实现, ...
- android中AsyncTask和Handler对比
1 ) AsyncTask实现的原理,和适用的优缺点 AsyncTask,是android提供的轻量级的异步类,可以直接继承AsyncTask,在类中实现异步操作,并提供接口反馈当前异步执行的程度(可 ...
- Android之——AsyncTask和Handler对照
转载请注明出处:http://blog.csdn.net/l1028386804/article/details/46952835 AsyncTask和Handler对照 1 ) AsyncTask实 ...
- android打地鼠设计报告,android开发中利用handler制作一个打地鼠小游戏
android开发中利用handler制作一个打地鼠小游戏 发布时间:2020-11-25 15:21:11 来源:亿速云 阅读:136 作者:Leah 这期内容当中小编将会给大家带来有关androi ...
- android 静态方法 构造方法,Android通过静态内部类构建Handler提示构造方法过时
Android中通常使用Handler来进行不同线程间的通讯以及消息的异步处理,但在定义Handler时,为防止出现内存泄漏风险,最好的方式是通过构建静态内部类实现. private MyHandle ...
- android版自动答录机,手机android自动答录机|自动答录机安卓版下载 v2.4.6.2 - 跑跑车安卓网...
一款专为手机通话打造的自动答录机,通过设置安卓自动答录机可以自动为来电人播放录制的提示音并留言,android自动答录机还可自动录制来电者的留言,电话神器,赶紧get起来吧. 软件介绍 功能1 设定时 ...
- android 左滑右滑,Android仿滴答清单左滑右滑效果
直接上效果图 记录仿写滴答清单App 过程中的技术点 本文分为以下章节,读者可按需阅读: 1.自定义RecycleItemTouchHelper 2.实现滴答清单左滑右滑效果 3.RecycleVie ...
- Android自问自答系列——持续更新ING
Hello,All,我是来自58同城的一名Android开发工程师,在58集团从事APP的开发工作.在日常的工作和学习过程中我经常会碰到一些好玩的和有意思的Android小知识点,有些知识可能都从未注 ...
- Android多线程:深入分析 Handler机制源码(二)
前言 在Android开发的多线程应用场景中,Handler机制十分常用 接下来,深入分析 Handler机制的源码,希望加深理解 目录 1. Handler 机制简介 定义 一套 Android 消 ...
最新文章
- [管理心得] 稻盛和夫为日航危机出诊--人情营销的典型
- [BetterExplained]书写是为了更好的思考
- Nginx的正向代理与反向代理
- Apache RocketMQ 4.8.0,DLedger 模式全面提升!
- 使用Maven管理Eclipse Java项目(多modules编译)
- oracle笔记整理2
- Jira 随便总结
- 《剑指Offer》52:两个链表的第一个公共节点
- 企业实战_21_MyCat_keepalived 安装配置验证
- FaceBoxes的学习笔记
- 亚马逊出的平板电脑_亚马逊发布Fire HD 8新系列平板电脑,90美元起
- SM2258XT数据恢复全解,慧荣SM2258XT主控数据恢复详细教程,SM2259XT可参考
- python全栈之路—十分钟搞定面向对象-类的结构-类的空间问题,建议收藏
- 强势破圈!时尚COSMO联合百度与小红书发布中国美妆地图、美妆种草度白皮书
- 删除xp计算机用户账户,XP系统怎么删除多余的用户帐号?XP系统删除多余用户帐号的方法...
- Qt编写小清新风格界面
- Python简单浪漫表白代码鲜花
- 获取当前日期是今年的第几周
- 关于前几天的招聘,我说几点
- linux通过手机热点上网耗流量,电脑通过手机热点上网,是不是比手机用流量更费一些?...