Activity 启动流程分析(一)
Android MVC 架构详解_lerendan的博客-CSDN博客_android mvc架构
@UiContext
public class Activity extends ContextThemeWrapper
implements LayoutInflater.Factory2,
Window.Callback, KeyEvent.Callback,
private IBinder mToken;
Activity是一个可定制资源的上下文,并实现Window,keyEvent的callback接口。
mToken是framework传过来的一个Binder代理对象,是一个句柄。这里仅用作标识,用于在addView添加窗口时,校验其是一个ActivityRecord容器的窗口
startActivity有两种方式:
1.通过Instrumentation对象
mInstrumentation.execStartActivity
2.直接通过ATMS代理对象,这是进程全局的,可通过反射使用
ActivityTaskManager.getService().startActivity
caller是一个代理对象,标识应用侧的主线程(IApplicationThread的实现类是主线程绑定looper的对象) callingPackage是发起startActivity语义的应用包名(一个进程可以有多个应用) intent是欲启动Activity的信息,resolvedType说明欲启动Activity的data类型(注册的IntentFilter) resultTo说明启动结果返回对象。A start B,B可以返回结果给A,这里resultTo便是A bOptions 可以指定欲启动Activity的一些附加信息,如task和window类型,边界rect等。 UserHandle.getCallingUserId()。startActivity是通过Binder 进程间通信的调用的,此时的framework的Binder线程,带有发起者的信息,用于鉴权。 Android支持多用户的,应用在安装时,由PMS分配uid。主用户userId为0。
@Override
public final int startActivity(IApplicationThread caller, String callingPackage,
String callingFeatureId, Intent intent, String resolvedType, IBinder resultTo,
String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo,
Bundle bOptions) {
return startActivityAsUser(caller, callingPackage, callingFeatureId, intent, resolvedType,
resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions,
UserHandle.getCallingUserId());
}
/*
public static @UserIdInt int getCallingUserId() {
return getUserId(Binder.getCallingUid());
}
*/
startActivity的核心逻辑全在以下四句方法:
mService.mGlobalLock是wm锁,故startActivity是线程安全的,多个应用可以同时调用,在系统处理时全程加锁。 在executeRequest中可能需要持有am锁,需小心,避免两个线程相互持锁,从而死锁。 clearCallingIdentity这个的意思,将Binder线程的calluid替换为当前进程的。 restoreCallingIdentity这个的意思,将Binder线程的calluid替换为发起请求进程的。 两者结合使用,以便方法接口权限校验和在本进程使用,避免使用错误身份。
synchronized (mService.mGlobalLock) {
final long origId = Binder.clearCallingIdentity();
res = executeRequest(mRequest);
Binder.restoreCallingIdentity(origId);
检查调用者信息,aInfo时wm通过pm查找的欲启动Activity的package和class信息,如果找不到,后续会报错。 这里也会检查,欲启动Activity是哪个用户的,启动双开应用,这里会有不同打印。 插件化的思想:在package中注册一些简易的ActivityInfo,以便通过框架这里的校验。
WindowProcessController callerApp = null;
if (caller != null) {
callerApp = mService.getProcessController(caller);
if (callerApp != null) {
callingPid = callerApp.getPid();
callingUid = callerApp.mInfo.uid;
} else {
Slog.w(TAG, "Unable to find app for caller " + caller + " (pid=" + callingPid
+ ") when starting: " + intent.toString());
err = ActivityManager.START_PERMISSION_DENIED;
}
}
final int userId = aInfo != null && aInfo.applicationInfo != null
? UserHandle.getUserId(aInfo.applicationInfo.uid) : 0;
if (err == ActivityManager.START_SUCCESS) {
Slog.i(TAG, "START u" + userId + " {" + intent.toShortString(true, true, true, false)
+ "} from uid " + callingUid);
}
校验result传递:A启动B,B启动C
A启动B时,A请求B返回结果(requestCode >= 0)B的resultTo为A,则B的resultRecord为A, 若此时B再启动C,B仅是跳转,(!(requestCode >= 0)),则C的resultRecord为B的resultTo,即A 注意B不能同时带FLAG_ACTIVITY_FORWARD_RESULT和(requestCode >= 0),同时声明转发和返回结果,语义冲突!
ActivityRecord sourceRecord = null;
ActivityRecord resultRecord = null;
if (resultTo != null) {
sourceRecord = mRootWindowContainer.isInAnyTask(resultTo);
if (DEBUG_RESULTS) {
Slog.v(TAG_RESULTS, "Will send result to " + resultTo + " " + sourceRecord);
}
if (sourceRecord != null) {
if (requestCode >= 0 && !sourceRecord.finishing) {
resultRecord = sourceRecord;
}
}
}
final int launchFlags = intent.getFlags();
if ((launchFlags & Intent.FLAG_ACTIVITY_FORWARD_RESULT) != 0 && sourceRecord != null) {
resultRecord = sourceRecord.resultTo;
startActivityUnchecked
deferWindowLayout 显式声明延迟布局,直到下一个continueWindowLayout语句,这段期间都延迟布局,除非force。这块在WindowSurfacePlacer中实现。
try {
mService.deferWindowLayout();
result = startActivityInner(r, sourceRecord, voiceSession, voiceInteractor,
} finally {
startedActivityRootTask = handleStartResult(r, result);
mService.continueWindowLayout();
startActivityInner
主要的处理逻辑便是根据各种条件,如何为一个ActivityRecord选择一个合适的rootTask
setInitialState
computeLaunchingTaskFlags
computeSourceRootTask // 计算mSourceRootTask
setInitialState
这里会mStartActivity = r;计算mStartActivity 的windowingMode及其Bound
// Preferred display id is the only state we need for now and it could be updated again
// after we located a reusable task (which might be resided in another display).
mSupervisor.getLaunchParamsController().calculate(inTask, r.info.windowLayout, r,
sourceRecord, options, mRequest, PHASE_DISPLAY, mLaunchParams);
mPreferredTaskDisplayArea = mLaunchParams.hasPreferredTaskDisplayArea()
? mLaunchParams.mPreferredTaskDisplayArea
: mRootWindowContainer.getDefaultTaskDisplayArea();
mPreferredWindowingMode = mLaunchParams.mWindowingMode;
computeLaunchingTaskFlags
根据mSourceRecord和mInTask,LaunchMode来计算LaunchFlag
if (mSourceRecord == null && mInTask != null && mInTask.getRootTask() != null) {
mSourceRecord == null说明,该次不是由一个Activity发起,mInTask != null说明指定了一个task参考来放置Activity。
从应用侧而言,可由getAppliaction().startActivity();从system来说,没有上下文限制,但一般都是由uiContext发起
getReusableTask
Activity有四种启动模式,是在这里对应用的声明进行选择的
if (putIntoExistingTask) {
if (LAUNCH_SINGLE_INSTANCE == mLaunchMode) {
// There can be one and only one instance of single instance activity in the
// history, and it is always in its own unique task, so we do a special search.
intentActivity = mRootWindowContainer.findActivity(mIntent, mStartActivity.info,
mStartActivity.isActivityTypeHome());
reusedTask是否找到一个已有的task可以将待启动的Activity加入其中,若找不到,则根据计算一个新的,可能为null
是否需要newTask,这是一个final字段。
// Compute if there is an existing task that should be used for.
final Task targetTask = reusedTask != null ? reusedTask : computeTargetTask();
final boolean newTask = targetTask == null;
mTargetTask = targetTask;
mStartActivity.resultTo == null,说明这个mStartActivity不需要返回结果,mInTask是否有声明一个参考task,
mAddingToTask说明,mStartActivity并没有加入一个task之中, 应用有指定了FLAG_ACTIVITY_NEW_TASK。这里有一点值得注意:同一个task才能返回结果
例如:在微信中设置头像,选择图库的照片,但如果此时图库的task与微信的task不是一个,此时,设置头像是不会返回结果的。有这段代码判断的:
private void sendNewTaskResultRequestIfNeeded() {
if (mStartActivity.resultTo != null && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
// For whatever reason this activity is being launched into a new task...
// yet the caller has requested a result back. Well, that is pretty messed up,
// so instead immediately send back a cancel and let the new task continue launched
// as normal without a dependency on its originator.
Slog.w(TAG, "Activity is launching as a new task, so cancelling activity result.");
mStartActivity.resultTo.sendResult(INVALID_UID, mStartActivity.resultWho,
mStartActivity.requestCode, RESULT_CANCELED,
null /* data */, null /* dataGrants */);
mStartActivity.resultTo = null;
}
}
private Task computeTargetTask() {
if (mStartActivity.resultTo == null && mInTask == null && !mAddingToTask
&& (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
// A new task should be created instead of using existing one.
return null;
} else if (mSourceRecord != null) {
return mSourceRecord.getTask();
} else if (mInTask != null) {
return mInTask;
} else {
final Task rootTask = getLaunchRootTask(mStartActivity, mLaunchFlags, null /* task */,
mOptions);
final ActivityRecord top = rootTask.getTopNonFinishingActivity();
if (top != null) {
return top.getTask();
} else { // 这说明该task是一个空的task,列表中里面没有Activity
// Remove the root task if no activity in the root task.
rootTask.removeIfPossible("computeTargetTask");
}
}
return null;
}
computeLaunchParams
在已计算targetTask之后,说明mStartActivity是加入该targetTask的。这时再重新计算一遍mLaunchParams
如果sourceRootTask.inSplitScreenWindowingMode为true,mStartActivity的windowingMode需要和sourceRootTask保持一致。
其实也可以做实验:sourceRootTask在Primary,mStartActivity debug到Secondary,也是可以的。
如freefrom小窗,则可以在calculate进行计算
if (newTask) { // mTargetRootTask.reuseOrCreateTask
setNewTask(taskToAffiliate);
} else if (mAddingToTask) {
addOrReparentStartingActivity(targetTask, "adding to task");
}
Task有一个mChildren的列表,里面的元素是ActivityRecord
startActivity分为两步: 将一个Activity加入到一个合适Task,作为其最后的子节点 再选择一个task作为focus,调度Task
private void addOrReparentStartingActivity(Task parent, String reason) {
if (mStartActivity.getTask() == null || mStartActivity.getTask() == parent) {
parent.addChild(mStartActivity);
} else {
mStartActivity.reparent(parent, parent.getChildCount() /* top */, reason);
}
}
setAlwaysOnTop说明该Task总是显示在上层。同类型的task,new的在上层
dream>pip>freefrom>alwaysOnTop
mTargetRootTask.getRootTask().moveToFront("reuseOrNewTask", targetTask);
if (mOptions != null) {
if (mOptions.getTaskAlwaysOnTop()) {
mTargetRootTask.setAlwaysOnTop(true);
}
}
S上Task分为rootTask(ActivityStack)和leafTask(Task)
startActivityLocked
mTargetRootTask.startActivityLocked
RootTask也有一个mChildren列表,子节点是Task。故这里需要将mStartActivity所在的task移到top(列表最后一个)
allowMoveToFront 准备一个切换动画。注意前面有mService.deferWindowLayout();故只是声明,并不是立马执行
showStartingWindow,同时添加一个启动窗口。启动窗口是system直接addView的,比Activity的生命周期调度要快,给用户流畅的视效
if (!r.mLaunchTaskBehind && allowMoveToFront && (!isOrhasTask || newTask)) {
positionChildAtTop(rTask);
}
// r应该是下个用户看到的界面,如果其所在的Task,在调度之前,有另外的Activity在r之前,则需要再次将r移到top
//
final ActivityRecord occludingActivity = getOccludingActivityAbove(r);
rTask.positionChildAtTop(r);
//再次将r移到top // 显然,如果r已是top,这次调用也无妨
task.positionChildAtTop(r);
if ((!isHomeOrRecentsRootTask() || hasActivity()) && allowMoveToFront) {
mTargetRootTask将其移到top
mRootWindowContainer.resumeFocusedTasksTopActivities 这是调度Task的入口,可以指定mTargetRootTask
// If the target root-task was not previously focusable (previous top running
// activity on that root-task was not visible) then any prior calls to move the
// root-task to the will not update the focused root-task. If starting the new
// activity now allows the task root-task to be focusable, then ensure that we
// now update the focused root-task accordingly.
if (mTargetRootTask.isTopActivityFocusable()
&& !mRootWindowContainer.isTopDisplayFocusedRootTask(mTargetRootTask)) {
mTargetRootTask.moveToFront("startActivityInner");
}
mRootWindowContainer.resumeFocusedTasksTopActivities(
mTargetRootTask, mStartActivity, mOptions, mTransientLaunch);
}
resumeTopActivityUncheckedLocked
将栈顶的Activity,调度到resume生命周期
mInResumeTopActivity 防止重复迭代调用
第一次isLeafTask为false,在mChildren,从上到下开始遍历,再次调用child的方法,从而走到resumeTopActivityInnerLocked
看注释,存在数组越界的异常,不知道是什么样的场景下的异常
锁屏休眠时,startActivity,并不一定保证,该Activity能立马展现给用户。如果不能点亮屏幕,则system会再次休眠。
checkReadyForSleep会将所有的Activity调度到stop状态
比如来电时,就有可能存在不亮屏的故障。
boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options,
boolean deferPause) {
if (mInResumeTopActivity) {
return false;
}
boolean someActivityResumed = false;
try {
mInResumeTopActivity = true;
if (isLeafTask()) {
if (isFocusableAndVisible()) {
someActivityResumed = resumeTopActivityInnerLocked(prev, options, deferPause);
}
} else {
int idx = mChildren.size() - 1;
while (idx >= 0) {
final Task child = (Task) getChildAt(idx--);
if (!child.isTopActivityFocusable()) {
continue;
}
if (child.getVisibility(null /* starting */) != TASK_VISIBILITY_VISIBLE) {
break;
}
someActivityResumed |= child.resumeTopActivityUncheckedLocked(prev, options,
deferPause);
if (idx >= mChildren.size()) {
idx = mChildren.size() - 1;
}
}
}
final ActivityRecord next = topRunningActivity(true /* focusableOnly */);
if (next == null || !next.canTurnScreenOn()) {
checkReadyForSleep();
}
} finally {
mInResumeTopActivity = false;
}
return someActivityResumed;
}
resumeTopActivityInnerLocked
R上,mTopActivityOccludesKeyguard是一个缓存值,在一些情况下会判断错误。S上已没有该mTopActivityOccludesKeyguard变量,在keyguardController使用实时计算值
在Resume某个Activity之前,需要将所有的Activity,置于pause,allPausedComplete
若Activity所在的进程还没有,则会异步启动进程,返回。待进程启动后,会立马再次resume对应的Activity
if (next.attachedToProcess()) { 走到这,说明应用的进程已经启动
if (pausing) {
if (next.attachedToProcess()) {
next.app.updateProcessInfo(false /* updateServiceConnectionActivities */,
} else if (!next.isProcessRunning()) {
final boolean isTop = this == taskDisplayArea.getFocusedRootTask();
mAtmService.startProcessAsync(next, false /* knownToBeDead */, isTop,
isTop ? "pre-top-activity" : "pre-activity");
}
return true;
next从这里开始可见,会将next加入open列表中
ResumeActivityItem Binder进程间通信,最终会走到ActivityThread类中
hasBeenLaunched说明启动出现错误,再次启动一次startSpecificActivity
// This activity is now becoming visible.
if (!next.mVisibleRequested || next.stopped || lastActivityTranslucent) {
next.setVisibility(true);
}
transaction.setLifecycleStateRequest(
ResumeActivityItem.obtain(next.app.getReportedProcState(),
dc.isNextTransitionForward()));
mAtmService.getLifecycleManager().scheduleTransaction(transaction);
if (!next.hasBeenLaunched) {
next.hasBeenLaunched = true;
}
mTaskSupervisor.startSpecificActivity(next, true, false);