不知道大家有没有好奇过点击 Launcher 图标时,到唤起一个应用页面,这个流程会是怎么样的?那这篇文章的目的就是尽可能梳理清楚流程,能够让大家对整个流程有一个相对清晰的认知。下面跟着小编一起学习学习。
Android 是一种基于 Linux 的自由及开放源代码的操作系统,主要使用于移动设备,如智能手机和平板电脑,由 Google 公司和开放手机联盟领导及开发。尚未有统一中文名称,中国大陆地区较多人使用 "安卓" 或 "安致"。
前言
在我们开始之前,希望您能最好已经满足以下条件:
1、有一份编译后的 Android 源码(亲自动手实践才会有更深入的理解)
2、对 Binder 机制有一定的了解
本文启动流程分析基于 Android 5.1 的源码。为什么是 5.1 的源码呢?因为手边编译完的代码只有这个版本… 另外,用什么版本的源码并不重要,大体的流程并无本质上的区别,仅仅是实现细节的调整,找一个你熟悉的版本就好。
1、启动时序图
作为一个轻微强迫症的人,整理的时序图,相信大家按图索骥,一定能搞明白整个启动流程:
说明:为了让大家更清楚的理解整个过程,将时序图中划分为三个部分:Launcher 进程、System 进程、App 进程,其中有涉及共用的类以 L / A 进行区分表示跟哪个进程有关,便于理解。
2、关键类说明
整个启动流程因为会涉及到多次 Binder 通信,这里先简要说明一下几个类的用途,方便大家理解整个交互流程:
1、ActivityManagerService:AMS 是 Android 中最核心的服务之一,主要负责系统中四大组件的启动、切换、调度及应用进程的管理和调度等工作,其职责与操作系统中的进程管理和调度模块相类似,因此它在 Android 中非常重要,它本身也是一个 Binder 的实现类。
2、Instrumentation:顾名思义,它用来监控应用程序和系统的交互。
3、ActivityThread:应用的入口类,系统通过调用 main 函数,开启消息循环队列。ActivityThread 所在线程被称为应用的主线程(UI 线程)。
4、ApplicationThread:ApplicationThread 提供 Binder 通讯接口,AMS 则通过代理调用此 App 进程的本地方法。
5、ActivityManagerProxy:AMS 服务在当前进程的代理类,负责与 AMS 通信。
6、ApplicationThreadProxy:ApplicationThread 在 AMS 服务中的代理类,负责与 ApplicationThread 通信。
3、流程分析
首先交代下整个流程分析的场景:用户点击 Launcher 上的应用图标到该应用主界面启动展示在用户眼前。
这整个过程涉及到跨进程通信,所以我们将其划分为时序图中所展示三个进程:Launcher 进程、System 进程、App 进程。为了不贴过长的代码又能说清楚进程间交互的流程,这里简述几个重要的交互点。
从时序图上大家也可以看到调用链相当长,对应的代码量也比较大,而且时序图只是分析了这个一个场景下的流程。道阻且长,行则将至!
3.1 Launcher 响应用户点击,通知 AMS
Launcher 做为应用的入口,还是有必要交代一下的,我们来看看 Launcher 的代码片段,Launcher 使用的是 packages/apps/Launcher3 的的源码。
- public class Launcher extends Activity
- implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks,
- View.OnTouchListener, PageSwitchListener, LauncherProviderChangeListener {
- ...
- /**
- * Launches the intent referred by the clicked shortcut.
- *
- * @param v The view representing the clicked shortcut.
- */
- public void onClick(View v) {
- // Make sure that rogue clicks don't get through while allapps is launching, or after the
- // view has detached (it's possible for this to happen if the view is removed mid touch).
- if (v.getWindowToken() == null) {
- return;
- }
- ...
- Object tag = v.getTag();
- if (tag instanceof ShortcutInfo) {
- onClickAppShortcut(v);
- } else if (tag instanceof FolderInfo) {
- ...
- } else if (v == mAllAppsButton) {
- onClickAllAppsButton(v);
- } else if (tag instanceof AppInfo) {
- startAppShortcutOrInfoActivity(v);
- } else if (tag instanceof LauncherAppWidgetInfo) {
- ...
- }
- }
- private void startAppShortcutOrInfoActivity(View v) {
- ...
- boolean success = startActivitySafely(v, intent, tag);
- ...
- }
- boolean startActivitySafely(View v, Intent intent, Object tag) {
- ...
- try {
- success = startActivity(v, intent, tag);
- } catch (ActivityNotFoundException e) {
- ...
- }
- return success;
- }
- boolean startActivity(View v, Intent intent, Object tag) {
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- try {
- ...
- if (user == null || user.equals(UserHandleCompat.myUserHandle())) {
- // Could be launching some bookkeeping activity
- startActivity(intent, optsBundle);
- } else {
- ...
- }
- return true;
- } catch (SecurityException e) {
- ...
- }
- return false;
- }
- }
通过 starActicity 辗转调用到
而后则调用至
- Activity:startActivityForResult
,代码片段如下:
- Instrumentation:execStartActivity
- public class Instrumentation {
- ...
- public ActivityResult execStartActivity(
- Context who, IBinder contextThread, IBinder token, Activity target,
- Intent intent, int requestCode, Bundle options) {
- IApplicationThread whoThread = (IApplicationThread) contextThread;
- ...
- try {
- ...
- int result = ActivityManagerNative.getDefault()
- .startActivity(whoThread, who.getBasePackageName(), intent,
- intent.resolveTypeIfNeeded(who.getContentResolver()),
- token, target != null ? target.mEmbeddedID : null,
- requestCode, 0, null, options);
- ...
- } catch (RemoteException e) {
- }
- return null;
- }
- ...
- }
这里的
返回
- ActivityManagerNative.getDefault
的远程接口,即
- ActivityManagerService
接口,有人可能会问了为什么会是
- ActivityManagerProxy
,这就涉及到 Binder 通信了,这里不再展开。通过 Binder 驱动程序,
- ActivityManagerProxy
与 AMS 服务通信,则实现了跨进程到 System 进程。
- ActivityManagerProxy
3.2 AMS 响应 Launcher 进程请求
从上面的流程我们知道,此时 AMS 应该处理 Launcher 进程发来的请求,请参看时序图及源码,此时我们来看
方法,目测这个方法已经超过 600 行代码,来看一些关键代码片段:
- ActivityStackSupervisor:startActivityUncheckedLocked
- public final class ActivityStackSupervisor implements DisplayListener {
- ...
- final int startActivityUncheckedLocked(ActivityRecord r, ActivityRecord sourceRecord,
- IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
- int startFlags, boolean doResume, Bundle options, TaskRecord inTask) {
- final Intent intent = r.intent;
- final int callingUid = r.launchedFromUid;
- ...
- final boolean launchSingleTop = r.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP;
- final boolean launchSingleInstance = r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE;
- final boolean launchSingleTask = r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK;
- int launchFlags = intent.getFlags();
- ...
- // We'll invoke onUserLeaving before onPause only if the launching
- // activity did not explicitly state that this is an automated launch.
- mUserLeaving = (launchFlags & Intent.FLAG_ACTIVITY_NO_USER_ACTION) == 0;
- ...
- ActivityRecord notTop =
- (launchFlags & Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP) != 0 ? r : null;
- // If the onlyIfNeeded flag is set, then we can do this if the activity
- // being launched is the same as the one making the call... or, as
- // a special case, if we do not know the caller then we count the
- // current top activity as the caller.
- if ((startFlags&ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) {
- ...
- }
- ...
- // If the caller is not coming from another activity, but has given us an
- // explicit task into which they would like us to launch the new activity,
- // then let's see about doing that.
- if (sourceRecord == null && inTask != null && inTask.stack != null) {
- final Intent baseIntent = inTask.getBaseIntent();
- final ActivityRecord root = inTask.getRootActivity();
- ...
- // If this task is empty, then we are adding the first activity -- it
- // determines the root, and must be launching as a NEW_TASK.
- if (launchSingleInstance || launchSingleTask) {
- ...
- }
- ...
- }
- ...
- if (inTask == null) {
- if (sourceRecord == null) {
- // This activity is not being started from another... in this
- // case we -always- start a new task.
- if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) == 0 && inTask == null) {
- Slog.w(TAG, "startActivity called from non-Activity context; forcing " +
- "Intent.FLAG_ACTIVITY_NEW_TASK for: " + intent);
- launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
- }
- } else if (sourceRecord.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
- // The original activity who is starting us is running as a single
- // instance... this new activity it is starting must go on its
- // own task.
- launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
- } else if (launchSingleInstance || launchSingleTask) {
- // The activity being started is a single instance... it always
- // gets launched into its own task.
- launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
- }
- }
- ...
- // We may want to try to place the new activity in to an existing task. We always
- // do this if the target activity is singleTask or singleInstance; we will also do
- // this if NEW_TASK has been requested, and there is not an additional qualifier telling
- // us to still place it in a new task: multi task, always doc mode, or being asked to
- // launch this as a new task behind the current one.
- if (((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0 &&
- (launchFlags & Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
- || launchSingleInstance || launchSingleTask) {
- // If bring to front is requested, and no result is requested and we have not
- // been given an explicit task to launch in to, and
- // we can find a task that was started with this same
- // component, then instead of launching bring that one to the front.
- if (inTask == null && r.resultTo == null) {
- // See if there is a task to bring to the front. If this is
- // a SINGLE_INSTANCE activity, there can be one and only one
- // instance of it in the history, and it is always in its own
- // unique task, so we do a special search.
- ActivityRecord intentActivity = !launchSingleInstance ?
- findTaskLocked(r) : findActivityLocked(intent, r.info);
- if (intentActivity != null) {
- ...
- }
- }
- }
- ...
- if (r.packageName != null) {
- // If the activity being launched is the same as the one currently
- // at the top, then we need to check if it should only be launched
- // once.
- ActivityStack topStack = getFocusedStack();
- ActivityRecord top = topStack.topRunningNonDelayedActivityLocked(notTop);
- if (top != null && r.resultTo == null) {
- if (top.realActivity.equals(r.realActivity) && top.userId == r.userId) {
- ...
- }
- }
- } else{
- ...
- }
- boolean newTask = false;
- boolean keepCurTransition = false;
- TaskRecord taskToAffiliate = launchTaskBehind && sourceRecord != null ?
- sourceRecord.task : null;
- // Should this be considered a new task?
- if (r.resultTo == null && inTask == null && !addingToTask
- && (launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
- ...
- if (reuseTask == null) {
- r.setTask(targetStack.createTaskRecord(getNextTaskId(),
- newTaskInfo != null ? newTaskInfo : r.info,
- newTaskIntent != null ? newTaskIntent : intent,
- voiceSession, voiceInteractor, !launchTaskBehind /* toTop */),
- taskToAffiliate);
- ...
- } else {
- r.setTask(reuseTask, taskToAffiliate);
- }
- ...
- } else if (sourceRecord != null) {
- } else if (!addingToTask &&
- (launchFlags&Intent.FLAG_ACTIVITY_REORDER_TO_FRONT) != 0) {
- } else if (inTask != null){
- } else {
- }
- ...
- targetStack.startActivityLocked(r, newTask, doResume, keepCurTransition, options);
- ...
- return ActivityManager.START_SUCCESS;
- }
- ...
- }
函数经过 intent 的标志值设置,通过
函数来查找存不存这样的 Task,这里返回的结果是 null,即
- findTaskLocked
为 null,因此,需要创建一个新的 Task 来启动这个
- intentActivity
。现在处理堆栈顶端的
- Activity
是
- Activity
,与我们即将要启动的
- Launcher
不是同一个
- MainActivity
,创建了一个新的 Task 里面来启动这个
- Activity
。
- Activity
经过栈顶检测,则需要将 Launcher 推入 Paused 状态,才可以启动新的
。后续则调用至
- Activity
,我们来看一下这个函数:
- ActivityStack:startPausingLocked
- final class ActivityStack {
- ...
- final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping, boolean resuming,
- boolean dontWait) {
- if (mPausingActivity != null) {
- ...
- }
- ActivityRecord prev = mResumedActivity;
- if (prev == null) {
- ...
- }
- ...
- mResumedActivity = null;
- mPausingActivity = prev;
- mLastPausedActivity = prev;
- mLastNoHistoryActivity = (prev.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_HISTORY) != 0
- || (prev.info.flags & ActivityInfo.FLAG_NO_HISTORY) != 0 ? prev : null;
- prev.state = ActivityState.PAUSING;
- ...
- if (prev.app != null && prev.app.thread != null) {
- try {
- ...
- prev.app.thread.schedulePauseActivity(prev.appToken, prev.finishing,
- userLeaving, prev.configChangeFlags, dontWait);
- } catch (Exception e) {
- ...
- }
- } else {
- ...
- }
- ...
- }
- ...
- }
这里的
是一个
- prev.app.thread
对象的远程接口,通过调用这个远程接口的
- ApplicationThread
来通知 Launcher 进入 Paused 状态。至此,AMS 对 Launcher 的请求已经响应,这是我们发现又通过 Binder 通信回调至 Launcher 进程。
- schedulePauseActivity
3.3 Launcher 进程挂起 Launcher,再次通知 AMS
这个流程相对会简单一些,我们来看
:
- ActivityThread
- public final class ActivityThread {
- ...
- private void handlePauseActivity(IBinder token, boolean finished,
- boolean userLeaving, int configChanges, boolean dontReport) {
- ActivityClientRecord r = mActivities.get(token);
- if (r != null) {
- ...
- performPauseActivity(token, finished, r.isPreHoneycomb());
- // Make sure any pending writes are now committed.
- if (r.isPreHoneycomb()) {
- QueuedWork.waitToFinish();
- }
- // Tell the activity manager we have paused.
- if (!dontReport) {
- try {
- ActivityManagerNative.getDefault().activityPaused(token);
- } catch (RemoteException ex) {
- }
- }
- ...
- }
- }
- ...
- }
这部分 Launcher 的
处理页面 Paused 并且再次通过
- ActivityThread
通知 AMS。
- ActivityManagerProxy
3.4 AMS 创建新的进程
- public final class ActivityManagerService extends ActivityManagerNative
- implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
- ...
- private final void startProcessLocked(ProcessRecord app, String hostingType, String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {
- ...
- try {
- ...
- // Start the process. It will either succeed and return a result containing
- // the PID of the new process, or else throw a RuntimeException.
- boolean isActivityProcess = (entryPoint == null);
- if (entryPoint == null) entryPoint = "android.app.ActivityThread";
- Process.ProcessStartResult startResult = Process.start(entryPoint,
- app.processName, uid, uid, gids, debugFlags, mountExternal,
- app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
- app.info.dataDir, entryPointArgs);
- ...
- } catch () {
- ...
- }
- }
- ...
- }
这里主要是调用
接口来创建一个新的进程,新的进程会导入
- Process:start
类,并且执行它的
- android.app.ActivityThread
函数,这就是每一个应用程序都有一个
- main
实例来对应的原因。
- ActivityThread
3.5 应用进程初始化
我们来看
的
- Activity
函数,这里绑定了主线程的 Looper,并进入消息循环,大家应该知道,整个 Android 系统是消息驱动的,这也是为什么主线程默认绑定 Looper 的原因:
- main
- public final class ActivityThread {
- ...
- public static void main(String[] args) {
- ...
- Looper.prepareMainLooper();
- ActivityThread thread = new ActivityThread();
- thread.attach(false);
- ...
- Looper.loop();
- ...
- }
- private void attach(boolean system) {
- ...
- if (!system) {
- ...
- final IActivityManager mgr = ActivityManagerNative.getDefault();
- try {
- mgr.attachApplication(mAppThread);
- } catch (RemoteException ex) {
- // Ignore
- }
- } else {
- ...
- }
- ...
- }
- ...
- }
attach 函数最终调用了
的远程接口
- ActivityManagerService
函数,传入的参数是
- ActivityManagerProxy的attachApplication
,这是一个
- mAppThread
类型的
- ApplicationThread
对象,它的作用是 AMS 与应用进程进行进程间通信的。
- Binder
3.6 在 AMS 中注册应用进程,启动启动栈顶页面
前面我们提到了 AMS 负责系统中四大组件的启动、切换、调度及应用进程的管理和调度等工作,通过上一个流程我们知道应用进程创建后通过 Binder 驱动与 AMS 产生交互,此时 AMS 则将应用进程创建后的信息进行了一次注册,如果拿 Windows 系统程序注册到的注册表来理解这个过程,可能会更形象一些。
从堆栈顶端取出要启动的
- mMainStack.topRunningActivityLocked(null)
,并在
- Activity
函数中通过
- realStartActivityLockedhan
调回 App 进程启动页面。
- ApplicationThreadProxy
- public final class ActivityStackSupervisor implements DisplayListener {
- ...
- final boolean realStartActivityLocked(ActivityRecord r,
- ProcessRecord app, boolean andResume, boolean checkConfig)
- throws RemoteException {
- ...
- r.app = app;
- ...
- try {
- ...
- app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
- System.identityHashCode(r), r.info, new Configuration(mService.mConfiguration),
- r.compat, r.launchedFromPackage, r.task.voiceInteractor, app.repProcState,
- r.icicle, r.persistentState, results, newIntents, !andResume,
- mService.isNextTransitionForward(), profilerInfo);
- ...
- } catch (RemoteException e) {
- ...
- }
- ...
- }
- ...
- }
此时在 App 进程,我们可以看到,经过一些列的调用链最终调用至
函数,之后会调用至
- MainActivity:onCreate
,而后会通知 AMS 该
- onResume
已经处于
- MainActivity
状态。至此,整个启动流程告一段落。
- resume
4、总结
通过上述流程,相信大家可以有了一个基本的认知,这里我们忽略细节简化流程,单纯从进程角度来看下图: launch_app_sim
图上所画这里就不在赘述,Activity 启动后至 Resume 状态,此时可交互。以上就是分析 Android 中应用启动流程的全部内容了,如何有疑问欢迎大家指正交流。
来源: http://www.phperz.com/article/17/0316/290415.html