Android启动Activity源码分析(四)

设置旧Activity paused状态

上一篇文章中讲到:ActivityManagerNative.getDefault().activityPaused(token)来继续启动TargetActivity的剩余任务。它会跨进程调用到:
com.android.server.am.ActivityStack#activityPaused:

    public final void activityPaused(IBinder token) {
        final long origId = Binder.clearCallingIdentity();
        mMainStack.activityPaused(token, false);
        Binder.restoreCallingIdentity(origId);
    }

clearCallingIdentity和restoreCallingIdentity作用已经在第二篇文章中讲过。
com.android.server.am.ActivityStack#activityPaused:

    final void activityPaused(IBinder token, boolean timeout) {
        ...
        ActivityRecord r = null;

        synchronized (mService) {
            //获得token对应的ActivityRecord在mHistory中的下标
            int index = indexOfTokenLocked(token);
            if (index >= 0) {
                //获得MainActivity的ActivityRecord
                r = mHistory.get(index);
                //移除pause超时消息
                mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
                //刚刚pause的就是MainActivity
                if (mPausingActivity == r) {
                    ...
                    //记录状态
                    r.state = ActivityState.PAUSED;
                    completePauseLocked();
                } else {
                    ...
                }
            }
        }
    }

把MainActivity的ActivityRecord找出来,设置一下pause状态,因为进程两侧ActivityRecord和ActivityClientRecord的状态要同步。接下来继续执行:
com.android.server.am.ActivityStack#completePauseLocked:

    private final void completePauseLocked() {
        ActivityRecord prev = mPausingActivity;
        ...
        if (prev != null) {
            //不走这
            if (prev.finishing) {
                ...
            } else if (prev.app != null) {//prev.app是ProcessRecord,代表它所属的进程,肯定不是null
                //MainActivity的waitingVisible还是false,不走这
                if (prev.waitingVisible) {
                    ...
                }
                //没有设置过,configDestroy是false
                if (prev.configDestroy) {
                    ...
                } else {//走这里
                    //MainActivity等待stop
                    mStoppingActivities.add(prev);
                    //要等待stop的Activity超过3个了,有一些特殊处理。不走这
                    if (mStoppingActivities.size() > 3) {
                        ...
                    } else {
                        checkReadyForSleepLocked();
                    }
                }
            } else {
                ...
            }
            //pause完了,状态置null
            mPausingActivity = null;
        }
        //AMS不在休眠状态,走这里
        if (!mService.isSleeping()) {
            resumeTopActivityLocked(prev);
        } else {
            //休眠的处理,不走这
            ...
        }
        //一堆电池、cpu时间啥的巴拉巴拉的逻辑,场景无关
        ...
    }

再次尝试resume新Activity

先把MainActivity设置为正在stop的状态,再检查AMS是否正在休眠,至于为什么AMS会休眠、休眠了会怎样,这里不关心,只知道正和AMS通讯呢,一定不在休眠状态。继续分析:
com.android.server.am.ActivityStack#resumeTopActivityLocked(com.android.server.am.ActivityRecord):

    final boolean resumeTopActivityLocked(ActivityRecord prev) {
        return resumeTopActivityLocked(prev, null);
    }

是不是有点眼熟?是的,曾经调用过这个函数,TargetActivity尝试resume,但发现需要先pause掉MainActivity的函数就是它。区别是之前传入的参数prev是null,这里传入的是MainActivity。
com.android.server.am.ActivityStack#resumeTopActivityLocked(com.android.server.am.ActivityRecord, android.os.Bundle):


    final boolean resumeTopActivityLocked(ActivityRecord prev, Bundle options) {
        // 找到mHistory顶部的Activity,也就是TargetActivity
        ActivityRecord next = topRunningActivityLocked(null);
        ...
        //不走这
        if (next == null) {
            ...
        }
        ...
        //要resume的已经resuem了,return掉。这里mResumedActivity是null了。
        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)) {
            ...
            return false;
        }
        //又把next的相关状态重置了一遍,说实话有点重复
        mStoppingActivities.remove(next);
        mGoingToSleepActivities.remove(next);
        next.sleeping = false;
        mWaitingVisibleActivities.remove(next);
        ...
        //上一次调用走这里了,但这一次mResumedActivity是null,不走这
        if (mResumedActivity != null) {
            ...
            return true;
        }
        
        //最近一次pause的Activity就是MainActivity
        final ActivityRecord last = mLastPausedActivity;
        //不走这
        if (mService.mSleeping && last != null && !last.finishing) {
            ...
        }
        
        //满足条件
        if (prev != null && prev != next) {
            //waitingVisible代表是否真该等待新的Activity变的可见,nowVisible当前Activity是否可见。MainActivity的waitingVisible还是false,TargetActivity的nowVisible也是false。
            if (!prev.waitingVisible && next != null && !next.nowVisible) {
                //进入等TargetActivity显示的状态
                prev.waitingVisible = true;
                mWaitingVisibleActivities.add(prev);
                ...
            }
            
        //一堆和Window有关的逻辑,以为新的Activity要显示了,通知一下WMS(管理Window的服务)
        ...
        //TargetActivity的进程信息还是null,不走这
        if (next.app != null && next.app.thread != null) {
        } else {
            //设置被启动过的标识
            if (!next.hasBeenLaunched) {
                next.hasBeenLaunched = true;
            }
            ...
            //下面继续分析
            startSpecificActivityLocked(next, true, true);
        }

        return true;
    }

这个函数之前走过一次,是为了pasue掉MainActivity,这一次是为了真正把TargetActivity resume起来。个人不太理解为什么不把两次调用拆分成两个函数,这样会更清晰。其实这个函数对于当前场景,也没做事情有限,只设置了一些状态。

为resume新Activity准备

下面继续分析:
com.android.server.am.ActivityStack#startSpecificActivityLocked:

    private final void startSpecificActivityLocked(ActivityRecord r,
            boolean andResume, boolean checkConfig) {
        //根据进程名和uid定位到TargetActivity的进程
        ProcessRecord app = mService.getProcessRecordLocked(r.processName,
                r.info.applicationInfo.uid);
        //设置TargetActivity的启动时间
        if (r.launchTime == 0) {
            r.launchTime = SystemClock.uptimeMillis();
            if (mInitialStartTime == 0) {
                mInitialStartTime = r.launchTime;
            }
        } else if (mInitialStartTime == 0) {
            mInitialStartTime = SystemClock.uptimeMillis();
        }
        
        //进程已经启动了,app和app.thread都不是null。其中Thread是IApplicationThread类型。
        if (app != null && app.thread != null) {
            try {
                //多个包名可以公用一个进程,这里把包名加到进程信息里
                app.addPackage(r.info.packageName);
                //后面继续分析
                realStartActivityLocked(r, app, andResume, checkConfig);
                return;
            }
            ...
        }
        ...
    }

把TargetActivity所属的进程信息设置好,启动时间设置好,进程信息补充一部分。

通知App进程自己完成Activity启动

继续分析:
com.android.server.am.ActivityStack#realStartActivityLocked:

    final boolean realStartActivityLocked(ActivityRecord r,
            ProcessRecord app, boolean andResume, boolean checkConfig)
            throws RemoteException {
            ...
        r.app = app;
        ...
        int idx = app.activities.indexOf(r);
        if (idx < 0) {
            app.activities.add(r);
        }
        ...
        try {
            //走到这里儿,还是null,不异常等什么
            if (app.thread == null) {
                throw new RemoteException();
            }
            //准备夸进程参数
            List<ResultInfo> results = null;
            List<Intent> newIntents = null;
            if (andResume) {
                results = r.results;
                newIntents = r.newIntents;
            }
            ...
            //设置一些TargetActivity的状态
            r.sleeping = false;
            r.forceNewConfig = false;
            ...
            //设置一些进程状态
            app.hasShownUi = true;
            app.pendingUiClean = true;
            ...
            //通知App进程,要启动Activity了!,正主来了。
            app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
                    System.identityHashCode(r), r.info,
                    new Configuration(mService.mConfiguration),
                    r.compat, r.icicle, results, newIntents, !andResume,
                    mService.isNextTransitionForward(), profileFile, profileFd,
                    profileAutoStop);
            ...
        } catch (RemoteException e) {
        ...
        }
        r.launchFailed = false;
        ...
        //andResume传入的true
        if (andResume) {
            //启动完成了,也就是完成了create和resume了。设置好状态。
            r.state = ActivityState.RESUMED;
            r.stopped = false;
            //注意这里,MainActivity启动的时候mResumedActivity是MainActivity。现在启动TargetActivity,mResumedActivity设置成TargetActivity。
            mResumedActivity = r;
            ...
        }
        ...
        return true;
    }

这个函数里最重要的是通知App去启动Activity,就好比申请了start Activity,AMS通过了,但是真正启动的还是需要App进程自己去完成,毕竟Activity的生命周期方法onCreate、onResume是在App进程里调用的。
准备好了参数,通过app.thread.scheduleLaunchActivity跨进程通知App进程启动Activity。等启动完成后,再设置一些状态AMS的任务就告一段落了。

总结

pause掉旧Activity,准备启动新的Activity,最后却让App进程自行启动Activity。
这是为什么呢?兜兜转一圈不费劲吗?其实这样做是合理的。操作系统的定义:管理计算机硬件与软件资源的计算机程序。如果App进程自己管理Activity,安全性、效率、统一性都会出现问题。而AMS作为系统进程的服务,批准和管理startActivity是再合理不过的了。

由此开始第四次握手。

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