内容简介:上文我们聊了Handler机制出现的道理,以及消息队列的写法。 今天我们来聊一聊上文中还未作出解答的问题。作为死循环的loop,如何做到不阻塞住我们的生命周期。首先,我们得弄清死循环的意义。我们都明白,对于线程来说。代码的结束也就意味的线程生命的终止。想要获得“永生”,那么该怎么做?很显然,只要代码永远执行不完就可以了。既然如此,那么死循环显然非常符合这个要求:这样我们就弄清楚了loop()死循环的意义之一。 但是随之而来,引入了全新的问题:既然是死循环,那么循环体之外的代码势必不会被执行。
上文我们聊了Handler机制出现的道理,以及消息队列的写法。 今天我们来聊一聊上文中还未作出解答的问题。作为死循环的loop,如何做到不阻塞住我们的生命周期。
正文
一、死循环
首先,我们得弄清死循环的意义。我们都明白,对于线程来说。代码的结束也就意味的线程生命的终止。想要获得“永生”,那么该怎么做?很显然,只要代码永远执行不完就可以了。既然如此,那么死循环显然非常符合这个要求:
while(true){ // 我是不朽的~! } 复制代码
这样我们就弄清楚了loop()死循环的意义之一。 但是随之而来,引入了全新的问题:既然是死循环,那么循环体之外的代码势必不会被执行。
为了能够正常的运行程序,我们就要想办法在循环体内调度其他方法。start其他线程,通过消息队列轮询。这显然是一个不错的方案,主线程的循环体只需要源源不断的从消息队列中取消息,执行就可以了。至于消息从哪里来,从哪个线程来这个不重要,一点都不重要。
Handler机制的一个作用就出来了:实现不同线程间的数据传递(这里传递是Message,其他线程往MessageQueue发送Message,主线程轮询取出并执行)。
有了这个思想,我们来想想对于Android来说是一个怎样的实现。
1.1、Android中的死循环
显然ActivityThread中,main方法里调用的loop()就是这个死循环体。
public static void loop() { //省略部分代码 for (;;) { //省略部分代码 } } 复制代码
有了上边的分析,我们就能够明白:
- 1、它的存在保证了我们主线程的不死不灭。
- 2、而Handler机制保证了我们线程之间消息的调度。
死循环的意义我们明白了。那么死循环为什么阻塞不住我们的UI线程?一句话先来概括一下:
真正会卡死主线程的操作是,在onCreate/onStart/onResume等这种生命周期方法中执行了耗时操作,而非loop死循环。 只要我们组件的生命周期正常被回调,那么loop死不死循环,对我们没有任何影响。因为我们的代码逻辑全部是在对应的生命周期中执行了。
那么问题就来了:**组件的生命周期方法是怎么被回调的?**回答这个问题之前,让我们好好捋一捋Handler机制的设计:
1.2、Handler机制的思路
主线程持有MessageQueue,loop()死循环从MessageQueue中不断的取出Message,不为null,就执行Message中对应的方法。其他线程通过LocalThread拿到MessageQueue,进而就可以往Queue中发送Message。
有了这个思路其实我们就可以回答这个问题: 组件的生命周期方法是怎么被回调的?
答案是:只需要在生命周期方法该被回调的时候,在 其他线程 中,通过往MessageQueue中发送Message的形式,就可以告知主线程,该回调对应的生命周期方法了。
当然用文字表达很简单。实际落实到代码中就涉及很多内容了。因此接下来让我们真正的从代码中看生命周期方法是如何被调度的。
二、生命周期的调度
2.1、简述流程
要想弄清楚生命周期的调度,这里不得不提起一个名词:Binder机制。而且这其中还涉及到了我们一定耳濡目染过的流程,先看一张图:
对于上图来说。进程俩端,一个是Binder客户端一个是Binder服务端。简单来说,服务端用于响应客户端的调用。
因此对于上图来说,App默认进程中的ApplicationThread就是Binder的客户端,用于响应system_server进程中Binder客户端ApplicationThreadProxy的调用。
同理,system_server进程中Binder客户端ActivityManagerService就是响应App默认进程中Binder客户端的ActivityManagerProxy对象的调用。
2.2、Handler + Binder
在2.1中我们用一张图一段话聊了Binder机制。有了这个基础,我们就可以在结合Handler机制,解释一下我们的生命周期是如何被调度的了。
对于Activity来说,它的生命周期一定是在主线程中被回调。那么此过程就是通过Handler机制的方式实现的。我们简单上一下ActivityThread中的一段代码,后文会着重分析:
private class H extends Handler { // 省略部分代码 public void handleMessage(Message msg) { switch (msg.what) { case RESUME_ACTIVITY: handleResumeActivity((IBinder) msg.obj, true, msg.arg1 != 0, true); break; } } } 复制代码
最开始我们分析了,Handler的轮询在loop这个死循环里。想要保证源源不断的Message那么势必要启动额外的线程。而我们的ActivityManagerProxy就是额外的线程。它在loop()调用前被启动:
Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread(); //建立Binder通道,也就是创建新线程 thread.attach(false); Looper.loop(); 复制代码
简单来说,比如我们想要执行某Activity的onResume()方法。流程是这样的:
用文字简单解释一下: 通过 ActivityManagerProxy (App默认进程),通过Binder传输到 ActivityManagerService 中, ActivityManagerService 再通过同进程的 ApplicationThreadProxy (system_server进程)通过Binder传输到 ApplicationThread (App默认进程)之中,此时已经来到了我们的App默认进程中的BInder线程中。那么此时就可以通过Handler机制,将Message发送给主线程了,再由主线程找到对应的Activity实例,回调对应的onResume方法。
三、Read F**k Code
3.1、Binder线程的启动
我们先来看一下上文提到的attch方法:
private void attach(boolean system) { final IActivityManager mgr = ActivityManager.getService(); try { mgr.attachApplication(mAppThread); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } } 复制代码
IActivityManager是IActivityManager,aidl的接口类。那它具体的对象是谁?
public static IActivityManager getService() { return IActivityManagerSingleton.get(); } private static final Singleton<IActivityManager> IActivityManagerSingleton = new Singleton<IActivityManager>() { @Override protected IActivityManager create() { final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE); final IActivityManager am = IActivityManager.Stub.asInterface(b); return am; } }; 复制代码
很明显我们需要追到ServiceManager中一探究竟:
private static HashMap<String, IBinder> sCache = new HashMap<String, IBinder>(); public static IBinder getService(String name) { // 省略部分代码 IBinder service = sCache.get(name); } 复制代码
我们可以看到这里通过HashMap去查多赢的IBinder对象。那我们就来看看这个key为Context.ACTIVITY_SERVICE的value是谁:
ActivityManagerService{ // 省略部分代码 ServiceManager.addService(Context.ACTIVITY_SERVICE, this, true); } 复制代码
那么此时我们也就知道IActivityManager 对象具体是谁了,真相只有一个ActivityManagerProxy。因为 IActivityManager.Stub.asInterface(b)
会根据当前进程情况来决定是返回Stub对象还是Proxy对象。我们的AMS运行在另一个进程那么此时返回的就是Proxy对象。
此时,我们的额外的线程就创建完毕了。有了这个线程我们就可以来在loop外去相应四大组件的生命周期回调了。
3.2、H
因为本篇的重点是Handler里,所以这里跳过Binder的过程,我们直接假设,ApplicationThreadProxy通过Binder调用了我们的ApplicationThread对象。
private class ApplicationThread extends IApplicationThread.Stub { // 省略部分代码 public final void scheduleResumeActivity(IBinder token, int processState, boolean isForward, Bundle resumeArgs) { // 省略部分代码 sendMessage(H.RESUME_ACTIVITY, token, isForward ? 1 : 0, 0, seq); } } 复制代码
此时我们可以很清晰的看到,在 scheduleResumeActivity
方法中,使用了sendMessage方法,很明显这是一个封装的方法。让我们进去看那一看:
private void sendMessage(int what, Object obj, int arg1, int arg2, int seq) { if (DEBUG_MESSAGES) Slog.v( TAG, "SCHEDULE " + mH.codeToString(what) + " arg1=" + arg1 + " arg2=" + arg2 + "seq= " + seq); Message msg = Message.obtain(); // 省略部分代码 mH.sendMessage(msg); } 复制代码
这里我们能够看到一个特别的Handler实现类H。说白了就是主线程中声明的一个Handler而已
private class H extends Handler { // 省略部分代码 public void handleMessage(Message msg) { switch (msg.what) { case RESUME_ACTIVITY: handleResumeActivity((IBinder) msg.obj, true, msg.arg1 != 0, true); break; } } } 复制代码
此时我们就能够看到,我们Activity的生命周期完成了调用。
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) { ActivityClientRecord r = mActivities.get(token); // 省略部分代码 r = performResumeActivity(token, clearHide, reason); } 复制代码
四、总结
我所疑惑的点,无非是下面这几个。现在已经解释的通了。
4.1、loop死循环的意义
对于线程来说,代码执行结束,线程消失。那么对于主线程来说,想要不死,死循环首选。
4.2、死循环如何保证声明周期的调度
这里主要是通过Binder机制。在loop被调用前,会启动一个Binder线程。当我们想要调用某个组件的生命周期方法时,通过Binder机制,交由ActivityManagerService(AMS)去处理,最终仍通过Binder机制回传给我们的ApplicationThread对象。这个对象最终听过Handler将调度的Message发送到主线程的MessageQueue里,完成生命周期的正常调度。
尾声
到此关于我对Handler先写的内容就正式结束了,当然这其中仍有很多的坑还没有填。比如篇幅比较长的Binder,关于Binder会在接下来的文章中出现。 此外这篇文中分析的过程更多的有点像Activity的启动过程。接下来关于Binder的文章,有真正的以启动Activity为例子,把Binder机制串起来~
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- [ Laravel 5.7 文档 ] 基础组件 —— HTTP 响应
- [ Laravel 5.8 文档 ] 基础组件 —— HTTP 响应
- 响应式文件浏览管理器angular-filemanager【组件】
- DevExtreme 18.2 发布,响应式 Web 开发 JS 组件
- Android官方架构组件Paging-Ex:列表状态的响应式管理
- 异步响应式 RPC 框架 turbo-rpc 0.0.4 发布,升级组件
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
The Creative Curve
Allen Gannett / Knopf Doubleday Publishing Group / 2018-6-12
Big data entrepreneur Allen Gannett overturns the mythology around creative genius, and reveals the science and secrets behind achieving breakout commercial success in any field. We have been s......一起来看看 《The Creative Curve》 这本书的介绍吧!
Base64 编码/解码
Base64 编码/解码
RGB CMYK 转换工具
RGB CMYK 互转工具