android N进程启动流程(一)(捕获输入事件、准备创建activity、焦点切换)

android N进程启动流程(一)(捕获输入事件、准备创建activity、焦点切换)

1. 背景

本文主要针对event log中各处节点进行进程启动流程分析。

//此处使用的是adb指令input tap + location的方法(具体实现可以参考)
//Input.java (frameworks\base\cmds\input\src\com\android\commands\input)
//1、捕获输入事件
01-01 00:34:12.298349  8599  8599 I InputManager: PERF:injectInputEvent: MotionEvent { action=ACTION_UP...

//2、准备创建activity
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...

//3、焦点切换
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]

//4、上一个activity的暂停,如此处是launcher桌面
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]

//5、进程启动
01-01 00:34:12.398299  1135  1698 I am_proc_start: [0,8610,10123,test2.com.myapplication,activity,test2.com.myapplication/.MainActivity]

//6、绑定与创建application
01-01 00:34:12.429265  1135  1697 I am_proc_bound: [0,8610,test2.com.myapplication]

//7、创建activity实例并且调用onCreate
01-01 00:34:12.439813  1135  1697 I am_restart_activity: [0,201384592,38,test2.com.myapplication/.MainActivity]

//8、调用active的resume方法
01-01 00:34:12.651913  8610  8610 I am_on_resume_called: [0,test2.com.myapplication.MainActivity,LAUNCH_ACTIVITY]

//9、界面添加完成并可见
01-01 00:34:12.795221  1135  1204 I am_activity_launch_time: [0,201384592,test2.com.myapplication/.MainActivity,417,417]

//10、上一个activity的停止
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) {
	//...
	// Dispatch pointer up events.
	while (!upIdBits.isEmpty()) {
	    uint32_t upId = upIdBits.clearFirstMarkedBit();
	    {
	        //输出点击抬起的log,这段log原生是没有的
	        ALOGD("PERF:AMOTION_EVENT_ACTION_POINTER_UP:Up");

	        //分发action up的事件
	        dispatchMotion(when, policyFlags, mSource,
	                AMOTION_EVENT_ACTION_POINTER_UP, 0, 0, metaState, buttonState, 0,
	        //...
	    }
	}
	    
	// Dispatch pointer down events using the new pointer locations.
	while (!downIdBits.isEmpty()) {
	//...
	    if (dispatchedIdBits.count() == 1) {
	        // First pointer is going down.  Set down time.
	        mDownTime = when;
	        //输出点击按下的log,这段log原生是没有的
	        ALOGD("PERF:AMOTION_EVENT_ACTION_POINTER_DOWN:Down");
	    }
	    //分发action down的事件
	    dispatchMotion(when, policyFlags, mSource,
	        AMOTION_EVENT_ACTION_POINTER_DOWN, 0, 0, metaState, buttonState, 0,
	//...

一般响应时间从点击抬起开始算时间(一般认为用户手抬起了,就认为用户的操作已经表述清楚,需要尽快处理)

2.2 自动化模拟点击事件

此处方法介绍最常见的2种:

  1. 使用Instrumentation(代理控制框架)
  2. 使用adb指令:input tap

两种达到的效果是一致的,至于开始的地方可以添加自己想要的日志信息。

1、使用Instrumentationm模拟点击事件

    //创建Instrumentation实例
    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) {
        //...
        //所有模拟的点击分发事件都是通过这个地方运行,所以方法1、2都可以在这里添加日志
        Log.i(TAG, "PERF:injectInputEvent: " + event);
        return mIm.injectInputEvent(event, mode);
        //...
    }

2、使用adb指令:input tap

//mx代表的是横向x轴的位置,my代表的是纵向y轴的位置
adb shell input tap mx my

至于日志LOG输出的地方可以参考上面第一种方法中的injectInputEvent函数

3. 准备创建activity

Launcher响应打开事件(如点击一个应用图标)会启动应用,会调用Activity或者ContextImpl的startActivity启动应用。

准备创建activity流程图
图3.1 准备创建activity流程图

3.1 startActivity(ContextImpl.java)

ContextImpl.java的startActivity,应用一般都会有context实例,启动应用最简单的办法是startActivity+传递一个intent

    //startActivity启动应用这个api大家应该不陌生
    public void startActivity(Intent intent) {
        startActivity(intent, null);
    }

    public void startActivity(Intent intent, Bundle options) {
        //通过获取Instrumentation代理来启动应用,这里主要是为了规范化,都通过Instrumentation去中转
        mMainThread.getInstrumentation().execStartActivity(
                getOuterContext(), mMainThread.getApplicationThread(), null,
                (Activity) null, intent, -1, options);
    }

3.2 execStartActivity(Instrumentation.java)

Instrumentation.java其是一个工具类,可以用来启动应用

    //execStartActivity执行启动活动对象Activity的操作
    public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {
            //...
            //中转AMS,具体实现是在AMS的startActivity
            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) {
        //android是支持多用户的,故此处还需要传入用户组的ID
        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) {
        // ...
        // 最后走的地方是ActivityStarter应用启动器的startActivityMayWait,
        // 此处传递的WaitResult == null,代表启动完成之后无需返回启动时间的结果
        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,
        //...
        //通过PMS解析意图
        ResolveInfo rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId);
        //...
        //获取该意图的活动对象Activity的Info信息,里面有需要启动应用的信息如包名test2
        ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo);
        //这里才是真正启动应用的地方
        int res = startActivityLocked(caller, intent, ephemeralIntent, resolvedType,
        //...
        //outResult输出activity的启动结果,一般自动化测试的中使用
            if (outResult != null) {
                    //...
                    do {
                        try {
                            //只有等到mService.notify()或者mService.notifyAll()此处才会继续执行下去
                            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,
        //...
        //新建需要启动活动对象activity(如test2)的ActivityRecord
        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);
        //...
        //这里是真正起到进程的地方,倒数第三个参数doResume=true
        err = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor, 
                startFlags, true, options, inTask);

3.6 startActivityUnchecked

  1. 此处会启动新的ActivityStack,输出am_create_task
  2. 输出am_create_activity,准备创建activity
  3. 设置焦点、焦点切换
  4. 上一个activity的onpause
    private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask) {
        //...
        //初始化一些参数,如mUserLeaving==true就是在这里设置的
        setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
                voiceInteractor);

        //...
        if (mStartActivity.resultTo == null && mInTask == null && !mAddingToTask
                && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
            newTask = true;
            //这里会进来,启动新的ActivityStack  mTargetStack
            setTaskFromReuseOrCreateNewTask(taskToAffiliate);
            //...
        }

        if (newTask) {
            //属于新的task,会在event log中输出am_create_task
            EventLog.writeEvent(
                    EventLogTags.AM_CREATE_TASK, mStartActivity.userId, mStartActivity.task.taskId);
        }
        //event log中输出am_create_activity,开始启动应用
        ActivityStack.logStartActivity(
                EventLogTags.AM_CREATE_ACTIVITY, mStartActivity, mStartActivity.task);

        //...
        //stack中的startActivityLocked,此处设置mStartActivity.task(TaskRecord)的
        //topRunningActivityLocked为test2
        mTargetStack.startActivityLocked(mStartActivity, newTask, mKeepCurTransition, mOptions);

        //mDoResume == true,会进入此处
        if (mDoResume) {
            //mLaunchTaskBehind没有设置过就是false,也就是会进入setFocusedActivityLocked
            if (!mLaunchTaskBehind) {
                //设置焦点,此处消耗大概6ms左右
                mService.setFocusedActivityLocked(mStartActivity, "startedActivity");
            }
            //TaskRecord最顶点的activity,如test2
            final ActivityRecord topTaskActivity = mStartActivity.task.topRunningActivityLocked();
            //...
            //一般情况都是走的这里,进行上一个activity的onpause(如launcher)
            mSupervisor.resumeFocusedStackTopActivityLocked(mTargetStack, mStartActivity,
                    mOptions);
            //...
    }

4. 焦点切换

接着章节3.6的startActivityUnchecked中会调用setFocusedActivityLocked(ActivityManagerService.java),
此处会进行焦点切换。

焦点切换流程图
图4.1 焦点切换流程图

4.1 setFocusedActivityLocked(ActivityManagerService.java)

里面包括3个部分

  1. moveActivityStackToFront移动堆栈到顶端
  2. setFocusedApp在WMS中设置focus的app,属于窗体焦点变换
  3. event log中输出am_focused_activity,代表已经在AMS中设置了focus的对象
    boolean setFocusedActivityLocked(ActivityRecord r, String reason) {
        //...
        //设置AMS中foucs的活动对象
        mFocusedActivity = r;
        //...
        if (mStackSupervisor.moveActivityStackToFront(r, reason + " setFocusedActivity")) {
            //moveActivityStackToFront是true才会进来
            mWindowManager.setFocusedApp(r.appToken, true);
        }
        //...
        //此处是eventlog的am_focused_activity打印
        EventLogTags.writeAmFocusedActivity(
                mFocusedActivity == null ? -1 : mFocusedActivity.userId,
                mFocusedActivity == null ? "NULL" : mFocusedActivity.shortComponentName,
                reason);
        //...
        return true;
    }

4.2 moveActivityStackToFront(ActivityStackSupervisor.java)

移动堆栈,此处会将test2移动到堆栈顶端:

  1. mStackSupervisor.setFocusStackUnchecked,event log中输出am_focused_stack,代表堆栈的焦点已经发生变化
  2. insertTaskAtTop,将test2的stack移动到最顶端
  3. moveTaskToTop插入WMS窗体相关的task的顶端
    // moveActivityStackToFront(ActivityStackSupervisor.java)
    boolean moveActivityStackToFront(ActivityRecord r, String reason) {
        …
        stack.moveToFront(reason, task);
        return true;
    }

    // moveToFront(ActivityStack.java)
    void moveToFront(String reason, TaskRecord task) {
        //...
        //此处判断是不是主的显示设备(注意这里不是指桌面),一般都是true
        if (isOnHomeDisplay()) {
            //此处会设置mFocusedStack为当前需要启动的activity,如test2.com.myapplication, 
            //getFocusedStack时就会是test2
            mStackSupervisor.setFocusStackUnchecked(reason, this);
        }
        if (task != null) {
            //此处是修改activity的task,会将test2放在mTaskHistory中,
            //当调用topRunningActivityLocked(ActivityStack)判断顶端activity的时候就会返回test2
            insertTaskAtTop(task, null);
        }

        if (task != null) {
            //此处是修改WMS中的task,对应于wm_task_moved: [44,1,2],
            //44代表的是移动task,1代表移动到顶端,2代表移动到task的哪个位置
            //顶端的话最后一个参数是mTasks.size()(也就是上面的2)
            //stack是栈,后进先出,所以放的位置是List最后面
            mWindowManager.moveTaskToTop(task.taskId);
        }
    }

    // setFocusStackUnchecked(ActivityStackSupervisor.java)
    void setFocusStackUnchecked(String reason, ActivityStack focusCandidate) {
        //...
        //如果需要设置的焦点focusCandidate和之前focus的不一样,会进入此处
        if (focusCandidate != mFocusedStack) {
            //更新堆栈焦点状态
            mLastFocusedStack = mFocusedStack;
            mFocusedStack = focusCandidate;
            //此处是eventlog的am_focused_stack打印
            EventLogTags.writeAmFocusedStack(
                    mCurrentUser, mFocusedStack == null ? -1 : mFocusedStack.getStackId(),
                    mLastFocusedStack == null ? -1 : mLastFocusedStack.getStackId(), reason);
        }
        //...
    }

4.3 setFocusedApp(WindowManagerService.java)

setFocusedApp用户更新当前WMS focus的窗体

    //moveFocusNow上面章节4.1传递进来的就是true
    public void setFocusedApp(IBinder token, boolean moveFocusNow) {
        //...
        //newFocus是test2,mFocusedApp是launcher,故changed==true
        final boolean changed = mFocusedApp != newFocus;
        //...
        //moveFocusNow==true, changed==true,故这里是会进来的
        if (moveFocusNow && changed) {
            //更新窗体显示
            updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/);
        //...
    }

updateFocusedWindowLocked会先遍历查找最新的焦点,如果窗体焦点有变化,则进行更新。
至于遍历查找窗体焦点是在computeFocusedWindowLocked完成的
此处会输出Changing focus from Window*** to null

    boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
        //会先调用查找最新的焦点窗口,如果返回null代表当前没有窗口聚焦
        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) {
                //更新input foucus的焦点,这个是在界面focus的时候做的
                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;
            }
            //...
            //当前接收按键相应的窗体仍是launcher(test2进程都还未启动)
            AppWindowToken wtoken = win.mAppToken;
            //...
            //当便遍历到token与mFocusedApp相等(test2)的时候,而且该window是允许聚焦
            if (mFocusedApp == token && token.windowsAreFocusable()) {
                //那我们就会把之前的焦点更换,就是把launcher焦点切换成null
                return null;
            }
            //...
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值