Context通过外观模式为我们封装了组件启动的繁琐流程
Activity
启动入口:startActivity(requestCode = -1)、startActivityForResult
@Override
public void startActivity(Intent intent, @Nullable Bundle options) {
if (options != null) {
startActivityForResult(intent, -1, options);
} else {
startActivityForResult(intent, -1);
}
}
最终通过Binder调用跳转到ActivityTaskManagerService.startActivity,传递的关键参数有:IApplicationThread(进程相关PID、UID)、Token、RequestCode、Intent
@Override
public final int startActivity(
IApplicationThread caller, IBinder resultTo, int requestCode, Intent intent,
String resultWho, String callingPackage, String resolvedType,
int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) {
return startActivityAsUser(caller, resultTo, requestCode, intent, resultWho,
callingPackage, resolvedType, startFlags, profilerInfo, bOptions,
UserHandle.getCallingUserId());
}
在ActivityStarter.startActivity中创建sourceRecord、resultRecord
ActivityRecord sourceRecord = null;
ActivityRecord resultRecord = null;
if (resultTo != null) {
sourceRecord = mRootActivityContainer.isInAnyStack(resultTo);
if (sourceRecord != null) {
if (requestCode >= 0 && !sourceRecord.finishing) {
resultRecord = sourceRecord;
}
}
}
if ((launchFlags & Intent.FLAG_ACTIVITY_FORWARD_RESULT) != 0 && sourceRecord != null) {
if (requestCode >= 0) {
SafeActivityOptions.abort(options);
return ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT;
}
resultRecord = sourceRecord.resultTo;
resultWho = sourceRecord.resultWho;
requestCode = sourceRecord.requestCode;
sourceRecord.resultTo = null;
}
最终通过resultRecord创建当前Activity对应的ActivityRecord
ActivityRecord r = new ActivityRecord(mService, callerApp, callingPid, callingUid,
callingPackage, intent, resolvedType, aInfo, mService.getGlobalConfiguration(),
resultRecord, resultWho, requestCode, componentSpecified, voiceSession != null,
mSupervisor, checkedOptions, sourceRecord);
根据launchMode、taskAffinity、Intent Flag来获取当前Activity对应的ActivityTask,如果是SingleTask且栈中存在实例则通过performClearTaskLocked方法执行清栈操作,非当前显示的Activity依次调用onDestory(10秒超时)
mRootActivityContainer.resumeFocusedStacksTopActivities(
mTargetStack, mStartActivity, mOptions);
- 接着pause正在显示的Activity
- 将Activity对应堆栈中的mPausingActivity赋值为当前的Activity
- 将Activity对应堆栈中的mResumedActivity赋值为NULL
- 通过ClientTransaction(PauseActivityItem)通知客户端
- Pause结束分两种情况:500ms超时/操作完成后通过activityPaused通知到AMS
- 清空Activity对应堆栈中的mPausingActivity
- 获取focusedActivityStack(isFocusableAndVisible),判断ActivityStack中是否存在mResumedActivity,在上面的步骤中,我们已经清空了mResumedActivity,因此会直接启动目标Activity,启动Activity分为两种情况:进程已启动,进程未启动
进程已启动
创建ClientTransaction,执行IAppliactionThread.scheduleTransaction跳转到用户端,用户端执行executeCallbacks、executeLifecycleState,进而执行handleLaunchActivity和handleResumeActivity
- handleLaunchActivity:
- 创建ContextImpl、创建Activity
- 执行Activity.attach
- 创建PhoneWindow
- 创建WindowManagerImpl
- 执行Activity.onCreate
- handleResumeActivity:onStart、onResume、Activity.makeVisible
- Activity.makeVisible:WindowManagerImpl.addView(DecorView)
- WindowManagerImpl.addView(DecorView):WindowManagerGlobal(保存params、roots、views)
- ViewRootImpl.addView:requestLayout、IWindow、IWindowSession
- WindowManagerService:持有WindowState、WindowToken,IWindowSession持有ISurfaceSession
- ViewRootImpl.addView:requestLayout、IWindow、IWindowSession
- WindowManagerImpl.addView(DecorView):WindowManagerGlobal(保存params、roots、views)
- Activity.makeVisible:WindowManagerImpl.addView(DecorView)
进程未启动
- 启动进程:Runtime.init
- initZygote
- initApplication
- Looper.prepare
- 执行AMS.attachApplication
- 回调执行IApplicationThread.bindAppliation
- 创建ContextImpl
- 创建Application
- 启动ContentProvider
- 执行Application.onCreate
- 执行RootActivityContainer.attachApplication
- 判断mTmpActivityList中ActivityRecord的进程信息是否与当前进程相符,并且能够匹配到focusedStack的topActivity,如果匹配则继续走Activity启动流程
- 回调执行IApplicationThread.bindAppliation
- Looper.loop
Service
startService
关键参数:(IApplicationThread、Intent、requestForeground)
AMS逻辑:判断当前进程是否是前台进程:callerFg、通过PMS解析得到ServiceLookupResult进而创建ServiceRecord,走启动逻辑:
- Service已启动:sendServiceArgs-> onStartCommand
- 进程已启动,Service未启动:realStartServiceLocked:
- scheduleCreateService:执行onCreate生命周期
- sendServiceArgsLocked:执行onStart生命周期
- 如果是前台服务,执行超时逻辑:scheduleServiceForegroundTransitionTimeoutLocked
- 进程未启动:
- 启动进程
- 在attachAppliaction中执行ActiveServices.attachApplication
- 启动mPendingServices
- bumpServiceExecutingLocked:发送ANR检测消息(20/200),AMS.serviceDoneExecuting
- bumpServiceExecutingLocked(r, execInFg, "create");
- bumpServiceExecutingLocked(r, execInFg, "start");
- bumpServiceExecutingLocked(r, execInFg, "bind");
- bumpServiceExecutingLocked(s, false, "unbind");
- bumpServiceExecutingLocked(r, false, "destroy");
bindService
- 封装ServiceConnection
- 判断是否调用了onBind,不会再次调用
- 如果调用了onUnbind,下次绑定判断onUnbind返回值,如果返回true,调用onRebind,否则不再调用任何生命周期
ContentProvider
正常情况下,在bindApplication中通过installContentProviders完成初始化,内部执行intallProvider,然后执行publishContentProviders通知到AMS
ContentProvider中存在一个比较特殊的数据结构:ContentProviderHolder,当我们调用AMS.getContentProvider时
- 如果ContentProvider能够在当前进程运行(multiproc/processName相等),则返回空的Holder,由本地进行填充(我理解是防止死锁)
- 如果无法在当前进程运行,则判断目标进程是否启动
- 如果进程已启动,则通过IApplicationThread.scheduleInstallProvider安装
- 如果进程未启动,则启动进程,并加入mLaunchingProviders中
- 请求逻辑原地等待,20秒超时
- 在AMS.attachApplication中发送CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG消息
- 在publishContentProviders中移除CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG消息
BroadcastReceiver
普通广播(可带权限)、有序广播(可带权限)、粘性广播、本地广播,8.0开始禁用隐式广播
对应广播队列有前台队列、后台队列,每个队列里还有并行和串行列表
广播可以动态注册、也可以静态注册。动态注册会判断当前是否有符合的粘性广播,如有,则接收
当我们发送广播时
- 首先在AMS侧设置Flag,增加NOT_INCLUDE_STOP_PACKAGE、如果系统未完成启动,增加REGISTERED_ONLY
- 判断权限,如果非系统进程使用了保护广播,则抛出异常
- 如果是粘性广播,则记录到stickyBroadcastList中
- 查询receivers(静态注册的广播接受者)和registeredReceivers(动态注册的广播接受者)
- 如果发送的是普通广播(非顺序广播),则通过parallelQueue来通知registeredReceivers
- 否则,将receivers和registeredReceivers按照优先级合并,顺序发送广播,客户端完成广播处理后,通过sendFinished告知AMS,调用cancelBroadcastTimeoutLocked取消超时广播
- 如果是串行广播,触发processNextBroadcastLocked方法,该方法会通过setBroadcastTimeoutLocked发送超时消息
onWindowFocusChanged
activityPaused -> WMS.H(IWindow.windowFocusChanged(false)) -> onWindowFocusChanged(false)
IWindowSession.relayout -> WMS.H(IWindow.windowFocusChanged(true)) -> onWindowFocusChanged(true)
onWindowFocusChanged函数是Activity的首帧渲染时间,并不是首页第一屏展示时间
1944 performTraversals 1944~2770
2028 dispatchAttachedToWindow
2264 relayoutWindow
2541 performMeasure
2590 performLayout
2755 performDraw
安卓系统启动流程
- 当按电源键触发开机,首先会从 ROM 中预定义的地方加载引导程序 BootLoader 到 RAM 中,并执行 BootLoader 程序启动 Linux Kernel, 然后启动用户级别的第一个进程:init 进程
- init 进程会解析 init.rc 脚本做一些初始化工作,包括挂载文件系统、创建工作目录以及启动系统服务进程,包括 Zygote、ServiceManager、Media 等
- 在 Zygote 中会进一步去启动 system_server 进程,然后在 system_server 进程中会启动 AMS、WMS、PMS 等服务,等这些服务启动之后,AMS 中就会启动 Launcher 应用来现实桌面
system_server 为什么要在 Zygote 中启动,而不是由 init 直接启动呢
Zygote 作为一个孵化器,可以提前加载一些资源,这样 fork() 时基于 Copy-On-Write 机制创建的其他进程就能直接使用这些资源,而不用重新加载。比如 system_server 就可以直接使用 Zygote 中的 JNI 函数、共享库、常用的类、以及主题资源
为什么要专门使用 Zygote 进程去孵化应用进程,而不是让 system_server 去孵化呢
首先 system_server 相比 Zygote 多运行了 AMS、WMS 等服务,这些对一个应用程序来说是不需要的。另外进程的 fork() 对多线程不友好,仅会将发起调用的线程拷贝到子进程,这可能会导致死锁,而 system_server 中肯定是有很多线程的
能说说具体是怎么导致死锁的吗
fork() 时只会把调用线程拷贝到子进程、其他线程都会立即停止,那如果一个线程在 fork() 前占用了某个互斥量,fork() 后被立即停止,这个互斥量就得不到释放,再去请求该互斥量就会发生死锁了
Zygote 为什么不采用 Binder 机制进行 IPC 通信
- 1、多线程问题:Linux中fork进程是不推荐fork一个多线程的进程的,因为如果存在锁的情况下,会导致锁异常
- Binder 机制中存在 Binder 线程池,是多线程的,如果 Zygote 采用 Binder 的话就存在上面说的 fork() 与 多线程的问题了
- 严格来说,Binder 机制不一定要多线程,所谓的 Binder 线程只不过是在循环读取 Binder 驱动的消息而已,只注册一个 Binder 线程也是可以工作的,比如 service manager 就是这样的
- 实际上 Zygote 尽管没有采取 Binder 机制,它也不是单线程的,但它在 fork() 前主动停止了其他线程,fork() 后重新启动了
- 2、效率问题:AMS和Zygote之间使用的LocalSocket,相对于网络Socket,减少了数据验证等环节,所以其实效率相对于正常的网络Socket会大幅的提升。虽然还是要经过两次拷贝,但是由于数据量并不大,所以其实影响并不明显
- 3、Binder拷贝问题:如果使用Binder机制的话,从Zygote中fork出子进程会拷贝Zygote中Binder对象。从而多占用了一块无用的内存区域
fork进程哪些内存段可以被继承
- 代码段:包含可执行程序的指令,子进程会继承父进程的代码段
- 数据段:包含已初始化的全局变量和静态变量,子进程会继承父进程的数据段
- BSS段:包含未初始化的全局变量和静态变量,子进程会继承父进程的BSS段,并且该段会被清零
以下内存段不会被继承:
- 堆段:存放动态分配的内存,比如通过malloc或new分配的内存。在执行fork创建新进程时,新进程的堆段不会继承父进程的内容,但是在Linux中,通常使用写时复制(Copy-On-Write)技术,因此只有在堆段中被修改时才会进行实际的复制
- 堆栈顶指针:指向栈段的顶部,子进程会获得与父进程相同的堆栈顶指针
- 栈段:存放函数调用时的局部变量、函数参数等。在执行fork创建新进程时,新进程会继承父进程的栈段。在Linux中,栈段通常也是写时复制的,因此只有在栈段中被修改时才会进行实际的复制