从设计角度理解Activity切换

Activity的切换过程其实是老生常谈了,基本上就是两个App和AMS利用Bindler互相通信,进行跨进程的调用,详细代码过程可以看罗升阳的博客
但是从设计角度来看这个过程,会发现很多有意思的细节,在这里简单整理一下。

基本切换流程

启动:
发起App中跳转Activity的代码
-->Context启动startActivity
-->Instrumentation通过Bindler通知AMS
-->等待AMS的通知,pause当前的Activity

-->AMS通过ActivityClientRecord找到Activity组件信息
-->AMS要检查ActivityStack里是否已存在目标Activity,如果存在,恢复即可
-->AMS寻找目标App的ApplicationThread,实现Bindler通信
-->AMS如果发现目标App没有启动,会通知Zygote去fork新的进程,并与新进程互相建立通信(ActivityManangerProxy和ApplicationThreadProxy)
-->AMS协调两个App的展示,隐藏旧的Activity,显示新的Activity
-->AMS需要通知WMS去更新界面

-->目标App的进程启动时,启动ActivityThread,主线程会先启动ContentProvider,再启动Application,创建Context和Instrumentation。
-->ActivityThread调用Instrumentation,用反射的方式创建Activity。
-->主线程会调用Activity的attach,绑定Application和Context,创建PhoneWindow等。
-->主线程继续执行Activity的生命周期,onCreate、onStart、onResume

主要问题

如果从实现的角度看,两个App的Activity要互相跳转,其实要解决这样几个问题:

  1. App和AMS如何分工
  2. 三个进程之间如何建立通信
  3. 如何判断App是否已存在并分别处理
  4. 如何提供统一的跳转调用入口

App和AMS的角色分工

在系统层面,App和AMS的职责很单一,App只负责操作Activity,AMS只负责协调,具体来说:
App负责执行Activity的创建、显示和收起;
AMS负责在两个App之间协调动作,并复用或创建App。
而且,App和AMS之间,只通过Bindler通信来实现接口形式的调用,并不关心对方如何处理,最少知道
比如,App只向AMS发出启动另一个Activity的请求,AMS是唤醒已存在的Activity,还是新建App及其Activity,发起方的App并不关心;反之也一样,被启动的App也只和AMS打交道,并不关心是谁发起的请求,AMS相当于一个中介者,两个App之间有良好的解耦。

通信的建立

App和AMS之间通过Bindler进行通信,每个Bindler其实只能建立单向通信,相互通信的话,需要互相持有对方的Binder代理才行。
AMS作为系统服务,可以发起创建App进程,其实就是要求Zygote通过fork进程的方式创建新的App进程,这时就顺带着把AMS的Bindler代理(ActivityManagerNative)传给新的App进程即可。
新的App启动时,可以联系到AMS,这时候再把自己的代理(ApplicationThreadNative)传给AMS即可。

如何判断App是否已存在并分别处理

App的职责划分中,没有也不应该有其他App的信息,这个功能只能在管理者AMS中实现。
既然每个App都需要经过AMS启动,AMS虽然不能保持Activity本身,但完全可以维护一个ActivityStack栈,并在其中记录每个已启动的Activity的信息ActivityRecord。
如果要打开的Activity已存在,可以直接通过ActivityRecord向对应的App发bindler调用,要求启动Activity;
如果目标Activity不存在,可能需要建立Application,建立好通信关系,然后再要求启动Activity。

如何提供统一的跳转调用入口

Context的装饰
我们知道Activity、Service和Application使用了Context来提供很多功能,Context是一个典型的装饰模式,ContextWrapper只是增加一些小特性,例如Activity的ContextThemeWrapper,真正干活的是ContextImpl。

Context的引用
发起StartActivity这种动作,其实比较复杂,不可能在每个组件类里实现一遍,所以是放在Context里统一提供这些功能。
这涉及到组件类对Context的引用,为了实现引用,每个组件类都需要实现一个attach函数,在attach函数里,被赋予Application和Context。

Instrumentation的分工
Context作为上下文环境,要提供App资源包(packageInfo)、文件读写、操作组件、发送广播等多种功能,这其实是一个外观模式,内部进一步做了职能的划分,其中操作组件的功能是交给了Instrumentation来实现的。
如果我们要Hook侵入组件的操作过程,要Hook的目标其实放在Instrumentation就可以了。

成员对象的单一实例
Activity里有很多成员对象是整个App内部只有一个实例的,例如Application、ActivityThread,Android没有采用单例模式来提供这些实例,而是直接在attach函数中提供了已有的对象。
Context并没有使用同一个对象,而是创建了新的ContextImpl,Context内部使用的LoadedApk对象packageInfo倒是指向了同一个对象。

统一的最终函数
其实我们自己开发的时候也会经常使用这种方式,就是会针对不同的逻辑场景写很多函数,但是层层调用之后,会汇集到同一个最终函数,统一处理,其实这样可以把核心代码抽象为一处,有利于编写和维护,Activity里的startActivityforResult和Handler里的sendMessageAtTime都是这类写法。

附录;

附录一;Android高级技术大纲

附录二;Android进阶实战技术视频

获取方式;

加Android进阶群;701740775。即可前往免费领取。麻烦备注一下csdn领取资料

猜你喜欢

转载自blog.csdn.net/feiyu1947/article/details/86580158