Android启动Activity源码分析(二)

AMS所在的进程

AMS所在的进程是系统进程,进程执行入口是com.android.server.SystemServer#main,它在系统开机时启动。AMS是SystemServer中的一个线程,且是Binder线程,可通过Binder进行进程间通讯。SystemServer调用com.android.server.am.ActivityManagerService#main启动AMS线程。

AMS接收startActivity消息

根据Binder原理,将会跨进程调用到android.app.ActivityManagerNative#onTransact:

    public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
            throws RemoteException {
        switch (code) {
        case START_ACTIVITY_TRANSACTION:
        {
            //读取Parcel数据 start
            data.enforceInterface(IActivityManager.descriptor);
            IBinder b = data.readStrongBinder();
            IApplicationThread app = ApplicationThreadNative.asInterface(b);
            Intent intent = Intent.CREATOR.createFromParcel(data);
            String resolvedType = data.readString();
            IBinder resultTo = data.readStrongBinder();
            String resultWho = data.readString();
            int requestCode = data.readInt();
            int startFlags = data.readInt();
            String profileFile = data.readString();
            ParcelFileDescriptor profileFd = data.readInt() != 0
                    ? data.readFileDescriptor() : null;
            Bundle options = data.readInt() != 0
                    ? Bundle.CREATOR.createFromParcel(data) : null;
            //读取Parcel数据 end
            int result = startActivity(app, intent, resolvedType,
                    resultTo, resultWho, requestCode, startFlags,
                    profileFile, profileFd, options);
            //写入Parcel无异常到reply
            reply.writeNoException();
            //写入AMS返回结果
            reply.writeInt(result);
            return true;
        }
        ...

com.android.server.am.ActivityManagerService#startActivity:

    public final int startActivity(IApplicationThread caller,
            Intent intent, String resolvedType, IBinder resultTo,
            String resultWho, int requestCode, int startFlags,
            String profileFile, ParcelFileDescriptor profileFd, Bundle options) {
        enforceNotIsolatedCaller("startActivity");
        //用户的唯一标识
        int userId = 0;
            ...
            //小于Process.FIRST_APPLICATION_UID表示请求方是系统进程
            if (Binder.getCallingUid() < Process.FIRST_APPLICATION_UID) {
                userId = 0;
            } else {
                //获取Binder跨进程请求方的userId
                userId = Binder.getOrigCallingUser();
            }
            ...
        //mMainStack是com.android.server.am.ActivityStack#ActivityStack的实例,在AMS初始化的时候创建
        return mMainStack.startActivityMayWait(caller, -1, intent, resolvedType,
                resultTo, resultWho, requestCode, startFlags, profileFile, profileFd,
                null, null, options, userId);
    }

Android系统支持多用户,当前场景未进行用户切换时,主用户的userId始终是0。

通过PMS查询Activity信息

com.android.server.am.ActivityStack#startActivityMayWait:

    final int startActivityMayWait(IApplicationThread caller, int callingUid,
            Intent intent, String resolvedType, IBinder resultTo,
            String resultWho, int requestCode, int startFlags, String profileFile,
            ParcelFileDescriptor profileFd, WaitResult outResult, Configuration config,
            Bundle options, int userId) {
        ...
        boolean componentSpecified = intent.getComponent() != null;

        // Don't modify the client's object!
        //把Intent拷贝了一份
        intent = new Intent(intent);

        // Collect information about the target of the Intent.
        //通过PMS(PackageManagerService)获取要启动的Activity信息
        ActivityInfo aInfo = resolveActivity(intent, resolvedType, startFlags,
                profileFile, profileFd, userId);
        ...
        //if else判断了多个场景,当前场景走最后一个else
        synchronized (mService) {
            int callingPid;
            if (callingUid >= 0) {
                callingPid = -1;
            } else if (caller == null) {
                callingPid = Binder.getCallingPid();
                callingUid = Binder.getCallingUid();
            } else {
                callingPid = callingUid = -1;
            }
            ...
            //暂存Binder调用方的信息
            final long origId = Binder.clearCallingIdentity();
            ...
            int res = startActivityLocked(caller, intent, resolvedType,
                    aInfo, resultTo, resultWho, requestCode, callingPid, callingUid,
                    startFlags, options, componentSpecified, null);
            ...
            //恢复Binder调用方的信息
            Binder.restoreCallingIdentity(origId);
            ...
            return res;

已省略场景无关代码。Binder.clearCallingIdentity()会清除存储的调用方的uid和pid,并返回给origId,其中uid和pid和分别占int类型的高32位和低32位。Binder.restoreCallingIdentity(origId)会恢复暂存的origId。
ActivityInfo aInfo存储了查找得到的Activity信息。

构造Activity在AMS中的记录:ActivityRecord

com.android.server.am.ActivityStack#startActivityLocked(IApplicationThread, android.content.Intent, java.lang.String, android.content.pm.ActivityInfo, android.os.IBinder, java.lang.String, int, int, int, int, android.os.Bundle, boolean, com.android.server.am.ActivityRecord[]):

    final int startActivityLocked(IApplicationThread caller,
            Intent intent, String resolvedType, ActivityInfo aInfo, IBinder resultTo,
            String resultWho, int requestCode,
            int callingPid, int callingUid, int startFlags, Bundle options,
            boolean componentSpecified, ActivityRecord[] outActivity) {

        int err = ActivityManager.START_SUCCESS;

        ProcessRecord callerApp = null;
        if (caller != null) {
            //根据代表调用方进程的IApplicationThread caller,获取存储在AMS中的进程信息记录,ProcessRecord记录了进程相关信息。
            callerApp = mService.getRecordForAppLocked(caller);
            if (callerApp != null) {
                //拿到调用方的pid和uid
                callingPid = callerApp.pid;
                callingUid = callerApp.info.uid;
            }
            ...
            
        }
        ...
        //表示当前Activity
        ActivityRecord sourceRecord = null;
        //表示startActivityForResult要返回的到的Activity
        ActivityRecord resultRecord = null;
        if (resultTo != null) {
            int index = indexOfTokenLocked(resultTo);
            ...
            if (index >= 0) {
                sourceRecord = mHistory.get(index);
                //若是requestCode>=0表示startActivity要返回结果,发起startActiviy的地方就是要获返回到的地方
                if (requestCode >= 0 && !sourceRecord.finishing) {
                    resultRecord = sourceRecord;//
                }
            }
        }

        int launchFlags = intent.getFlags();
        //FLAG_ACTIVITY_FORWARD_RESULT允许对startActivity的result进行转发,这里是相关逻辑,本场景走不到。
        if ((launchFlags&Intent.FLAG_ACTIVITY_FORWARD_RESULT) != 0
                && sourceRecord != null) {
                ...
        }
        //一些失败场景的处理会直接return err,略。
        ...
        
        //START_ANY_ACTIVITY:可以启动任何Activity的权限。第三方应用设置了uses-permission的START_ANY_ACTIVITY无效,它要求必须和系统app签名一致,因此权鉴失败。
        final int startAnyPerm = mService.checkPermission(
                START_ANY_ACTIVITY, callingPid, callingUid);final int componentPerm = mService.checkComponentPermission(aInfo.permission, callingPid,
                callingUid, aInfo.applicationInfo.uid, aInfo.exported);
        //要启动的Activity设置了android:permission,也要检查是否能启动TargetActivity,当前场景设置Activity权限,因此权鉴通过。
        final int componentPerm = mService.checkComponentPermission(aInfo.permission, callingPid,
                callingUid, aInfo.applicationInfo.uid, aInfo.exported);
        //权鉴失败的处理,不走这
        if (startAnyPerm != PERMISSION_GRANTED && componentPerm != PERMISSION_GRANTED) {
        }
        
        //mMainStack是true,ActivityStack创建时设置的
        if (mMainStack) {
            //mController可以通过android.app.ActivityManagerProxy#setActivityController设置,可以监听、拦截App的启动,不走这。
            if (mService.mController != null) {
            ...
            }
        }
        //创建ActivityRecord,它是Activity在AMS中的记录
        ActivityRecord r = new ActivityRecord(mService, this, callerApp, callingUid,
                intent, resolvedType, aInfo, mService.mConfiguration,
                resultRecord, resultWho, requestCode, componentSpecified);
        //outActivity传入的null,不走这
        if (outActivity != null) {
            outActivity[0] = r;
        }
        if (mMainStack) {
            //处于后台的Activity发起startActivity请求,这样的操作google有限制,代码就在这里,略。
            if (mResumedActivity == null
                    || mResumedActivity.info.applicationInfo.uid != callingUid) {
            ...
        }
        
        err = startActivityUncheckedLocked(r, sourceRecord,
                startFlags, true, options);
        ...
        return err;

这里做了这几件事:

  • uid和pid的获取
    -需要result时的处理
  • startActivity权鉴
  • 创建ActivityRecord

com.android.server.am.ActivityStack#startActivityUncheckedLocked:

    final int startActivityUncheckedLocked(ActivityRecord r,
            ActivityRecord sourceRecord, int startFlags, boolean doResume,
            Bundle options) {
        final Intent intent = r.intent;
        final int callingUid = r.launchedFromUid;
        final int userId = r.userId;

        int launchFlags = intent.getFlags();
        //一些flag和其它的无关逻辑,略
        ...
        //sourceRecord是null表示从Application Context而不是从Activity启动,singleInstance、singleInstance、singleTask这些场景都强制加上FLAG_ACTIVITY_NEW_TASK。这里不会走
        if (sourceRecord == null) {
            ...
        } else if (sourceRecord.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) 
            ...
        } else if (r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE
                || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK) {
            ...
        }
        //一大堆无关逻辑,包括Task处理、startActivity返回result的异常处理等等
        ...
        boolean newTask = false;
        boolean keepCurTransition = false;
        if (r.resultTo == null && !addingToTask
                && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
            //需要创建Task的情况,不走这里
            ...
        } else if (sourceRecord != null) {
            if (!addingToTask &&
                    (launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) {
                //FLAG_ACTIVITY_CLEAR_TOP相关逻辑,不走这里
            } else if (!addingToTask &&
                    (launchFlags&Intent.FLAG_ACTIVITY_REORDER_TO_FRONT) != 0) {
                //FLAG_ACTIVITY_REORDER_TO_FRONT相关处理,不走这里
            }
            //把MainActivity所在的Task共享给了TargetActivity,后文分析
            r.setTask(sourceRecord.task, sourceRecord.thumbHolder, false);
        } else {
            //从Application Context启动,直接把最近的Activity所在栈设置给要启动Activity,不走这里
        }
        ...
        startActivityLocked(r, newTask, doResume, keepCurTransition, options);
        return ActivityManager.START_SUCCESS;
    }

Task和mHistory的处理

先分析r.setTask再分析startActivityLocked
com.android.server.am.ActivityRecord#setTask:

    void setTask(TaskRecord newTask, ThumbnailHolder newThumbHolder, boolean isRoot) {
        //如果该Activity已经在被AMS启动过了,换新的Task,要做一些处理,不走这
        if (inHistory && !finishing) {
            if (task != null) {
                task.numActivities--;
            }
            if (newTask != null) {
                newTask.numActivities++;
            }
        }
        if (newThumbHolder == null) {
            newThumbHolder = newTask;
        }
        //赋值新Task
        task = newTask;
        // 无关逻辑
        if (!isRoot && (intent.getFlags()&Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) {
            if (thumbHolder == null) {
                thumbHolder = new ThumbnailHolder();
            }
        } else {
            thumbHolder = newThumbHolder;
        }
    }

com.android.server.am.ActivityStack#startActivityLocked(com.android.server.am.ActivityRecord, boolean, boolean, boolean, android.os.Bundle):

    //传入值r:刚创建的ActivityRecord,newTask:false,doResume:true,keepCurTransition:false,options:null
    private final void startActivityLocked(ActivityRecord r, boolean newTask,
            boolean doResume, boolean keepCurTransition, Bundle options) {
        //mHistory是AMS中启动的Activity的个数
        final int NH = mHistory.size();

        int addPos = -1;
        
        //复用的Task,找个合适的位置插ActivityRecord进去
        if (!newTask) {
            boolean startIt = true;
            for (int i = NH-1; i >= 0; i--) {
                ActivityRecord p = mHistory.get(i);
                if (p.finishing) {
                    continue;
                }
                if (p.task == r.task) {
                    //TargetActivity要插入mHistory的位置在MainActivity的前一个
                    addPos = i+1;
                    if (!startIt) {
                        //所在的Task不在前台显示着,直接启动要启动的Activity,不走这里,略
                        ...
                    }
                    //直接break
                    break;
                }
                if (p.fullscreen) {
                    startIt = false;
                }
            }
        }
        //没有可以插入的位置,直接顶上,不走这
        if (addPos < 0) {
            addPos = NH;
        }
        if (addPos < NH) {
            ...
        }
        ...
        mHistory.add(addPos, r);
        //见后文分析
        r.putInHistory();
        //frontOfTask表示是否是Task的根Activity,这里是false
        r.frontOfTask = newTask;
        if (NH > 0) {
            // We want to show the starting preview window if we are
            // switching to a new task, or the next activity's process is
            // not currently running.
            //走这里。一些动画相关的操作,可见上面官方注释,还会通知WMS(WindowsManagerService做一些事情),场景无关,不再分析。
            ...
        else {
            //不需要动画但是也通知WMS做了一些事情
            ...
        }
        ...
        if (doResume) {
            resumeTopActivityLocked(null);
        }

com.android.server.am.ActivityRecord#putInHistory:

    void putInHistory() {
        //新建的ActivityRecord,不在mHistory中,inHistory是false
        if (!inHistory) {
            inHistory = true;
            //当前场景满足条件,把Task中记录的Activity个数加1
            if (task != null && !finishing) {
                task.numActivities++;
            }
        }
    }

要想resume新的Activity,先pause掉前一个Activity

com.android.server.am.ActivityStack#resumeTopActivityLocked(com.android.server.am.ActivityRecord):

    final boolean resumeTopActivityLocked(ActivityRecord prev) {
        return resumeTopActivityLocked(prev, null);
    }
    final boolean resumeTopActivityLocked(ActivityRecord prev, Bundle options) {
        // 找到mHistory中最近的一个没有正在finishing的Activity,因为TargetActivity已经被插入进去了,一次next就是TargetActivity
        ActivityRecord next = topRunningActivityLocked(null);
        final boolean userLeaving = mUserLeaving;
        mUserLeaving = false;
        if (next == null) {
            //已经没有可以显示的Activity了,直接显示Launcher,也就是桌面进程,不走这
            if (mMainStack) {
                ActivityOptions.abort(options);
                return mService.startHomeActivityLocked(0);
            }
        }
        next.delayedResume = false;
        //mResumedActivity表示当前正在resume的Activity,是MainActivity。要启动的Activity已经resume了,直接return,不走这里
        if (mResumedActivity == next && next.state == ActivityState.RESUMED) {
            ...
            return false;
        }
        if ((mService.mSleeping || mService.mShuttingDown)
                && mLastPausedActivity == next
                && (next.state == ActivityState.PAUSED
                    || next.state == ActivityState.STOPPED
                    || next.state == ActivityState.STOPPING)) {
            ...
            //休眠状态下的情况,mLastPausedActivity保存着Task顶的Activity,如果要启动的就是休眠后的Task顶,直接return,不走这
            return false;
        }
        //都是要resume的Activity了,从这些list中删除了,状态重置了
        mStoppingActivities.remove(next);
        mGoingToSleepActivities.remove(next);
        next.sleeping = false;
        mWaitingVisibleActivities.remove(next);
        ...
        //如果有正在pause的Activity,啥也不干,等它暂停完再说,不走这
        if (mPausingActivity != null) {
            if (DEBUG_SWITCH) Slog.v(TAG, "Skip resume: pausing=" + mPausingActivity);
            return false;
        }
        ...
        //有正在resume的Activity,先把它pause掉,mResumedActivity就是MainActivity
        if (mResumedActivity != null) {
            if (DEBUG_SWITCH) Slog.v(TAG, "Skip resume: need to start pausing");
            startPausingLocked(userLeaving, false);
            return true;
        }
        ...

com.android.server.am.ActivityStack#startPausingLocked:

//userLeaving和uiSleeping传入的都是false
    private final void startPausingLocked(boolean userLeaving, boolean uiSleeping) {
        //有正在pause的,属于异常情况,不走这
        if (mPausingActivity != null) {
            RuntimeException e = new RuntimeException();
            Slog.e(TAG, "Trying to pause when pause is already pending for "
                  + mPausingActivity, e);
        }
        ActivityRecord prev = mResumedActivity;
        //没有可以用来pause的,属于异常情况,不走这里
        if (prev == null) {
            RuntimeException e = new RuntimeException();
            Slog.e(TAG, "Trying to pause when nothing is resumed", e);
            resumeTopActivityLocked(null);
            return;
        }
        ...
        //这一堆,把正在resume的Activity变成正在pause的
        mResumedActivity = null;
        mPausingActivity = prev;
        mLastPausedActivity = prev;
        prev.state = ActivityState.PAUSING;
        ...
        if (prev.app != null && prev.app.thread != null) {
            if (DEBUG_PAUSE) Slog.v(TAG, "Enqueueing pending pause: " + prev);
            try {
                ...
                //通知resume的Activity,也即是MainActivity,pause掉
                prev.app.thread.schedulePauseActivity(prev.appToken, prev.finishing,
                        userLeaving, prev.configChangeFlags);
                ...
            } catch (Exception e) {
                ...
            }
        } else {
            ...
        }
        //设置pause超时时间
        if (mPausingActivity != null) {
            ...
            Message msg = mHandler.obtainMessage(PAUSE_TIMEOUT_MSG);
            msg.obj = prev;
            prev.pauseTime = SystemClock.uptimeMillis();
            mHandler.sendMessageDelayed(msg, PAUSE_TIMEOUT);
            ...
        } else {
            ...
        }

prev是ActivityRecord也即是MainActivity,prev.app是ProcessRecord实例,prev.app.thread是IApplicationThread类型,schedulePauseActivity的调用会跨进程。跨进程请求方是AMS,接收方是App进程。
分析一下它的参数:

  • prev.appToken:com.android.server.am.ActivityRecord#appToken,构造ActivityRecord的时候创建的,用于唯一标识Activity。向AMS发送startActivity跨进程请求时的android.app.Activity#mToken就是它
  • prev.finishing:是否正在finishing
  • onUserLeaving:传入的false,后续用到再分析
  • prev.configChangeFlags:ActivityRecord的一个int属性,当前是0,用到再分析

至此,AMS要向App进程发起第二次握手了,回到App进程继续执行。startActivity还不算完。

总结

AMS要经过一系列的操作如:Activity信息收集、uid和pid获取、权限检查、Task和mHistory的处理等一系列操作,才能确定要启动Activity,尝试启动时又需要把前一个Activity pause掉,因此AMS无可奈何,只能请求App进程把前一个Activity把pause,由此拉开了第二次握手的序幕。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,142评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,298评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,068评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,081评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,099评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,071评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,990评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,832评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,274评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,488评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,649评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,378评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,979评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,625评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,643评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,545评论 2 352