android N进程启动流程(一)(捕获输入事件、准备创建activity、焦点切换)
1. 背景
本文主要针对event log中各处节点进行进程启动流程分析。
01-01 00:34:12.298349 8599 8599 I InputManager: PERF:injectInputEvent: MotionEvent { action=ACTION_UP...
01-01 00:34:12.311531 1135 3103 I am_create_task: [0,38]
01-01 00:34:12.311755 1135 3103 I am_create_activity: [0,201384592,38,test2.com.myapplication/.MainActivity,android.intent.action.MAIN...
01-01 00:34:12.318456 1135 3103 I am_focused_stack: [0,1,0,startedActivity setFocusedActivity]
01-01 00:34:12.323919 1135 3103 I am_focused_activity: [0,test2.com.myapplication/.MainActivity,startedActivity]
01-01 00:34:12.324637 1135 3103 I am_pause_activity: [0,125629571,com.android.launcher/.Launcher]
01-01 00:34:12.342909 2699 2699 I am_on_paused_called: [0,com.android.launcher.Launcher,handlePauseActivity]
01-01 00:34:12.398299 1135 1698 I am_proc_start: [0,8610,10123,test2.com.myapplication,activity,test2.com.myapplication/.MainActivity]
01-01 00:34:12.429265 1135 1697 I am_proc_bound: [0,8610,test2.com.myapplication]
01-01 00:34:12.439813 1135 1697 I am_restart_activity: [0,201384592,38,test2.com.myapplication/.MainActivity]
01-01 00:34:12.651913 8610 8610 I am_on_resume_called: [0,test2.com.myapplication.MainActivity,LAUNCH_ACTIVITY]
01-01 00:34:12.795221 1135 1204 I am_activity_launch_time: [0,201384592,test2.com.myapplication/.MainActivity,417,417]
01-01 00:34:12.832973 1135 1610 I am_stop_activity: [0,125629571,com.android.launcher/.Launcher]
01-01 00:34:12.835790 2699 2699 I am_on_stop_called: [0,com.android.launcher.Launcher,handleStopActivity]
下面主要按照上述流程部分来讲解:
- 1、捕获输入事件
- 2、准备创建activity
- 3、焦点切换
- 4、上一个activity的暂停,如此处是launcher桌面
- 5、进程启动
- 6、绑定与创建application
- 7、创建activity实例并且调用onCreate
- 8、调用active的resume方法
- 9、界面添加完成并可见
- 10、上一个activity的停止
此处讲解的流程:
=> 在桌面(如android原生luancher:com.android.launcher) -> 点击test2测试应用的图标(test2.com.myapplication) -> test2完全显示给用户
ps:测试应用test2在该操作之前是没有进程在后台运行的。
2. 捕获输入事件
输入事件一般情况有2种: 一种是从触摸屏点击屏幕;一种是自动化软件模拟点击事件。
图2.1 捕获输入事件流程图
2.1 触摸点击屏幕
当用户手动点击的时候,会输出类似如下日志,如果没有可以自行添加:
01-01 22:31:24.065 868 1085 D InputReader: PERF:AMOTION_EVENT_ACTION_POINTER_UP:Up
代码位置:
InputReader.cpp (frameworks\native\services\inputflinger)
void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) {
while (!upIdBits.isEmpty()) {
uint32_t upId = upIdBits.clearFirstMarkedBit();
{
ALOGD("PERF:AMOTION_EVENT_ACTION_POINTER_UP:Up");
dispatchMotion(when, policyFlags, mSource,
AMOTION_EVENT_ACTION_POINTER_UP, 0, 0, metaState, buttonState, 0,
}
}
while (!downIdBits.isEmpty()) {
if (dispatchedIdBits.count() == 1) {
mDownTime = when;
ALOGD("PERF:AMOTION_EVENT_ACTION_POINTER_DOWN:Down");
}
dispatchMotion(when, policyFlags, mSource,
AMOTION_EVENT_ACTION_POINTER_DOWN, 0, 0, metaState, buttonState, 0,
一般响应时间从点击抬起开始算时间(一般认为用户手抬起了,就认为用户的操作已经表述清楚,需要尽快处理)
2.2 自动化模拟点击事件
此处方法介绍最常见的2种:
- 使用Instrumentation(代理控制框架)
- 使用adb指令:input tap
两种达到的效果是一致的,至于开始的地方可以添加自己想要的日志信息。
1、使用Instrumentationm模拟点击事件
Instrumentation inst = new Instrumentation();
long now = SystemClock.uptimeMillis();
inst.sendPointerSync(MotionEvent.obtain(now, now, MotionEvent.ACTION_DOWN, mx, my, 0));
now = SystemClock.uptimeMillis();
inst.sendPointerSync(MotionEvent.obtain(now + 10, now + 10, MotionEvent.ACTION_UP, mx, my, 0));
上述是模拟点击事件的方法,通过这个方法其实很多应用都可以实现自动化测试,这个方法是很基础的,如有需要大家可以自行拓展。
可以添加输出日志的地方:
// frameworks/base/core/java/android/hardware/input/InputManager.java
public boolean injectInputEvent(InputEvent event, int mode) {
Log.i(TAG, "PERF:injectInputEvent: " + event);
return mIm.injectInputEvent(event, mode);
}
2、使用adb指令:input tap
adb shell input tap mx my
至于日志LOG输出的地方可以参考上面第一种方法中的injectInputEvent
函数
3. 准备创建activity
Launcher响应打开事件(如点击一个应用图标)会启动应用,会调用Activity或者ContextImpl的startActivity启动应用。
图3.1 准备创建activity流程图
3.1 startActivity(ContextImpl.java)
ContextImpl.java的startActivity,应用一般都会有context实例,启动应用最简单的办法是startActivity+传递一个intent
public void startActivity(Intent intent) {
startActivity(intent, null);
}
public void startActivity(Intent intent, Bundle options) {
mMainThread.getInstrumentation().execStartActivity(
getOuterContext(), mMainThread.getApplicationThread(), null,
(Activity) null, intent, -1, options);
}
3.2 execStartActivity(Instrumentation.java)
Instrumentation.java其是一个工具类,可以用来启动应用
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {
int result = ActivityManagerNative.getDefault()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target != null ? target.mEmbeddedID : null,
requestCode, 0, null, options);
}
3.3 startActivity(ActivityManagerService.java)
ActivityManagerService.java这里最后会走到ActivityStarter的 startActivityMayWait函数,才是真正的启动应用的地方
public final int startActivity(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) {
return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
resultWho, requestCode, startFlags, profilerInfo, bOptions,
UserHandle.getCallingUserId());
}
public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {
return mActivityStarter.startActivityMayWait(caller, -1, callingPackage, intent,
resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
profilerInfo, null, null, bOptions, false, userId, null, null);
}
3.4 startActivityMayWait(ActivityStarter.java)
ActivityStarter活动对象启动器
final int startActivityMayWait(IApplicationThread caller, int callingUid,
ResolveInfo rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId);
ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo);
int res = startActivityLocked(caller, intent, ephemeralIntent, resolvedType,
if (outResult != null) {
do {
try {
mService.wait();
} catch (InterruptedException e) {
}
} while (outResult.result != START_TASK_TO_FRONT
&& !outResult.timeout && outResult.who == null);
}
...
}
启动活动对象之前会先解析意图,和activity的Info,使用AMS的startActivity传递的outResult参数一般都是null,故不会等待结果。
ps:自动化测试中使用的是AMS的startActivityAndWait,里面传递的WaitResult对象不为null,此时会等到界面绘制完成才会返回结果
3.5 startActivityLocked
final int startActivityLocked(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
ActivityRecord r = new ActivityRecord(mService, callerApp, callingUid, callingPackage,
intent, resolvedType, aInfo, mService.mConfiguration, resultRecord, resultWho,
requestCode, componentSpecified, voiceSession != null, mSupervisor, container,
options, sourceRecord);
doPendingActivityLaunchesLocked(false);
err = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor,
startFlags, true, options, inTask);
3.6 startActivityUnchecked
- 此处会启动新的ActivityStack,输出am_create_task
- 输出am_create_activity,准备创建activity
- 设置焦点、焦点切换
- 上一个activity的onpause
private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask) {
setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
voiceInteractor);
if (mStartActivity.resultTo == null && mInTask == null && !mAddingToTask
&& (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
newTask = true;
setTaskFromReuseOrCreateNewTask(taskToAffiliate);
}
if (newTask) {
EventLog.writeEvent(
EventLogTags.AM_CREATE_TASK, mStartActivity.userId, mStartActivity.task.taskId);
}
ActivityStack.logStartActivity(
EventLogTags.AM_CREATE_ACTIVITY, mStartActivity, mStartActivity.task);
mTargetStack.startActivityLocked(mStartActivity, newTask, mKeepCurTransition, mOptions);
if (mDoResume) {
if (!mLaunchTaskBehind) {
mService.setFocusedActivityLocked(mStartActivity, "startedActivity");
}
final ActivityRecord topTaskActivity = mStartActivity.task.topRunningActivityLocked();
mSupervisor.resumeFocusedStackTopActivityLocked(mTargetStack, mStartActivity,
mOptions);
}
4. 焦点切换
接着章节3.6的startActivityUnchecked中会调用setFocusedActivityLocked(ActivityManagerService.java),
此处会进行焦点切换。
图4.1 焦点切换流程图
4.1 setFocusedActivityLocked(ActivityManagerService.java)
里面包括3个部分
- moveActivityStackToFront移动堆栈到顶端
- setFocusedApp在WMS中设置focus的app,属于窗体焦点变换
- event log中输出am_focused_activity,代表已经在AMS中设置了focus的对象
boolean setFocusedActivityLocked(ActivityRecord r, String reason) {
mFocusedActivity = r;
if (mStackSupervisor.moveActivityStackToFront(r, reason + " setFocusedActivity")) {
mWindowManager.setFocusedApp(r.appToken, true);
}
EventLogTags.writeAmFocusedActivity(
mFocusedActivity == null ? -1 : mFocusedActivity.userId,
mFocusedActivity == null ? "NULL" : mFocusedActivity.shortComponentName,
reason);
return true;
}
4.2 moveActivityStackToFront(ActivityStackSupervisor.java)
移动堆栈,此处会将test2移动到堆栈顶端:
- mStackSupervisor.setFocusStackUnchecked,event log中输出am_focused_stack,代表堆栈的焦点已经发生变化
- insertTaskAtTop,将test2的stack移动到最顶端
- moveTaskToTop插入WMS窗体相关的task的顶端
boolean moveActivityStackToFront(ActivityRecord r, String reason) {
…
stack.moveToFront(reason, task);
return true;
}
void moveToFront(String reason, TaskRecord task) {
if (isOnHomeDisplay()) {
mStackSupervisor.setFocusStackUnchecked(reason, this);
}
if (task != null) {
insertTaskAtTop(task, null);
}
if (task != null) {
mWindowManager.moveTaskToTop(task.taskId);
}
}
void setFocusStackUnchecked(String reason, ActivityStack focusCandidate) {
if (focusCandidate != mFocusedStack) {
mLastFocusedStack = mFocusedStack;
mFocusedStack = focusCandidate;
EventLogTags.writeAmFocusedStack(
mCurrentUser, mFocusedStack == null ? -1 : mFocusedStack.getStackId(),
mLastFocusedStack == null ? -1 : mLastFocusedStack.getStackId(), reason);
}
}
4.3 setFocusedApp(WindowManagerService.java)
setFocusedApp用户更新当前WMS focus的窗体
public void setFocusedApp(IBinder token, boolean moveFocusNow) {
final boolean changed = mFocusedApp != newFocus;
if (moveFocusNow && changed) {
updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true );
}
updateFocusedWindowLocked会先遍历查找最新的焦点,如果窗体焦点有变化,则进行更新。
至于遍历查找窗体焦点是在computeFocusedWindowLocked完成的
此处会输出Changing focus from Window*** to null
boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
WindowState newFocus = computeFocusedWindowLocked();
if (mCurrentFocus != newFocus) {
if (DEBUG_FOCUS_LIGHT || localLOGV) Slog.v(TAG_WM, "Changing focus from " +
mCurrentFocus + " to " + newFocus + " Callers=" + Debug.getCallers(4));
final WindowState oldFocus = mCurrentFocus;
mCurrentFocus = newFocus;
if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS) {
mInputMonitor.setInputFocusLw(mCurrentFocus, updateInputWindows);
}
}
return false;
}
computeFocusedWindowLocked这个函数是会遍历当前显示的设备(可能有多个显示设备),返回当前系统焦点所在的窗体
private WindowState computeFocusedWindowLocked() {
final int displayCount = mDisplayContents.size();
for (int i = 0; i < displayCount; i++) {
final DisplayContent displayContent = mDisplayContents.valueAt(i);
WindowState win = findFocusedWindowLocked(displayContent);
if (win != null) {
return win;
}
}
return null;
}
findFocusedWindowLocked查找当前需要focus的窗体,如果之前有focus,而且需要切换到别的focus的activity窗体的时候,会把上一个launcher的窗体的焦点先切换成null
WindowState findFocusedWindowLocked(DisplayContent displayContent) {
final WindowList windows = displayContent.getWindowList();
for (int i = windows.size() - 1; i >= 0; i--) {
final WindowState win = windows.get(i);
if (!win.canReceiveKeys()) {
continue;
}
AppWindowToken wtoken = win.mAppToken;
if (mFocusedApp == token && token.windowsAreFocusable()) {
return null;
}
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)