Android启动Activity源码分析(五)

再遇Looper&构造ActivityClientRecord

上一篇文章分析到:app.thread.scheduleLaunchActivity会跨进程通知App进程启动Activity。thread是android.app.ActivityThread.ApplicationThread类型的实例。
继续分析android.app.ActivityThread.ApplicationThread#scheduleLaunchActivity:

        public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
                ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo,
                Bundle state, List<ResultInfo> pendingResults,
                List<Intent> pendingNewIntents, boolean notResumed, boolean isForward,
                String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler) {
            ActivityClientRecord r = new ActivityClientRecord();

            r.token = token;
            r.ident = ident;
            r.intent = intent;
            r.activityInfo = info;
            r.compatInfo = compatInfo;
            r.state = state;

            r.pendingResults = pendingResults;
            r.pendingIntents = pendingNewIntents;

            r.startsNotResumed = notResumed;
            r.isForward = isForward;

            r.profileFile = profileName;
            r.profileFd = profileFd;
            r.autoStopProfiler = autoStopProfiler;

            //系统信息相关内容,场景无关
            updatePendingConfiguration(curConfig);

            queueOrSendMessage(H.LAUNCH_ACTIVITY, r);
        }

参数挺多的,都是用于构造ActivityClientRecord实例,前几篇文章讲过ActivityClientRecord是用来记录App进程侧的Activity的,我们之前使用过MainActivity的ActivityClientRecord,而这次构造的是属于TargetActivity的ActivityClientRecord。

    private void queueOrSendMessage(int what, Object obj) {
        //是不是有点眼熟
        queueOrSendMessage(what, obj, 0, 0);
    }

是的,之前在pause掉MainActivity的时候就是调用的它,往looper里面抛信息的。这里思考一个问题为什么要用looper去异步执行?而不是直接顺序同步执行?
稍微思考一下即可得到答案:Binder是同步调用的,也就是说执行到这AMS还在等待App进程执行完成呢,同样startActivity最初的调用处还在等待AMS执行完成呢,如果不用looper异步执行,那么系统进程会一直被挂在那里,这哪能行呢,AMS还要处理其他的startActivity或者其他的跨进程调用呢。
因此当系统进程通知App进程执行任务时,都是通过looper的方式来执行的。就好比一个母猪十个崽,奶就在那,已经告诉你吃奶了,爱吃不吃,还有剩下九个崽要管呢。
android.app.ActivityThread.H#handleMessage:

        public void handleMessage(Message msg) {
            switch (msg.what) {
                case LAUNCH_ACTIVITY: {
                    ...
                    ActivityClientRecord r = (ActivityClientRecord)msg.obj;

                    r.packageInfo = getPackageInfoNoCheck(
                            r.activityInfo.applicationInfo, r.compatInfo);
                    handleLaunchActivity(r, null);
                    ...
                } break;
            ...
        }

onCreate、onStart、onResume的准备工作

继续分析android.app.ActivityThread#getPackageInfoNoCheck:


    public final LoadedApk getPackageInfoNoCheck(ApplicationInfo ai,
            CompatibilityInfo compatInfo) {
        return getPackageInfo(ai, compatInfo, null, false, true);
    }
    private LoadedApk getPackageInfo(ApplicationInfo aInfo, CompatibilityInfo compatInfo,
            ClassLoader baseLoader, boolean securityViolation, boolean includeCode) {
        synchronized (mPackages) {
            WeakReference<LoadedApk> ref;
            //先从内存缓存里取Apk信息
            if (includeCode) {
                ref = mPackages.get(aInfo.packageName);
            } else {
                ref = mResourcePackages.get(aInfo.packageName);
            }
            LoadedApk packageInfo = ref != null ? ref.get() : null;
            //取不到的话创建一个,并且放在内存缓存里,下次可以直接用
            if (packageInfo == null || (packageInfo.mResources != null
                    && !packageInfo.mResources.getAssets().isUpToDate())) {
                ...
                packageInfo =
                    new LoadedApk(this, aInfo, compatInfo, this, baseLoader,
                            securityViolation, includeCode &&
                            (aInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0);
                if (includeCode) {
                    mPackages.put(aInfo.packageName,
                            new WeakReference<LoadedApk>(packageInfo));
                } else {
                    mResourcePackages.put(aInfo.packageName,
                            new WeakReference<LoadedApk>(packageInfo));
                }
            }
            return packageInfo;
        }
    }

具体如何执行不再分析,该函数就是获得了Apk的信息。赋值给了r.packageInfo。
android.app.ActivityThread#handleLaunchActivity:

    private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ...
        Activity a = performLaunchActivity(r, customIntent);

        if (a != null) {
            r.createdConfig = new Configuration(mConfiguration);
            Bundle oldState = r.state;
            handleResumeActivity(r.token, false, r.isForward);
            ...
        } else {
            ...
        }
    }

onCreate、onStart的调用

函数代码比较长,实际上场景相关的只有两行,分别是performLaunchActivity和handleResumeActivity的调用。
先分析android.app.ActivityThread#performLaunchActivity。
函数比较长(难道google编码规范,没有限制函数行数吗,100多行了),分两段进行分析,第一段:


    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ActivityInfo aInfo = r.activityInfo;
        //若没有拿到Apk信息,再次尝试取拿,不走这,之前已经拿到了Apk信息,上文分析过
        if (r.packageInfo == null) {
            r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
                    Context.CONTEXT_INCLUDE_CODE);
        }

        ComponentName component = r.intent.getComponent();
        //当前场景的组件名不会为null。若是null,还会尝试向PMS查找
        if (component == null) {
            component = r.intent.resolveActivity(
                mInitialApplication.getPackageManager());
            r.intent.setComponent(component);
        }
        ...

        Activity activity = null;
        try {
            //拿到ClassLoader
            java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
            //利用ClassLoader和组件名(TargetActivity类全称),实例化TargetActivity
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            //不知道干啥用的,场景无关
            StrictMode.incrementExpectedActivityCount(activity.getClass());
            //一些赋值
            r.intent.setExtrasClassLoader(cl);
            if (r.state != null) {
                r.state.setClassLoader(cl);
            }
        } catch (Exception e) {
            ...
        }
        //第一段结束
        ...
    }

第一段核心内容:创建Activity实例。
android.app.Instrumentation#newActivity(java.lang.ClassLoader, java.lang.String, android.content.Intent):

    public Activity newActivity(ClassLoader cl, String className,
            Intent intent)
            throws InstantiationException, IllegalAccessException,
            ClassNotFoundException {
        return (Activity)cl.loadClass(className).newInstance();
    }

创建过程很简单。
继续分析系第二段:

    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ...
        //第二段开始
        try {
            //拿到App进程对应的Application对象
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);
            ...

            if (activity != null) {
                //设置Activity相关的Context内容
                ContextImpl appContext = new ContextImpl();
                appContext.init(r.packageInfo, r.token, this);
                appContext.setOuterContext(activity);
                ...
                //把一些信息设置个Activity,其中Activity的”身份证”mToken就是在这里设置的
                activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config);
                ...
                activity.mCalled = false;
                //调用onCreate
                mInstrumentation.callActivityOnCreate(activity, r.state);
                if (!activity.mCalled) {
                    throw new SuperNotCalledException(
                        "Activity " + r.intent.getComponent().toShortString() +
                        " did not call through to super.onCreate()");
                }
                //把Activity记录给ActivityClientRecord
                r.activity = activity;
                //还没有resume,先设置成true
                r.stopped = true;
                //onCreate调用后,马上调用onStart
                if (!r.activity.mFinished) {
                    activity.performStart();
                    r.stopped = false;
                }
                ...
                
            }
            //还没有resume,先设置成true
            r.paused = true;
            
            //把ActivityClientRecord记录下来。前面文章中使用过mActivities查找MainActivity的ActivityClientRecord
            mActivities.put(r.token, r);

        } 
        ...

        return activity;
    }

这函数主要干了一下几件事:

  1. 创建Activity实例
  2. 给Activity设置好Context
  3. 设置attach一些Activity需要的信息
  4. 调用Activity的onCreate方法
  5. 调用Activity的onStart方法
  6. 把Activity实例记录下来,方便后续查找
    这里再简单分析下onCreate的调用android.app.Instrumentation#callActivityOnCreate:
    public void callActivityOnCreate(Activity activity, Bundle icicle) {
        ...
        activity.performCreate(icicle);
        ...
    }
    final void performCreate(Bundle icicle) {
        onCreate(icicle);
        ...
        //Fragment的onActivityCreated分发
        mFragments.dispatchActivityCreated();
    }

调用和onPause类似,也是用过Instrumentation进行调用,也会验证是否调用了super方法。不再赘述。
在onCreate里会调用setContentView来设置布局。
android.app.Activity#setContentView(int):

    public void setContentView(int layoutResID) {
        getWindow().setContentView(layoutResID);
        initActionBar();
    }

getWindow()返回的是com.android.internal.policy.impl.PhoneWindow类型的。

    @Override
    public void setContentView(int layoutResID) {
        if (mContentParent == null) {
            // 构造DecorView
            installDecor();
        } else {
            mContentParent.removeAllViews();
        }
        // 根据xml创建View树
        mLayoutInflater.inflate(layoutResID, mContentParent);
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
    }

setContentView构造DecorView和View树,这里不是本文重点,不再分析。

onResume的调用

继续回到handleLaunchActivity中,分析:
android.app.ActivityThread#handleResumeActivity:

    final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward) {
        ...
        // 执行onResume
        ActivityClientRecord r = performResumeActivity(token, clearHide);

        if (r != null) {
            final Activity a = r.activity;

            if (localLOGV) Slog.v(
                TAG, "Resume " + r + " started activity: " +
                a.mStartedActivity + ", hideForNow: " + r.hideForNow
                + ", finished: " + a.mFinished);
    
            final int forwardBit = isForward ?
                    WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;

            // If the window hasn't yet been added to the window manager,
            // and this guy didn't finish itself or start another activity,
            // then go ahead and add the window.
            boolean willBeVisible = !a.mStartedActivity;
            if (!willBeVisible) {
                ...
            }
            //记录Window、DecorView等。最后把View加进Window(使用ViewRootImpl进行add)
            if (r.window == null && !a.mFinished && willBeVisible) {
                r.window = r.activity.getWindow();
                View decor = r.window.getDecorView();
                //暂时设置成INVISIBLE
                decor.setVisibility(View.INVISIBLE);
                ViewManager wm = a.getWindowManager();
                WindowManager.LayoutParams l = r.window.getAttributes();
                a.mDecor = decor;
                l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
                l.softInputMode |= forwardBit;
                if (a.mVisibleFromClient) {
                    a.mWindowAdded = true;
                    wm.addView(decor, l);
                }
            } else if (!willBeVisible) {
                ...
            }
            ...
                    // 里面会把DecorView设置成VISIBLE
                    r.activity.makeVisible();
    }

代码比较长,但场景有用的就两步:

  1. 调用onResume函数
  2. 记录Window、DecorView等到ActivityClientRecord里,并把View添加进Window

下面我们继续分析android.app.ActivityThread#performResumeActivity:

    public final ActivityClientRecord performResumeActivity(IBinder token,
            boolean clearHide) {
        //根据token拿ActivityClientRecord,老套路了
        ActivityClientRecord r = mActivities.get(token);
        ...
        //显而易见,走这儿
        if (r != null && !r.activity.mFinished) {
            //clearHide传入的是true,设置了一些变量,一时半会儿用不到
            if (clearHide) {
                r.hideForNow = false;
                r.activity.mStartedActivity = false;
            }
            try {
                ...
                //里面用调用onResume
                r.activity.performResume();
                ...

                //这俩在调用onCreate的时候都设置成true了,现在已经resume了,设置成false
                r.paused = false;
                r.stopped = false;
                r.state = null;
            } catch (Exception e) {
                ...
            }
        }
        return r;
    }

过程很简单,注释里已经能描述清楚了。主要是调用onResume和设置状态值。
继续看下android.app.Activity#performResume:

    final void performResume() {
        //里面会调用onRestart声明和周期函数
        performRestart();
        ...
        mCalled = false;
        //依然是老套路,通过Instrumentation调用onResume,并且必须调用super方法
        mInstrumentation.callActivityOnResume(this);
        if (!mCalled) {
            throw new SuperNotCalledException(
                "Activity " + mComponent.toShortString() +
                " did not call through to super.onResume()");
        }

        mCalled = false;
        
        //Fragement分发resume
        mFragments.dispatchResume();
        ...
        if (!mCalled) {
            throw new SuperNotCalledException(
                "Activity " + mComponent.toShortString() +
                " did not call through to super.onPostResume()");
        }
    }

onResume和onPause、onCreate不同的是,它会尝试调用一下onRestart。
android.app.Activity#performRestart:

    final void performRestart() {
        mFragments.noteStateNotSaved();

        //很显然这里是false,不会走进去。只有当退出Activity的时候才是true,才会调用onRestart声明周期函数
        if (mStopped) {
            ....
            performStart();
        }
    }

这里也印证了启动Activity的声明周期函数不包括onRestart。
android.app.Instrumentation#callActivityOnResume:

    public void callActivityOnResume(Activity activity) {
        activity.mResumed = true;
        activity.onResume();
        ...
    }

这里就是onResume的调用了。

任务完成

我们知道周期函数是在looper里执行的,跨进程的调用实际上早已返回到了AMS中。当Handler的LAUNCH_ACTIVITY消息发后,这时AMS的主要任务就已经完成了。而App进程侧完成了LAUNCH_ACTIVITY消息里的内容后,TargetActivity就算真正被启动了。

总结

App进程侧的任务执行都通过Looper来执行的,不能一直阻塞着系统进程。

  1. App进程侧记录了构造ActivityClientRecord,并且通过android.app.Activity#mToken映射好,存下来,以备以后使用。
  2. 调用了onCreate、onStart
  3. 调用onResume。调用前检查了是否需要调用onRestart,发现不用调用。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念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