美文网首页
ActivityManagerService-C 管理场景分析

ActivityManagerService-C 管理场景分析

作者: 开开向前冲 | 来源:发表于2017-12-15 16:38 被阅读0次

    版权说明:本文为 开开向前冲 记录文章,转载请注明出处;
    注:限于作者水平有限,文中有不对的地方还请指教

    在文章ActivityManagerService-A中对AMS 中核心的几个数据类都有说道,在ActivityManagerService-B中对ActivityRecord 的状态变化做了一个介绍,Activity的状态只是管理的结果,具体的过程需要需要详细分析;本文将从几个简单的场景结合代码来分析Activity的管理过程;

    场景1:Activity在AMS中的存在形式是ActivityRecord,应用程序的组件是Activity,ActivityRecord与Activity是如何关联的呢?——>static class Token extends IApplicationToken.Stub

    这里涉及到到应用程序的主线程-ActivityThread(ActivityThread其实并不是一个线程类),这个类已经有很多人讲解,这里推荐一篇关于ActivityThread和ApplicationThread的解析,ActivityThread有一个内部类 ApplicationThread;
    class ApplicationThread extends ApplicationThreadNative
    class ApplicationThreadNative extends Binder implements IApplicationThread
    AMS 和ActivityStackSupervisor中的 app.thread 就是这里ActivityThread的内部类ApplicationThread对象;

    Activity在ActivityThread中的存在形式是ActivityClientRecord;ActivityRecord有一个IApplicationToken.Stub类型的变量appToken,在构造ActivityRecord的时候被初始化;

    ------>frameworks\base\services\core\java\com\android\server\am\ActivityRecord.java
      static class Token extends IApplicationToken.Stub {
            final WeakReference<ActivityRecord> weakActivity;
            Token(ActivityRecord activity) {
                weakActivity = new WeakReference<ActivityRecord>(activity);
            }
            ......
    
    ------>frameworks\base\services\core\java\com\android\server\am\ActivityRecord.java
        ActivityRecord(ActivityManagerService _service, ProcessRecord _caller,
                int _launchedFromUid, String _launchedFromPackage, Intent _intent, String _resolvedType,
                ActivityInfo aInfo, Configuration _configuration,
                ActivityRecord _resultTo, String _resultWho, int _reqCode,
                boolean _componentSpecified, ActivityStackSupervisor supervisor,
                ActivityContainer container, Bundle options) {
              service = _service;
              appToken = new Token(this);
              ......
        }
    

    构造一个ActivityRecord时,会将自己(this)传给Token,变量ActivityRecord.appToken存的就是最终创建出来的Token。

    上述ActivityRecord.appToken的功能是什么呢?ActivityRecord是在Activity的启动过程中被初始化,继续跟进Activity的启动过程;

    ------>frameworks\base\services\core\java\com\android\server\am\ActivityStackSupervisor.java
    ------>frameworks\base\core\java\android\app\ActivityThread.java
    ActivityStackSupervisor.realStartActivityLocked(ActivityRecord, ...)
      ——> IApplicationThread.scheduleLaunchActivity(...,token, ...)
          ——>跨进程调用,调用ActivityThread.ApplicationThread.scheduleLaunchActivity(...,token,...)
          ——>ApplicationThread.scheduleLaunchActivity(...token, ...)
            ——>ActivityThread.handleLaunchActivity(LAUNCH_ACTIVITY)
              ——>ActivityThread.performLaunchActivity(ActivityClientRecord, ...)
                   ——>从ActivityActivityClientRecord取出token使用
                   ——> Activity.attch(...token, ...) //attach中将token 赋值给Activity的mToken变量
    

    通过上述函数调用,ActivityRecord的appToken就和应用进程的mToken建立关联;在发生Activity切换时,应用进程会将上一个Activity的Token(AMS.startActivity()的输入参数IBinder resultTo)传递给系统进程,系统进程会根据这个Token找到ActivityRecord,对其完成调度后,再通知应用进程:Activity状态发生了变化。

    场景2:启动新Activity时,需要将新ActivityRecord压入任务栈顶;

    这里先用自然语言描述一下可能的情况:1:新的ActivityRecord 的任务栈已经存在,处于后台,此时需要将这个后台任务栈切换为前台任务栈,然后将这个ActivityRecord置于前台任务栈栈顶; 2:新ActivityRecord所在的任务栈没有,需要新建任务栈,然后将ActivityRecord置于栈顶;

    代码逻辑:任务栈存在于后台的情况:

    第一步:需要先找到ActivityRecord所在的任务(TaskRecord);
    第二步:将TaskRecord所在的ActivityStack移动到前台;
    第三步:将TaskRecord移动到ActivityStack的栈顶;

    1:找到ActivityRecord所在的TaskRecord: findTaskLocked()
    ------>frameworks\base\services\core\java\com\android\server\am\ActivityStackSupervisor.java
    /**
     * Returns the top activity in any existing task matching the given
     * Intent.  Returns null if no such task is found.
     */
    ActivityRecord findTaskLocked(ActivityRecord target) {
        Intent intent = target.intent;
        ActivityInfo info = target.info;
        ComponentName cls = intent.getComponent();
        if (info.targetActivity != null) {
            cls = new ComponentName(info.packageName, info.targetActivity);
        }
        ...
        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
            final TaskRecord task = mTaskHistory.get(taskNdx);
            ...
            final ActivityRecord r = task.getTopActivity();
                if (r == null || r.finishing || r.userId != userId ||
                        r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {//这些条件的直接过滤
                    continue;
                }
            ...
            final Intent taskIntent = task.intent;
            final Intent affinityIntent = task.affinityIntent;
            ...
            if (!isDocument && !taskIsDocument && task.rootAffinity != null) {
                if (task.rootAffinity.equals(target.taskAffinity)) { //Affinity和Task的rootAffinity相同,
                //则就是这个task 了;
                    return r;
                }
            } else if (taskIntent != null && taskIntent.getComponent() != null &&
                taskIntent.getComponent().compareTo(cls) == 0 &&
                Objects.equals(documentData, taskDocumentData)) {//Task的Intent 和target的包名相同
                return r;
            } else if if (affinityIntent != null && affinityIntent.getComponent() != null &&
                affinityIntent.getComponent().compareTo(cls) == 0 &&
                Objects.equals(documentData, taskDocumentData)) { //Affinity Intent的包名相同
                return r
            }
            ...
        }
        return null;
    }
    

    改函数主要是对ActivityStack中的所有Task进行遍历,找到ActivityRecord的所在的Task,如果找到Task,则返回Task最上面的ActivityRecord;找到Task的条件如下:

    • ActivityRecord target 的affinity和正在遍历的Task的rootAffinity相同;
    • ActivityRecord target的包名和Task的intent 的包名相同;
    • ActivityRecord target的包名和Task的affinityIntent的包名相同;
    2:将TaskRecord所在的ActivityStack调整到前台
    • 2.1:findTaskLocked()方法找到Activity所在的Task的顶部ActivityRecord,根据这个ActivityRecord找到所在的TaskRecord和ActivityStack;
    ------> frameworks\base\services\core\java\com\android\server\am\ActivityStackSupervisor.java
        final int startActivityUncheckedLocked(ActivityRecord r, ActivityRecord sourceRecord,
                IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, int startFlags,
                boolean doResume, Bundle options, TaskRecord inTask) {
                    ......
                    ActivityRecord intentActivity = !launchSingleInstance ?
                            findTaskLocked(r) : findActivityLocked(intent, r.info);
                    if (intentActivity != null) {
                        ......
                        if (r.task == null) {
                            r.task = intentActivity.task;
                        }
                        targetStack = intentActivity.task.stack;//找到ActivityRecord r所在的ActivityStack
                        targetStack.mLastPausedActivity = null;
                        targetStack.moveToFront("intentActivityFound");
                      ......
    
    • 2.2:调整ActivityStack,主要根据ActivityStack的类型来进行ActivityStack栈(mStacks)调整;moveToFront();
    ------>frameworks\base\services\core\java\com\android\server\am\ActivityStack.java
    final void moveToFront(String reason) {
            if (isAttached()) {//是否绑定到显示设备
                if (isOnHomeDisplay()) {//是否显示在默认显示设备
                    mStackSupervisor.moveHomeStack(isHomeStack(), reason);//ActivityStack栈调整
                }
                mStacks.remove(this);//删除mStacks栈中的该ActivityStack
                mStacks.add(this);//将该ActivityStack添加到mStacks中
                final TaskRecord task = topTask();
                if (task != null) {
                    mWindowManager.moveTaskToTop(task.taskId);
                }
            }
        }
    
    final boolean isHomeStack() {//判断当前ActivityStack 是不是HomeStack
            return mStackId == HOME_STACK_ID;
        }
    
    void moveHomeStack(boolean toFront, String reason) {//栈调整
        // 获取当前的Top Stack
        ArrayList<ActivityStack> stacks = mHomeStack.mStacks;//mStacks代表的是绑定到显示设备的所有Stack;
                              //所以这里也可以用mFocusedStack.mStacks
        final int topNdx = stacks.size() - 1;
        if (topNdx <= 0) {
            return;
        }
        ActivityStack topStack = stacks.get(topNdx);
        // HomeStack是否需要调整
        final boolean homeInFront = topStack == mHomeStack;//判断stacks 栈顶的ActivityStack是不是HomeStack,
        // 即判断当前HomeStack是不是在前台;
        if (homeInFront != toFront) {
            mLastFocusedStack = topStack;
            stacks.remove(mHomeStack);
            stacks.add(toFront ? topNdx : 0, mHomeStack);//这里根据toFront来调整HomeStack位置
            mFocusedStack = stacks.get(topNdx);//调整mFocusedStack
        }
        ...
        if (mService.mBooting || !mService.mBooted) {  // 开机过程处理
            final ActivityRecord r = topRunningActivityLocked();
            if (r != null && r.idle) {
                checkFinishBootingLocked();
            }
        }
    }
    

    moveHomeStack()方法根据mStacks栈顶的ActivityStack是否是HomeStack来对HomeStack和mFocusedStack 进行调整;homeInFront 表示mStacks栈顶是不是HomeStack(即HomeStack 是否在前台);toFront 是isHomeStack()的返回值,即当前ActivityStack是否是HomeStack,也就是需要将当前ActivityStack调整到前台还是后台;

    这里存在如下4种情况:

    • homeInFront = true,即topStack == mHomeStack, 表示当前mStacks最顶的栈是HomeStack,即表示HomeStack在前台;toFront = false 即isHomeStack()返回false,即表示ActivityRecord所在的栈不是HomeStack,所以需要将HomeStack调整到mStacks的底部,即将HomeStack调整到mStacks的0号位置;
    • homeInFront = true, toFront = true: 表示HomeStack在前台,要显示的ActivityStack就是HomeStack,所以不需要对mStacks进行调整;
    • homeInFront = false, toFront = true: 表示HomeStack在后台,要显示的ActivityStack就是HomeStack,需要将HomeStack调整到mStacks的最顶端;
    • homeInFront = false, toFront = false: 表示HomeStack在后台,要显示的ActivityStack 也 是HomeStack,不需要对mStacks进行调整。

    moveToFront中moveHomeStack()调整完HomeStack位置后,调整mStacks栈中当前ActivityStack位置,即先删除当前ActivityStack,然后再向mStacks中添加当前ActivityStack(这样做时为了保证将当前ActivityStack置于栈顶);

    3:将当前需要显示的ActivityRecord的TaskRecord 移动到ActivityStack 的栈顶;moveTaskToFrontLocked()

    ActivityStack有前台和后台之分,前台ActivityStack代表当前显示的ActivityRecord在的TaskRecord所在的ActivityStack(不是前台ActivityStack中所有的TaskRecord 和ActivityRecord都处于可见,只有ActivityStack 栈顶的TaskRecord 栈顶的ActivityRecord处于可见状态);

    第二步已经将ActivityRecord所在的Task的Stack移动到前台,现在需要将需要显示的ActivityRecord 所在的TaskRecord 移动到ActivityStack的栈顶,以及将ActivityRecord移动到TaskRecord的栈顶;

    ------> frameworks\base\services\core\java\com\android\server\am\ActivityStack.java
    final void moveTaskToFrontLocked(TaskRecord tr, ActivityRecord source, Bundle options,
                String reason) {
            final int numTasks = mTaskHistory.size();
            final int index = mTaskHistory.indexOf(tr);
            if (numTasks == 0 || index < 0)  { //numTasks 代表当前ActivityStack中的所有TaskRecord数量,
            //index 表示当前需要调整的TaskRecord在ActivityStack mTaskHistory中的位置
                // nothing to do!
                if (source != null &&
                        (source.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
                    ActivityOptions.abort(options);
                } else {
                    updateTransitLocked(AppTransition.TRANSIT_TASK_TO_FRONT, options);
                }
                return;
            }
    
            // Shift all activities with this task up to the top
            // of the stack, keeping them in the same internal order.
            insertTaskAtTop(tr);//将Task置于ActivityStack栈顶
            moveToFront(reason);//前面说过moveToFront()方法,前面的情况只调用了moveToFront(),
            //不会调用moveTaskToFrontLocked(); 
            //一旦要将任务调整到ActivityStack栈顶,意味着ActivityStack也一定要调整到前台;
    
            if (source != null &&
                    (source.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
                mWindowManager.prepareAppTransition(AppTransition.TRANSIT_NONE, false);
                ActivityRecord r = topRunningActivityLocked(null);
                if (r != null) {
                    mNoAnimActivities.add(r);
                }
                ActivityOptions.abort(options);
            } else {
                updateTransitLocked(AppTransition.TRANSIT_TASK_TO_FRONT, options);
            }
    
            mStackSupervisor.resumeTopActivitiesLocked();//当Activity各种栈调整好后,就可以将当前Activity的状态置为RESUMED
            ......
        }
    
    private void insertTaskAtTop(TaskRecord task) {
            // If this is being moved to the top by another activity or being launched from the home
            // activity, set mOnTopOfHome accordingly.
            if (isOnHomeDisplay()) {
                ActivityStack lastStack = mStackSupervisor.getLastStack();
                final boolean fromHome = lastStack.isHomeStack();
                if (!isHomeStack() && (fromHome || topTask() != task)) {
                    task.setTaskToReturnTo(fromHome
                            ? lastStack.topTask() == null
                                    ? HOME_ACTIVITY_TYPE
                                    : lastStack.topTask().taskType
                            : APPLICATION_ACTIVITY_TYPE);//设置ReturnTo Task类型
                }
            } else {
                task.setTaskToReturnTo(APPLICATION_ACTIVITY_TYPE);
            }
            mTaskHistory.remove(task);//先从mTaskHistory中移除task
            // Now put task at top.
            int taskNdx = mTaskHistory.size();
            if (!isCurrentProfileLocked(task.userId)) {
                // Put non-current user tasks below current user tasks.
                while (--taskNdx >= 0) {
                    if (!isCurrentProfileLocked(mTaskHistory.get(taskNdx).userId)) {
                        break;
                    }
                }
                ++taskNdx;
            }
            mTaskHistory.add(taskNdx, task);//将task添加到mTaskHistory,确保添加到了顶部
            updateTaskMovement(task, true);
        }
    

    这里将ActivityRecord所在的TaskRecord 移动到ActivityStack的栈顶,还需要将需要显示的ActivityRecord移动到TaskRecord的栈顶,这个过程通过addActivityToTop()来实现;addActivityToTop()在ActivityStack.java的startActivityLocked()方法中被调用,startActivityLocked()在ActivityStackSupervisor.java的startActivityUncheckedLocked()方法中被调用;

    ------> frameworks\base\services\core\java\com\android\server\am\TaskRecord.java
        void addActivityToTop(ActivityRecord r) {
            addActivityAtIndex(mActivities.size(), r);
        }
    
        void addActivityAtIndex(int index, ActivityRecord r) {
            // Remove r first, and if it wasn't already in the list and it's fullscreen, count it.
            if (!mActivities.remove(r) && r.fullscreen) {//删除mActivities中已经存在的r
                // Was not previously in list.
                numFullscreen++;
            }
            // Only set this based on the first activity
            if (mActivities.isEmpty()) { //判断当前启动的Activity是否为该任务Task的第一个Activity
                taskType = r.mActivityType;
                isPersistable = r.isPersistable();
                mCallingUid = r.launchedFromUid;
                mCallingPackage = r.launchedFromPackage;
                // Clamp to [1, max].
                maxRecents = Math.min(Math.max(r.info.maxRecents, 1),
                        ActivityManager.getMaxAppRecentsLimitStatic());
            } else {
                // Otherwise make all added activities match this one.
                r.mActivityType = taskType;
            }
            Log.d(TAG+"mActivities.add",Log.getStackTraceString(new Throwable()));//Keiven-chen
            mActivities.add(index, r);//指定将当前Activity添加到mActivities中的位置
            updateEffectiveIntent();//如函数名,更新任务栈相关Intent
            if (r.isPersistable()) {
                mService.notifyTaskPersisterLocked(this, false);
            }
        }
    

    这里需要明确一点问题,mActivities就是我说的TaskRecord栈, mTaskHistory也就是ActivityStack栈;

    场景3: 任务栈会经常调整,任务栈刚开始从底到顶是 A - B - C, 经过调整后任务栈变化为 C - B - A,这个过程会对整个任务栈带来哪些改变呢?

    该问题看似只需要调整mActivities的顺序,但是会这么简单吗?ActivityManagerService-A文中有说过TaskRecord类,随着mActivities栈的改变,TaskRecord中的属性是否也应该改变呢?比如Affinity,Intent;这些值是如何改变的呢?这里涉及updateEffectiveIntent()和setFrontOfTask();

    每次调整完mActivities后,需要调用updateEffectiveIntent方法来修改该TaskRecord相关的Intent;

    void updateEffectiveIntent() {
        final int effectiveRootIndex = findEffectiveRootIndex();//找到根Activity索引
        final ActivityRecord r = mActivities.get(effectiveRootIndex);
        setIntent(r);//设置Intent相关属性,顺着代码往下看,
    }
        /** Sets the original intent, and the calling uid and package. */
        void setIntent(ActivityRecord r) {
            setIntent(r.intent, r.info);
            mCallingUid = r.launchedFromUid;
            mCallingPackage = r.launchedFromPackage;
        }
    
        /** Sets the original intent, _without_ updating the calling uid or package. */
        private void setIntent(Intent _intent, ActivityInfo info) { //该重载方法更新affinity, 
        //origActivity ,realActivity等属性;
            if (intent == null) {
                mNeverRelinquishIdentity =
                        (info.flags & ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY) == 0;
            } else if (mNeverRelinquishIdentity) {
                return;
            }
    
            affinity = info.taskAffinity;
            if (intent == null) {
                // If this task already has an intent associated with it, don't set the root
                // affinity -- we don't want it changing after initially set, but the initially
                // set value may be null.
                rootAffinity = affinity;
            }
            effectiveUid = info.applicationInfo.uid;
            stringName = null;
    
            if (info.targetActivity == null) {
                if (_intent != null) {
                    // If this Intent has a selector, we want to clear it for the
                    // recent task since it is not relevant if the user later wants
                    // to re-launch the app.
                    if (_intent.getSelector() != null || _intent.getSourceBounds() != null) {
                        _intent = new Intent(_intent);
                        _intent.setSelector(null);
                        _intent.setSourceBounds(null);
                    }
                }
                if (ActivityManagerService.DEBUG_TASKS) Slog.v(ActivityManagerService.TAG,
                        "Setting Intent of " + this + " to " + _intent);
                intent = _intent;
                realActivity = _intent != null ? _intent.getComponent() : null;
                origActivity = null;
            } else {
                ComponentName targetComponent = new ComponentName(
                        info.packageName, info.targetActivity);
                if (_intent != null) {
                    Intent targetIntent = new Intent(_intent);
                    targetIntent.setComponent(targetComponent);
                    targetIntent.setSelector(null);
                    targetIntent.setSourceBounds(null);
                    if (ActivityManagerService.DEBUG_TASKS) Slog.v(ActivityManagerService.TAG,
                            "Setting Intent of " + this + " to target " + targetIntent);
                    intent = targetIntent;
                    realActivity = targetComponent;
                    origActivity = _intent.getComponent();
                } else {
                    intent = null;
                    realActivity = targetComponent;
                    origActivity = new ComponentName(info.packageName, info.name);
                }
            }
    
            final int intentFlags = intent == null ? 0 : intent.getFlags();
            if ((intentFlags & Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
                // Once we are set to an Intent with this flag, we count this
                // task as having a true root activity.
                rootWasReset = true;
            }
    
            userId = UserHandle.getUserId(info.applicationInfo.uid);
            if ((info.flags & ActivityInfo.FLAG_AUTO_REMOVE_FROM_RECENTS) != 0) {
                // If the activity itself has requested auto-remove, then just always do it.
                autoRemoveRecents = true;
            } else if ((intentFlags & (Intent.FLAG_ACTIVITY_NEW_DOCUMENT
                    | Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS)) == Intent.FLAG_ACTIVITY_NEW_DOCUMENT) {
                // If the caller has not asked for the document to be retained, then we may
                // want to turn on auto-remove, depending on whether the target has set its
                // own document launch mode.
                if (info.documentLaunchMode != ActivityInfo.DOCUMENT_LAUNCH_NONE) {
                    autoRemoveRecents = false;
                } else {
                    autoRemoveRecents = true;
                }
            } else {
                autoRemoveRecents = false;
            }
        }
    

    任务栈A - B - C的栈底是 A,调整后, C - B - A的栈底是C,然而,TaskRecord并没有标注当前栈底的属性, 这个栈底是根据任务栈中每个ActivityRecord的frontOfTask属性来标识:这个属性通过setFrontOfTask()方法来修改;

    /** Call after activity movement or finish to make sure that frontOfTask is set correctly */
        final void setFrontOfTask() {
            boolean foundFront = false;
            final int numActivities = mActivities.size();
            for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) {//重栈底开始
                final ActivityRecord r = mActivities.get(activityNdx);
                if (foundFront || r.finishing) {
                    r.frontOfTask = false; //非栈底Activity 该属性都置为false
                } else {
                    r.frontOfTask = true;//栈底Activity 该属性置为true
                    // Set frontOfTask false for every following activity.
                    foundFront = true;
                }
            }
            if (!foundFront && numActivities > 0) {
                // All activities of this task are finishing. As we ought to have a frontOfTask
                // activity, make the bottom activity front.
                mActivities.get(0).frontOfTask = true; //这种情况将mActivities第一个Activity的frontOfTask置为true;
            }
        }
    

    该函数对任务栈从底到顶进行遍历,找到第一个未结束(finishing = false)的ActivityRecord, 将其frontOfTask属性设置成true;其他所有ActivtyRecord的frontOfTask属性设置为false。这样就能标记该ActivityRecord为所在任务栈的栈底;

    参考文章:Android四大组件之Activity--管理方式

    相关文章

      网友评论

          本文标题:ActivityManagerService-C 管理场景分析

          本文链接:https://www.haomeiwen.com/subject/ehawvxtx.html