内容简介:上文我们聊了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 发布,升级组件
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
JavaScript语言精髓与编程实践
周爱民 / 电子工业出版社 / 2012-3 / 79.00元
《JavaScript语言精髓与编程实践(第2版)》详细讲述JavaScript作为一种混合式语言的各方面特性,包括过程式、面向对象、函数式和动态语言特性等,在动态函数式语言特性方面有着尤为细致的讲述。《JavaScript语言精髓与编程实践(第2版)》的主要努力之一,就是分解出这些语言原子,并重现将它们混合在一起的过程与方法。通过从复杂性到单一语言特性的还原过程,读者可了解到语言的本质,以及“层......一起来看看 《JavaScript语言精髓与编程实践》 这本书的介绍吧!