换个姿势,带着问题看Handler(上)

简介: Handler,老生常谈,网上关于它的文章可谓是 "泛滥成灾",而实际开发中,我们却很少手写Handler,毕竟 RxAndroid链式调用 和 Kotlin协程同步方式写异步代码 还是挺香的。但对于我这种好刨根问底之人来说,得自己过一遍源码才踏实,而且我发现 带着问题 看源码,思考理解本质,印象更深,收获更多,遂有此文。

罗列下本文提及的知识点,按需阅读即可:


  • 1、Handler问题三连:是什么?有什么用?为什么要用,不用行不行?


  • 2、Android UI更新机制(GUI) 为何设计成了单线程的?


  • 3、真的只能在主(UI)线程中更新UI吗?


  • 4、真的不能在主(UI)线程中执行网络操作吗?


  • 5、Handler怎么用?


  • 6、为什么建议使用Message.obtain()来创建Message实例?


  • 7、为什么子线程中不可以直接new Handler()而主线程中可以?


  • 8、主线程给子线程的Handler发送消息怎么写?


  • 9、HandlerThread实现的核心原理?


  • 10、当你用Handler发送一个Message,发生了什么?


  • 11、Looper是怎么拣队列里的消息的?


  • 12、分发给Handler的消息是怎么处理的?


  • 13、IdleHandler是什么?


  • 14、Looper在主线程中死循环,为啥不会ANR?


  • 15、Handler泄露的原因及正确写法


  • 16、Handler中的同步屏障机制


  • 17、Android 11 Handler相关变更


0x1、Handler问题三连


1.Handler是什么


答:Android Framework 架构中的一个 基础组件,用于 子线程与主线程间的通讯,实现了一种 非堵塞的消息传递机制


2.Handler有什么用


答:把子线程中的 UI更新信息传递 给主线程(UI线程),以此完成UI更新操作。


3.为什么要用Handler,不用行不行


答:不行,Handler是android在设计之初就封装的 一套消息创建、传递、处理机制。

Android要求:在主线程(UI线程)中更新UI,注意,是要求,并不是规定,你不听,硬是要:在子线程中更新UI,也是可以的,比如在子线程中创建Dialog:


网络异常,图片无法展示
|


运行后:


网络异常,图片无法展示
|


没有报错,对话框正常弹出,而我们平时在 子线程中更新UI 的报错信息是这样的:


网络异常,图片无法展示
|


  • 异常翻译:只有创建视图层次结构的原始线程才能触摸其视图


  • 引起原因:在子线程中直接更新主线程创建的UI;


  • 也就是说:子线程更新UI也行,但是只能更新子线程创建的View;


  • 换句话说:Android的UI更新(GUI)被设计成了单线程


你可能会问,为啥不设计成多线程?


答:多个线程同时对同一个UI控件进行更新,容易发生不可控的错误!


那么怎么解决这种线程安全问题?


答:最简单的处理方式——加锁,不是加一个,是每层都要加锁(用户代码→GUI顶层→GUI底层...)但这样也意味着更多的 耗时,从而导致UI更新效率降低,界面卡顿等。


而如果每一层共用一把锁的话,其实就是 单线程,所以,最后的结论是:


Android没有采用「线程锁」,而是采用「单线程消息队列机制」,实现了一个「伪锁


这个疑问解决了,再说一个网上很常见的主线程更新UI的例子:


网络异常,图片无法展示
|


上面这段代码 直接在子线程中更新了UI,却没有报错:


网络异常,图片无法展示
|


打脸?莫慌,在子线程代码块中加一句休眠模拟耗时操作:


网络异常,图片无法展示
|


程序就崩溃了,报错日志如下:


网络异常,图片无法展示
|


前面说了 Android的UI更新被设计成单线程,但为何在添加休眠后才报错,限于篇幅,不跟源码,直接说原因:


  • ViewRootImponCreate() 调用时还没创建;


  • onResume() 时,即 **ActivityThread.handleResumeActivity()**执行后才创建;


  • 调用 View.requestLayout(),最终调到 ViewRootImpl.requestLayout(),走到**checkThread()**报错;


可以打个日志简单的验证下:


网络异常,图片无法展示
|


加上休眠


网络异常,图片无法展示
|


行吧,以后去面试别人问「子线程是不是一定不可以更新UI」别傻乎乎地点头说是了。


4.引生的另一个问题


说到「只能在主线程中更新UI」我又想到另一个问题「不能在主线程中进行网络操作


网络异常,图片无法展示
|


上述代码运行直接闪退,日志如下:


网络异常,图片无法展示
|


NetworkOnMainThreadException:网络请求在主线程进行异常。


em... 真的不能在主线程中做网络操作吗?


onCreate() 的 setContentView() 后插入下面两句代码:


网络异常,图片无法展示
|


运行下看看:


网络异常,图片无法展示
|


这...又打脸?先说下 StrictMode(严苟模式)


Android 2.3 引入,用于检测两大问题:ThreadPolicy(线程策略) 和 VmPolicy(VM策略)


相关方法如下


网络异常,图片无法展示
|


把严苟模式的网络检测关了,就可以 在主线程中执行网络操作了,不过一般是不建议这样做的:


在主线程中进行耗时操作,可能会导致程序无响应,即 ANR (Application Not Responding)。


至于常见的ANR时间,可以在对应的源码中找到:


// ActiveServices.java → Service服务
static final int SERVICE_TIMEOUT = 20*1000;     // 前台
static final int SERVICE_BACKGROUND_TIMEOUT = SERVICE_TIMEOUT * 10;     // 后台
// ActivityManagerService.java → Broadcast广播、InputDispatching、ContentProvider
static final int BROADCAST_FG_TIMEOUT = 10*1000;    // 前台
static final int BROADCAST_BG_TIMEOUT = 60*1000;    // 后台
static final int KEY_DISPATCHING_TIMEOUT = 5*1000;  // 关键调度
static final int CONTENT_PROVIDER_PUBLISH_TIMEOUT = 10*1000;    // 内容提供者


时间统计区间:


  • 起点System_Server 进程调用 startProcessLocked() 后调用 AMS.attachApplicationLocked()


  • 终点Provider 进程的 **installProvider()**及 publishContentProviders() 调用到  AMS.publishContentProviders()

超过这个时间,系统就会杀掉 Provider 进程。


相关文章
|
6月前
|
安全 Android开发 开发者
【Android开发小技巧】扔掉这坑人的 Handler
【Android开发小技巧】扔掉这坑人的 Handler
75 0
|
Java Android开发
Handler 中的奥秘
Handler 中的奥秘
51 0
|
安全 Android开发 开发者
【Android开发小技巧】扔掉这坑人的 Handler
大家都知道 Handler 特别坑,使用不当会造成各种问题,使用 Kotlin Coroutines + Lifecycle 可以很好地替代 Handler。
829 0
对于async和await的使用方式、作用效果不怎么理解 ?没关系,初步看这篇就够了
对于async和await的使用方式、作用效果不怎么理解 ?没关系,初步看这篇就够了
|
Serverless C语言 Python
学编程这么久,还傻傻分不清什么是方法(method),什么是函数(function)?
在标准库inspect 中,它提供了两个自省的函数,即 ismethod() 和 isfunction(),可以用来判断什么是方法,什么是函数。
316 0
学编程这么久,还傻傻分不清什么是方法(method),什么是函数(function)?
|
消息中间件 算法 Java
换个姿势,带着问题看Handler(下)
Handler,老生常谈,网上关于它的文章可谓是 "泛滥成灾",而实际开发中,我们却很少手写Handler,毕竟 RxAndroid链式调用 和 Kotlin协程同步方式写异步代码 还是挺香的。但对于我这种好刨根问底之人来说,得自己过一遍源码才踏实,而且我发现 带着问题 看源码,思考理解本质,印象更深,收获更多,遂有此文。
258 0
|
消息中间件 安全 Java
换个姿势,带着问题看Handler(中)
Handler,老生常谈,网上关于它的文章可谓是 "泛滥成灾",而实际开发中,我们却很少手写Handler,毕竟 RxAndroid链式调用 和 Kotlin协程同步方式写异步代码 还是挺香的。但对于我这种好刨根问底之人来说,得自己过一遍源码才踏实,而且我发现 带着问题 看源码,思考理解本质,印象更深,收获更多,遂有此文。
139 0
|
存储 消息中间件 安全
Handler二十七问|你真的了解我吗?(上)
对于handler,你会想到什么呢?
137 0
Handler二十七问|你真的了解我吗?(上)
|
消息中间件 安全 Android开发
Handler二十七问|你真的了解我吗?(下)
对于handler,你会想到什么呢?
123 0
|
消息中间件 Android开发
【Android 异步操作】手写 Handler ( Handler 发送与处理消息 | Handler 初始化 | 完整 Handler 代码 )
【Android 异步操作】手写 Handler ( Handler 发送与处理消息 | Handler 初始化 | 完整 Handler 代码 )
151 0