前言
前段时间在公司一个老项目中 , 看到有个惊为天人的操作 —— 直接 new 了一个 activity , 当然最后他没有调用 , 估计是调用后发现崩溃了 , 当时的第一反应是怎么敢这样干的啊 , activity 的创建应该是 AMS 干的 , 自己 new 的 activity 系统就没办法进行管理 , 关于 activity 的生命周期等等全部都乱套了 , 然后就在想 , 那 Android 的 activity 是如何启动的 , 甚至与 Android 系统是如何启动的 ?
一、 为什么不能自己 new Ac tivity ?
其实 Activity 本质上也是一个普通的类 , 他的基类也是 Object , 所以直接创建 Activity 在编译时并不会报错 , Android 系统中是通过 AMS 来创建 Activity 的 ( 具体分析可以直接看下面的内容 )。 更具体的来说 , Activity 是被 newInstance 创建出来的 。Instrumentation 是系统与应用交互前的捕捉类,可以在这里进行监控应用与系统的交互操作。我们与系统交互一般是通过 Activity 来进行的。 Instrumentation 是通过类加载器的 newInstance() 方法来创建 Activity 实例的。
那么 Instrumentation 的 newInstance 创建的 Activity 和自己 new 的 Activity 有什么区别 ?
从名称上就可以看出,如果要使用新实例,就需要确保该类已被装入,并已被连接。在开始类装入程序时,在新实例之前有一个 loadClass 类(类名称)。装载类的静态装载方式,可以让装载类更加灵活,无论类如何变化,都可以将类对象实例化,从而更好的解耦。
而new 创建一个新类时,这个类可以没有被加载,只能调用 public 构造方法,但对类强耦合,如果类变化了, new 相应的类名也要跟着变化。
那么自己 new 的 Activity 为什么会报错?
首先 , 运行代码后 , 报的错误叫
Attempt to invoke virtual method 'android.view.Window$Callback android.view.Window.getCallback()' on a null object reference
这是 window 为空的报错 , 这是因为我们 只要调用了super.onCreate(savedInstanceState) 方法 ,就会给Activity 添加窗口,而我们 并 没有靠系统给Activity 创建窗口,调用了 super.onCreate(savedInstanceState) 方法,必然会用到窗口, 所以 会报错的。
如果只调用Acitivity 跟 View 不相关的方法, 其实 能正常运行,这和普通类的普通方法没什么区别。
二、 简述 Android 系统的启动流程
在研究 A c tivity 的启动流程时 , 我就在想 , Android 系统的启动流程是什么样子的 , 所以先大概了解了一下 , 如果有如果的话 , 我再找时间深入了解一下 Android 的启动流程 , 然后跟打击分享 。 这里直接说结论 :
总的来说,Android 系统启动流程的主要经历 init 进程 -> Zygote 进程 –> SystemServer 进程 –> 各种系统服务 –> 应用进程等阶段。
1. 启动电源以及系统启动:当电源按下时引导芯片从预定义的地方(固化在 ROM )开始执行,加载引导程序 BootLoader 到 RAM ,然后执行。
2. 引导程序 BootLoader : BootLoader 是在 Android 系统开始运行前的一个小程序,主要用于把系统 OS 拉起来并运行。
3. Linux 内核启动:当内核启动时,设置缓存、被保护存储器、计划列表、加载驱动。当其完成系统设置时,会先在系统文件中寻找 init.rc 文件,并启动 init 进程。
4. init 进程启动:初始化和启动属性的服务,并且启动 Zygote 进程。
5. Zygote 进程启动:创建 JVM 并为其注册 JNI 方法,创建服务器端 Socket ,启动 SystemServer 进程。
6. SystemServer 进程启动:启动 Binder 线程池和 SystemServiceManager ,并且启动各种系统服务。
7. Launcher 启动:被 SystemServer 进程启动的 AMS 会启动 Launcher , Launcher 启动后会将已安装应用的快捷图标显示到系统桌面上。
三、 Android 系统中重要的类
ActivityManagerServices ,简称 AMS ,服务端对象,负责系统中所有 Activity 的生命周期。 ActivityManagerService 进行初始化的时机很明确,在 SystemServer 进程开启的时候,就会初始化 ActivityManagerService 。 在 经过 一系列的 步骤, 创建好 ActivityManagerService 对象 的同时 完成了成员变量初始化。而且在这之前, 也 调用createSystemContext() 创建系统上下文的时候,也已经完成了 mSystemContext 和 ActivityThread 的创建。注意,这是系统进程开启时的流程,在这之后,会开启系统的 Launcher 程序,完成系统界面的加载与显示。
很多文章都称 AMS 为服务端对象 , 是因为其实服务器客户端的概念不仅仅区别于前端后端,在 Android 的框架设计中,使用的也是这一种概念。比如我们这里提到的 ActivityManagerService ,和 PackageManagerService 、 WindowManagerService 等等都是服务器端 , 也是所有 App 共用的系统服务 。 这些基础的系统服务是被所有的应用公用的,当某个应用想实现某个操作的时候,要告诉这些系统服务,比如你想打开另外一个应用,那么我们知道了包名和 MainActivity 类名之后就可以告诉 AMS 来打开 。
为什么需要告诉 AMS 呢 , 因为 我们的 应用 通过调用startActivity() 并不能直接打开另外一个 应用 ,这个方法会通过一系列的调用,最后还是告诉AMS 说: “ 我 知道这个应用的地址和名称 , 请帮我打开他 ” , 所以是AMS 来通知 zygote 进程来 fork 一个新进程,来开启我们的目标的。知道了 Android Framework 的客户端服务器架构之后,我们还需要了解一件事情,那就是我们的 应用 和AMS(SystemServer 进程 ) 还有 zygote 进程分属于三个独立的进程,他们之间如何通信呢?
应用 与AMS 通过 Binder 进行 IPC 通信, AMS(SystemServer 进程 ) 与 zygote 通过 Socket 进行 IPC 通信。
那么AMS 有什么用呢?
在前面我们知道了,如果想打开一个App 的话,需要 AMS 去通知 zygote 进程,除此之外,其实所有的 Activity 的开启、暂停、关闭都需要 AMS 来控制,所以我们说, AMS 负责系统中所有 Activity 的生命周期。
在Android 系统中,任何一个 Activity 的启动都是由 AMS 和应用程序进程(主要是 ActivityThread )相互配合来完成的。 AMS 服务统一调度系统中所有进程的 Activity 启动,而每个 Activity 的启动过程则由其所属的进程具体来完成。
Launcher , Launcher 本质上也是一个应用程序,和我们的 App 一样,也是继承自 Activity 。 应用就是由 Launcher 启动的 。Launcher 实现了点击、长按等回调接口,来接收用户的输入。 继承于 Activity , 那么逻辑自然也跟我们想象的一样 , 它的实际实现代码就是捕捉图标点击事件,然后使用 startActivity() 发送对应的 Intent 请求
Instrumentation ,每一个应用程序只有一个 Instrumentation 对象,每个 Activity 内都有一个对该对象的引用,但是整个进程只会存在一个 Instrumentation 对象。当 startActivityForResult() 调用之后,实际上还是调用了 mInstrumentation.execStartActivity() 。 Instrumentation 可以理解为应用进程的管家, ActivityThread 要创建或暂停某个 Activity 时,都需要通过 Instrumentation 来进行具体的操作。
ActivityThread , App 的真正入口。当开启 App 之后,会调用 main() 开始运行,开启消息循环队列,这就是传说中的 UI 线程或者叫主线程。与 ActivityManagerServices 配合,一起完成 Activity 的管理工作。 而 ActivityThread 与 AMS 通信就是采用的 Binder 的 IPC 通信 , 在文章前面说到当我们调用 startActivity , 实际上是调用到了 Instrumentation 的 execStartActivity , 但是在后面又紧接着调用了 ActivityManagerNative.getDefault() .startActivity , 这里的 ActivityManagerNative.getDefault() 返回的就是 AMS 的远程接口 , 也就是 ActivityManagerProxy 。 接下来就是调用 ActivityManagerProxy.startActivity() ,在这里面做的事情就是 IPC 通信,利用 Binder 对象,调用 transact() ,把所有需要的参数封装成 Parcel 对象,向AMS 发送数据进行通信。
ApplicationThread ,用来实现 ActivityManagerService 与 ActivityThread 之间的交互。在 ActivityManagerService 需要管理相关 Application 中的 Activity 的生命周期时,通过 ApplicationThread 的代理对象与 ActivityThread 通讯。
ApplicationThreadProxy ,是 ApplicationThread 在服务器端的代理,负责和客户端的 ApplicationThread 通讯。 AMS 就是通过该代理与 ActivityThread 进行通信的。
ActivityStack , Activity 在 AMS 的栈管理,用来记录已经启动的 Activity 的先后关系,状态信息等。通过 ActivityStack 决定是否需要启动新的进程。 在文章前面说到 AMS 收到 startActivity 的请求之后 , 一步步调用 , 到了 startActivityAsUser () 方法后就会遇到 ActivityStackSupervisor 这个类 , 他就可以首先对 ActivityStack 的部分操作
ActivityRecord , ActivityStack 的管理对象,每个 Activity 在 AMS 对应一个 ActivityRecord ,来记录 Activity 的状态以及其他的管理信息。其实就是服务器端的 Activity 对象的映像。
TaskRecord , AMS 抽象出来的一个 “ 任务 ” 的概念,是记录 ActivityRecord 的栈,一个 “Task” 包含若干个 ActivityRecord 。 AMS 用 TaskRecord 确保 Activity 启动和退出的顺序。如果你清楚 Activity 的 4 种 launchMode ,那么对这个概念应该不陌生。
四、Activity 启动流程
终于到了本片文章到主题了,下面让我们细说Activity的启动流程是怎么个回事
1. Launcher 通过 Binder 进程间通信机制通知 AMS ,它要启动一个 Activity
public boolean startActivitySafely(View v, Intent intent, ItemInfo item) { ... //标记在新的栈启动 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); ... startActivity(intent, optsBundle); ... }
(Launcher.java )
@Override public void startActivity(Intent intent, @Nullable Bundle options) { ... if (options != null) { //-1为requestCode表明不需要知道是否启动成功 startActivityForResult(intent, -1, options); } else { startActivityForResult(intent, -1); } } public void startActivityForResult(@RequiresPermission Intent intent, int requestCode, @Nullable Bundle options) { ... Instrumentation.ActivityResult ar = mInstrumentation.execStartActivity( this, mMainThread.getApplicationThread(), mToken,this,intent, requestCode, options); ... }
(Activity.java )
上面说到 , 每一个应用程序只有一个Instrumentation 对象 , 当我们调用 startActivity , 实际上是调用到了 Instrumentation 的 execStartActivity 。
public ActivityResult execStartActivity( Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) { ... int result = ActivityTaskManager.getService().startActivity(whoThread,who.getBasePackageName(), who.getAttributionTag(),intent,intent.resolveTypeIfNeeded(who.getContentResolver()), token,target != null ? target.mEmbeddedID : null, requestCode, 0, null, options); ... }
(Instrumentation.java )
2. AMS 通过 Binder 进程间通信机制通知 Launcher 进入 Paused 状态
private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) { ... if (mResumedActivity != null) { pausing |= startPausingLocked(userLeaving, false , next); } ... mStackSupervisor.startSpecificActivity(next, true, false); ... }
(ActivityStack.java )
void startSpecificActivity(ActivityRecord r, boolean andResume, boolean checkConfig) { // 获取要启动的Activity进程信息 final WindowProcessController wpc = mService.getProcessController(r.processName, r.info.applicationInfo.uid); boolean knownToBeDead = false; //如果进程存在且有进程中有线程存在 就是启动一个同应用的Activity(普通Activity就在此执行) if (wpc != null && wpc.hasThread()) { try { realStartActivityLocked(r, wpc, andResume, checkConfig); return; } catch (RemoteException e) { Slog.w(TAG, "Exception when starting activity " + r.intent.getComponent().flattenToShortString(), e); } // If a dead object exception was thrown -- fall through to // restart the application. knownToBeDead = true; } //否则通过AMS向Zygote进程请求创建新的进程 r.notifyUnknownVisibilityLaunchedForKeyguardTransition(); final boolean isTop = andResume && r.isTopRunningActivity(); mService.startProcessAsync(r, knownToBeDead, isTop, isTop ? "top-activity" : "activity"); }
( ActivityStackSupervisor.java )
3. Launcher 通过 Binder 进程间通信机制通知 AMS ,它已经准备就绪进入 Paused 状态,于是 AMS 就创建一个新的线程,用来启动一个 ActivityThread 实例,即将要启动的 Activity 就是在这个 ActivityThread 实例中运行
public static void main(String[] args) { ... Looper.prepareMainLooper(); ... ActivityThread thread = new ActivityThread(); thread.attach(false, startSeq); ... Looper.loop(); ... } private void attach(boolean system, long startSeq) { final IActivityManager mgr = ActivityManager.getService(); try { mgr.attachApplication(mAppThread, startSeq); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } ... }
(ActivityThread.java )
private boolean attachApplicationLocked(@NonNull IApplicationThread thread, int pid, int callingUid, long startSeq) { ... thread.bindApplication(processName, appInfo, providerList, instr2.mClass, profilerInfo, instr2.mArguments, instr2.mWatcher, instr2.mUiAutomationConnection, testMode, mBinderTransactionTrackingEnabled, enableTrackAllocation, isRestrictedBackupMode || !normalMode, app.isPersistent(), new Configuration(app.getWindowProcessController().getConfiguration()), app.compat, getCommonServicesLocked(app.isolated), mCoreSettingsObserver.getCoreSettingsLocked(), buildSerial, autofillOptions, contentCaptureOptions, app.mDisabledCompatChanges); ... didSomething = mAtmInternal.attachApplication(app.getWindowProcessController()); ... }
(ActivityManagerService.java )
public final void bindApplication(String processName, ApplicationInfo appInfo, ProviderInfoList providerList, ComponentName instrumentationName, ProfilerInfo profilerInfo, Bundle instrumentationArgs, IInstrumentationWatcher instrumentationWatcher, IUiAutomationConnection instrumentationUiConnection, int debugMode, boolean enableBinderTracking, boolean trackAllocation, boolean isRestrictedBackupMode, boolean persistent, Configuration config, CompatibilityInfo compatInfo, Map services, Bundle coreSettings, String buildSerial, AutofillOptions autofillOptions, ContentCaptureOptions contentCaptureOptions, long[] disabledCompatChanges) { ... sendMessage(H.BIND_APPLICATION, data); } public void handleMessage(Message msg) { switch (msg.what) { case BIND_APPLICATION: AppBindData data = (AppBindData)msg.obj; handleBindApplication(data); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); break; ... } } private void handleBindApplication(AppBindData data) { ... mInstrumentation.callApplicationOnCreate(app); ... }
4. ActivityThread 通过 Binder 进程间通信机制将一个 ApplicationThread 类型的 Binder 对象传递给 AMS ,以便以后 AMS 能够通过这个 Binder 对象和它进行通信
5. AMS 通过 Binde 进程间通信机制通知 ActivityThread ,现在一切准备就绪,它可以真正执行 Activity
public Activity handleLaunchActivity(ActivityClientRecord r, PendingTransactionActions pendingActions, Intent customIntent) { ... final Activity a = performLaunchActivity(r, customIntent); ... return a; } private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { ContextImpl appContext = createBaseContextForActivity(r); ... //反射创建Activity java.lang.ClassLoader cl = appContext.getClassLoader(); activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent); ... Application app = r.packageInfo.makeApplication(false, mInstrumentation); ... activity.attach(appContext, this, getInstrumentation(), r.token, r.ident, app, r.intent, r.activityInfo, title, r.parent, r.embeddedID, r.lastNonConfigurationInstances, config, r.referrer, r.voiceInteractor, window, r.configCallback, r.assistToken); ... activity.setTheme(theme); ... mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState); }
五、为什么没有跨应用的启动Activity 也要用到跨进程通信
不知道大家有没有看到前面的代码,在我们启动Activity时,必然会执行到跨进程的代码,这是因为Android 需要统一管理所有 Activity 及其生命周期回调, Android 支持跨进程启动其他应用的 Activity ,如果发现要启动的 Activity 组件的进程不存在是,会由 AMS 所在的 SystemServer 进程触发 Zgote 进程, fork 出一个子进程作为新的 Activity 组件的进程。 AMS 为了统一管理,不管是不是同一个进程中的 Activity 组件启动,都要到 AMS 中,告诉它要进行什么操作。
六、启动一个新的Activity 至少要触发多少次跨进程通信
答案是至少 4 次
第一次:ActivityManagerProxy.startActivity()->ActivityManagerService.startActivity() 应用进程告诉AMS 我要启动新的Acitvity 组件;
第二次:ApplicationThreadProxy.schedulePauseActivity()->ApplicationThread.schedulePauseActivity() AMS 通知应用进程将前一个 Activity 组件 pause 掉;
第三次:ActivityManagerService.activityPaused() 应用进程通知 AMS 上一个 Activity 已 paused 掉了
第四次:ApplicationThreadProxy.scheduleLaunchActivity()-->ApplicationThread.scheduleLaunchActivity() , AMS 通知应用进程通信启动新的 Activity 组件