前言
学习过程中一直很好奇,Application 实例是在哪创建的?Activity 实例是在哪创建的?Activity 的生命周期又是在哪调用的?Activity 内部界面绘制究竟在哪个时机,为什么 onCreate、onStart、onResume 都取不到 View 的宽高?应用程序启动时的白屏是怎么回事,怎么才能不显示?为了解决这些问题,下载编译了 AOSP 源码,阅读了这部分源码,本文基于 Android 7.1。
准备
下载 AOSP 源码参考官网,在线阅读源码使用AndroidXRef 。
Launcher
当用户点击桌面上应用图标时会执行 Launcher.onClick 方法,省略后代码如下:
// packages/apps/Launcher3/src/android/launcher3/Launcher.java |
可以看到最终 Launcher 也是使用 Activity.startActivity 启动应用程序,这里有个疑问,这个 AppInfo 和其内部的 Intent 从哪来的?其实在最初 Launcher 加载所有 App 信息时就会构建 AppInfo 实例,内部会执行如下代码:
public AppInfo(Context context, LauncherActivityInfoCompat info, UserHandleCompat user, |
设置了 Action 为 Intent.ACTION_MAIN,Category 为 Intent.CATEGORY_LAUNCHER ,这也就是为什么 App 的首个 Activity 必须要配置 android.intent.action.MAIN 、android.intent.category.LAUNCHER 的原因,同时还设置了 Intent.FLAG_ACTIVITY_NEW_TASK 这使得新开启的 Activity 不跟 Launcher 运行在同一个栈中。继续看 Activity.startActivity。
// frameworks/base/core/java/android/app/Activity.java |
使用 Instrumentation 启动 Activity。 注:该类负责管理应用程序与系统的交互。
// frameworks/base/core/java/android/app/Instrumentation.java |
由此可以看到最终会执行 IActivityManager.startActivity ,先从 ServiceManager 中获取 IBinder 实例,然后将其转换为 IActivityManager ,通信方式同 AIDL 就不展开了。
ServiceManager 是客户端获取系统服务的桥梁,SystemServer 进程通过 ServiceManager.addService 添加了很多服务,ActivityManagerService 就是其中之一,详细添加地方在 SystemServer.startBootstrapServices 方法中,添加后客户端程序就能使用 ServiceManager.getService 获取 SystemServer 进程中指定服务对应的 BinderProxy。至于 ServiceManager 的具体实现,由于是 Native 的暂时就先不了解了。
SystemServer
当客户端调用 IActivityManager.startActivity 后服务端就执行了 ActivityManagerService.startActivity 。
AMS.startActivity
代码如下:
// frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java |
继续看 startActivityLocked 方法。
AST.startActivityLocked
代码如下:
final int startActivityLocked(...) { |
这个方法主要内容还是处理错误情况,接着看 startActivityUnchecked。
AST.startActivityUnchecked
代码如下:
private int startActivityUnchecked(...) { |
AST.setInitialState
代码如下:
private void setInitialState(ActivityRecord r, ActivityOptions options, TaskRecord inTask, |
这个方法需要注意,*如果 Intent 里面设置了 Intent_FLAG_ACTIVITY_NEW_TASK ,那么使用 startActivityForResult 启动 Activity 会立即收到 onActivityResult *。
AST.computeLaunchingTaskFlags
代码如下:
private void computeLaunchingTaskFlags() { |
startActivityUnchecked 内部主要处理的还是启动模式的处理,接着执行 ActivityStack.startActivityLocked 。
ASK.startActivityLocked
代码如下:
// frameworks/base/services/core/java/com/android/server/am/ActivityStack.java |
阅读到这里就找到了显示白屏的地方,继续往下看,看看是否有地方可以不显示白屏。
ARD.showStartingWindow
代码如下:
// frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java |
这里的 mWindowManager 其实现是 WindowManagerService。
WMS.setAppStartingWindow
代码如下:
// frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java |
通过阅读源码可以发现,只要 Window_windowIsTranslucent、Window_windowIsFloating、Window_windowDisablePreview 三个有一个为 true 就不会显示白屏了,不过虽然不显示白屏了,但是进程启动需要时间,因此会停留在桌面一段时间。
回到主线,继续执行 ActivityStart.startActivityUnchecked。
ASS.resumeFocusedStackTopActivityLocked
代码如下:
// frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java |
这里主要看看进程不存在的情况:
AMS.startProcessLocked
代码如下:
final void startProcessLocked(...) { |
经过一系列流程,最终通过 Socket 发送消息给 Zygote 进程,通知其 fork 进程,然后将 pid 返回。Zygote 进程是在 init 进程启动后通过 fork 自身创建的,其主要代码在 ZygoteInit 类中,主要负责预加载类和资源(比如:Activity、Context)然后 fork 自身创建 SystemServer 进程,接着自身进入循环等待处理 Socket 消息。
Zygote
Zygote 创建应用程序进程代码在 ZygoteConnection.processOneCommand。
ZC.processOneCommand
代码如下:
Runnable processOneCommand(ZygoteServer zygoteServer) { |
App
在 processOneCommand 进入到代码一时,已经是在 App 进程执行了,接着调用 handleChildProc。
ZC.handleChildProc
代码如下:
private Runnable handleChildProc(Arguments parsedArgs, FileDescriptor[] descriptors, |
接下来代码就执行到了 ActivityThread.main 。
AT.main
代码如下:
public static void main(String[] args) { |
首先 Looper.prepareMainLoop 创建了主线程的 Looper 实例,Loop.loop 死循环从消息队列中取出消息,那么是那里给主线程发消息呢,看来主要逻辑还是在 attach 方法中了。
AT.attach
代码如下:
private void attach(boolean system, long startSeq) { |
attach 内部其实主要工作也只是调用了 AMS.attachApplication。注意传入一个 ApplicationThread 实例,先来看看该类。
private class ApplicationThread extends ApplicationThreadNative {} |
ApplicationThreadNative 其实可以看做 AIDL 里面的 Stub 类, 因此将 ApplicationThread 实例传递给 AMS,AMS 就可以远程调用该实例的方法。
AMS.attachApplication
代码如下:
public final void attachApplication(IApplicationThread thread) { |
方法内部首先 IPC 调用 ApplicationThread.bindApplication 。
public final void bindApplication(...) { |
其内部只是发了个消息,这时候消息是没办法处理的因为 Looper.loop 还没调用,于是继续执行 ActivityStackSupervisor.attachApplicationLocked。
boolean attachApplicationLocked(ProcessRecord app) throws RemoteException { |
执行到这里其实可以与不创建进程的情况并起来了,最终在进程存在后,都是通过调用 ActivityStackSupervisor.realStartActivityLocked 启动 Activity。
final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app, |
scheduleLaunchActivity 与 bindApplication 一样都是发了个消息返回了,现在 AMS 执行完成了,ActivityThread 继续执行 Looper.loop,因此会先执行 BIND_APPLICATION 消息。
AT.handleBindApplication
在 H 类中 BIND_APPLICATION 消息最终调用 handleBindApplication 代码如下:
private void handleBindApplication(AppBindData data) { |
可以看到方法内部主要就是创建 Application 对象并且回调其 onCreate,注意 ContentProvider 的 onCreate,优先于 Application 的 onCreate。
AT.handleLaunchActivity
在 H 类中 LAUNCH_ACTIVITY 消息最终调用 handleLaunchActivity 代码如下:
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) { |
handleLaunchActivity 内部主要执行了 performLaunchActivity 以及 handleResumeActivity。
AT.performLaunchActivity
代码如下:
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { |
方法内部主要创建了 Activity 实例并且调用其 attach、onCreate、onStart、onRestoreInstanceState(如果有)、onPostCreate。接着看 handleResumeActivity。
AT.handleResumeActivity
final void handleResumeActivity(IBinder token, |
方法内部主要是调用了 Activity.onResume,接着调用 WindowManager.addView 执行 View 的三大流程。
总结
基本阅读完整个启动流程,前言那几个问题都可以解决了。
- Application 实例是在哪创建的?Instrumetation.newApplication。
- Activity 实例是在哪创建的?Instrumetaion.newActivity。
- Activity 内部界面绘制究竟在哪个时机,为什么 onCreate、onStart、onResume 都取不到 View 的宽高?在 onResume 执行完毕后才开始,因此 onResume 其之前都是取不到的。
- 应用程序启动时的白屏是怎么回事,怎么才能不显示?设置 Window_windowIsTranslucent、Window_windowIsFloating、Window_windowDisablePreview 三个有一个为 true 就不会显示白屏了。
注:挂载并运行模拟器命令
sudo hdiutil attach ~/android.dmg.sparseimage -mountpoint /Volumes/android |