Activity是如何创建出来的?(下)

892 阅读7分钟

前言

当我们调用startActivity方法来启动某个Activity时,系统需要根据该Activity所在进程是否存在而做不同的处理,如果不存在,则需要AMS通过socket向Zygote大佬发起进程fork的请求,接着再从fork出来的子进程中run对应APP进程的ActivityThread main方法。这个过程可以看本文序列文章《我是怎么把一个个APP进程创建起来的?》。

而在这之后,就开始了Activity的创建。这个过程可以看《Activity是如何显示出来的?(上)》。

这一章节承接上一个章节《Activity是如何显示出来的?》继续分析。其实标题更改为《Activity是如何创建出来的?》可能更为合适。

在开始分析之前,我们先来张图大概看看整个流程,方便大家理解。(这个图来自网络,大部分博文都有,已不清楚具体的出处,若有作者看到,可以告知!)

image

概述

每个APP的入口都是ActivityThread的main方法,而在main方法中,主要做了三件事:

1.创建Looper,Handler,Message,用于消息处理,后期AMS对activity的生命周期管理都得间接通过Handler来处理;
2.创建ApplicationThread对象,这个是ActivityThread的内部类,也是个Binder服务端,极其重要,是Activity对外跟AMS通讯的接口。
3.创建Activity对象,并调用onCreate方法,view的创建在此开启。

承接上个章节,从这里开始:

//frameworks/base/core/java/android/app/ActivityThread.java
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    ....
    activity = mInstrumentation.newActivity(
                cl, component.getClassName(), r.intent);
    
    ....
    activity.attach(appContext, this, getInstrumentation(), r.token,
                    r.ident, app, r.intent, r.activityInfo, title, r.parent,
                    r.embeddedID, r.lastNonConfigurationInstances, config,
                    r.referrer, r.voiceInteractor, window, r.configCallback);
    
    ....
    mInstrumentation.callActivityOnPostCreate(activity, r.state);
    ....

}

onCreate

callActivityOnPostCreate方法最终会调用到activity的onCreate方法,而onCreate方法中,我们常见的会调用的方法为setContentView方法,从字面上理解,该方法正是跟设置view相关。进入查看:

frameworks/base/core/java/android/app/Activity.java
public void setContentView(@LayoutRes int layoutResID) {
    getWindow().setContentView(layoutResID);
    initWindowDecorActionBar();
}

Activity中涉及到的setContentView方法有三个,只是各自传参不同,我们挑选了其中一个比较常用的方法进行分析。方法很简单,只有两行代码,这里转身调用getWindow中的方法来进行实现。getWindow方法最终返回的是一个Window对象:

public Window getWindow() {
    return mWindow;
}

mWindow是Activity的一个成员变量,这是何时被创建的呢?答案在Activity的attach方法中。还记得在调用activity的onCreate方法前,先调用了Activity的attach方法么?我们看看它的实现:

final void attach(Context context, ActivityThread aThread,
        Instrumentation instr, IBinder token, int ident,
        Application application, Intent intent, ActivityInfo info,
        CharSequence title, Activity parent, String id,
        NonConfigurationInstances lastNonConfigurationInstances,
        Configuration config, String referrer, IVoiceInteractor voiceInteractor,
        Window window, ActivityConfigCallback activityConfigCallback) {
        
    ...
    
    mWindow = new PhoneWindow(this, window, activityConfigCallback);
    ...
    mWindow.setWindowManager(
            (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
            mToken, mComponent.flattenToString(),
            (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
    if (mParent != null) {
        mWindow.setContainer(mParent.getWindow());
    }
    mWindowManager = mWindow.getWindowManager();
    ...
}

mWindow变量是一个Window对象,但Window类只是一个抽象类,具体的实现方法却是在PhoneWindow类中:

mWindow = new PhoneWindow(this, window, activityConfigCallback);

这里还稍微带出了mWindowManager,这是个WindowManager类型的对象,用来跟WMS通讯,后面会继续介绍。

setContentView

回到setContentView方法上:

getWindow().setContentView(layoutResID);

通过上面的分析,我们知道getWindow方法返回的其实是个PhoneWindow对象,那么setContentView的方法实现也是在这里:

frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java
public void setContentView(int layoutResID) {
    // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
    // decor, when theme attributes and the like are crystalized. Do not check the feature
    // before this happens.
    if (mContentParent == null) {
        installDecor(); //创建DecorView对象和ContentParent对象
    } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
       //如果setContentView方法不是首次调用,这里需要清除ContentParent对象的旧的内容
        mContentParent.removeAllViews(); 
    }

    if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                getContext());
        transitionTo(newScene);
    } else {
        //将view布局文件填充到mContentParent这个容器中
        mLayoutInflater.inflate(layoutResID, mContentParent);
    }
    mContentParent.requestApplyInsets();
    final Callback cb = getCallback(); //获取activity回调
    if (cb != null && !isDestroyed()) {
        cb.onContentChanged(); //通知Activity布局改变了
    }
    mContentParentExplicitlySet = true;
}

在应用界面开发的时候,我们通过一个xml文件来描述整个界面。当Activity首次调用setContentView方法时,需要先创建DecorView对象和ContentParent对象,并将两者进行关联。此时的ContentParent对象的内容还是空的。需要通过inflate方法将布局文件内容填充到ContentParent对象中。

installDecor

installDecor的方法比较长,主要关注两个地方:

private void installDecor() {

    ...
    mDecor = generateDecor(-1); //创建DecorView对象
    ...
    mContentParent = generateLayout(mDecor); //创建ContentParent对象
    ...
}

该方法主要做了两件事,创建DecorView对象和ContentParent对象。其中generateDecor方法比较简单,直接通过new DecorView()的方法返回一个DecorView对象。主要看看generateLayout方法:

protected ViewGroup generateLayout(DecorView decor) {

    //获取窗口风格
    TypedArray a = getWindowStyle();
    mIsFloating = a.getBoolean(R.styleable.Window_windowIsFloating, false);
    ...
    int layoutResource;
    int features = getLocalFeatures();
    //如下部分是根据具体的风格,为layoutresource挑选匹配的资源
    if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
        layoutResource = R.layout.screen_swipe_dismiss;
        setCloseOnSwipeEnabled(true);
    } else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
    
        ...
    }
    ...
    mDecor.startChanging();
    //这里会对匹配到的资源进行解析,并将资源加载到DecorView中
    mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
    //通过findViewById的方法获取ContentParent对象,待填充内容
    ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
    ...
    
    mDecor.finishChanging();

    return contentParent;
}

generateLayout方法中,主要做了四件事:

1.获取窗口风格;
2.根据具体的风格,为layoutresource挑选匹配的资源;
3.将获取到的资源加载到DecorView中,其中包括id为ID_ANDROID_CONTENT的FrameLayout类型的容器,此时内容为空,待填充;
4.通过findViewById的方法获取ContentParent对象;

稍微总结下setContentView方法,如果是第一次调用,则先创建DecorView对象,在这个过程中,会创建一个ContentParent对象,这是一个容器,用于填充用户自定义的view内容。接着通过回调的方法,通知activity,我这边已经将view内容填充完毕。

使用一张图大概描述下DecorView与ContentParent的关系:

image

显示

Activity的onCreate流程执行完,还不能显示出来具体的view内容。要想显示出来,还需要走onResume流程。在上篇文章中,我们知道在realStartActivityLocked方法中,创建了一个加载Activity的事务,等一切就绪,就处理该事务。其实此时在该事务中还会设置activity最终需要显示的状态:resume or pause。具体如下:

//frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java
final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app,
        boolean andResume, boolean checkConfig) throws RemoteException {
    ...
    // Create activity launch transaction.
    final ClientTransaction clientTransaction = ClientTransaction.obtain(app.thread,
            r.appToken);
    clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
            System.identityHashCode(r), r.info,
            // TODO: Have this take the merged configuration instead of separate global
            // and override configs.
            mergedConfiguration.getGlobalConfiguration(),
            mergedConfiguration.getOverrideConfiguration(), r.compat,
            r.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle,
            r.persistentState, results, newIntents, mService.isNextTransitionForward(),
            profilerInfo));

    // Set desired final state.
    final ActivityLifecycleItem lifecycleItem;
    if (andResume) {
        lifecycleItem = ResumeActivityItem.obtain(mService.isNextTransitionForward());
    } else {
        lifecycleItem = PauseActivityItem.obtain();
    }
    clientTransaction.setLifecycleStateRequest(lifecycleItem);

    // Schedule transaction.
    mService.getLifecycleManager().scheduleTransaction(clientTransaction);
    ...
}

如果我们想要启动一个activity,则此时我们走的是resume的分支,即此时clientTransaction会被设置一个ResumeActivityItem类型的lifecycleItem 。在TransactionExecutor类中,执行完LaunchActivityItem后,就会执行resume流程:

/frameworks/base/core/java/android/app/servertransaction/TransactionExecutor.java

public void execute(ClientTransaction transaction) {
    final IBinder token = transaction.getActivityToken();
    log("Start resolving transaction for client: " + mTransactionHandler + ", token: " + token);

    executeCallbacks(transaction); //这里最终会执行onCreate流程

    executeLifecycleState(transaction); //这里最终会执行onResume流程
    mPendingActions.clear();
    log("End resolving transaction");
}

 private void executeLifecycleState(ClientTransaction transaction) {
    final ActivityLifecycleItem lifecycleItem = transaction.getLifecycleStateRequest();

    ...
    // Execute the final transition with proper parameters.
    lifecycleItem.execute(mTransactionHandler, token, mPendingActions);
    lifecycleItem.postExecute(mTransactionHandler, token, mPendingActions);
}

这里实际上执行了ResumeActivityItem的execute方法,最终调用到了ActivityThread的handleResumeActivity方法:

//frameworks/base/core/java/android/app/ActivityThread.java
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
        String reason) {
    
    ...
    //最终调用activity的onResume方法
    final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
    ...
    ViewManager wm = a.getWindowManager();
    WindowManager.LayoutParams l = r.window.getAttributes();
    View decor = r.window.getDecorView();
    decor.setVisibility(View.INVISIBLE);
    ...
    if (a.mVisibleFromClient) {
        if (!a.mWindowAdded) {
            a.mWindowAdded = true;
            wm.addView(decor, l);
        } else {
            // The activity will get a callback for this {@link LayoutParams} change
            // earlier. However, at that time the decor will not be set (this is set
            // in this method), so no action will be taken. This call ensures the
            // callback occurs with the decor set.
            a.onWindowAttributesChanged(l);
        }
    }
    
    ...
        if (r.activity.mVisibleFromClient) {
            r.activity.makeVisible();
        }
    ...

}

handleResumeActivity方法中先获取DecorView对象(在serContentView的方法中创建),然后通过WindowManager方法添加到窗口中,接着再调用makeVisible方法显示出来。

//frameworks/base/core/java/android/app/Activity.java
void makeVisible() {
    if (!mWindowAdded) {
        ViewManager wm = getWindowManager();
        wm.addView(mDecor, getWindow().getAttributes());
        mWindowAdded = true;
    }
    mDecor.setVisibility(View.VISIBLE);
}

结语

用两个章节稍微走了一遍activity的创建过程,期间我们大概了解到,AMS把控着activity的生命周期管理,通过IApplicationThread跟Activity进行通讯, ApplicationThread作为ActivityThread的内部类,充满各种各样的schedule方法,提供给AMS使用。ApplicationThread本身只是个通讯接口,不做具体的业务处理,通过Handler将具体的处理放到了ActivityThread中。

onCreate流程创建了DecorView对象和ContentParent对象,并将两者进行了关联,关联的关键是id号为ID_ANDROID_CONTENT的FrameLayout类型的容器。最后将开发者自定义的view填充到了mContentParent这个容器中。之后则开始了onResume流程,在这里,最终调用makeVisible方法显示出整个界面,包括标题栏和开发者自定义的view。

从前面的分析也看到,显示的关键在于addView和makeVisible,前者会将view添加到窗口中,这个过程会涉及到跟WMS的通讯。下个章节则会开始这一块的分析。

微信公众号

我在微信公众号也有写文章,更新比较及时,有兴趣者可以扫描如下二维码,或者微信搜索【Android系统实战开发】,关注有惊喜哦!

关联文章

Activity是如何显示出来的?(上)

我是怎么把一个个App创建起来的?