Activity 启动流程(六)—— Activity 窗口显示

点击阅读:Activity 启动流程系列文章

点击阅读:Activity 启动流程系列文章

阅读本文前,如果不了解 Activity、Window 和 View 之间的关系,可以先阅读:Android 窗口显示(一)------ Activity、Window 和 View 之间的联系


本文中将以 Activity 生命周期为时间线,分析 Activity 的窗口显示流程。

1. Activity onCreate 回调执行前

performLaunchActivity 方法中,在 onCreate 方法回调执行前,做了以下几件事:

  • 反射创建 Activity 实例
  • 调用 Activity attach 方法关联上下文
  • 初始化 PhoneWindow
  • 调用 onCreate 方法

1.1 Activity 实例创建与初始化

看一下 Activity 启动过程中的 performLaunchActivity 方法:

java 复制代码
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    ...
    Activity activity = null;
    activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
    if (activity != null) {
        Window window = null;
        activity.attach(activityBaseContext, 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.activityConfigCallback,
                        r.assistToken, r.shareableActivityToken);
    }
    ...
    if (r.isPersistable()) {
        mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
    } else {
        mInstrumentation.callActivityOnCreate(activity, r.state);
    }
    
}

1.2 attach 关联上下文

此方法中通过反射创建了 Activity 实例,接着调用 attach 方法为 Activity 关联上下文环境:

java 复制代码
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, IBinder assistToken,
        IBinder shareableActivityToken) {
    ...
    // 创建 PhoneWindow 对象,PhoneWindow 初始化时创建了 DecorView 对象
    mWindow = new PhoneWindow(this, window, activityConfigCallback);
    mWindow.setWindowControllerCallback(mWindowControllerCallback);
    mWindow.setCallback(this);
    mWindow.setOnWindowDismissedCallback(this);
    mWindow.getLayoutInflater().setPrivateFactory(this);
    if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
        mWindow.setSoftInputMode(info.softInputMode);
    }
    if (info.uiOptions != 0) {
        mWindow.setUiOptions(info.uiOptions);
    }
    mUiThread = Thread.currentThread();

    mMainThread = aThread;
    ...

    // PhoneWindow set WindowManager
    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 转为 WindowManagerImpl 对象,WindowManagerImpl 为 WindowManager 的实现类
    mWindowManager = mWindow.getWindowManager();
    mCurrentConfig = config;

    mWindow.setColorMode(info.colorMode);
    mWindow.setPreferMinimalPostProcessing(
            (info.flags & ActivityInfo.FLAG_PREFER_MINIMAL_POST_PROCESSING) != 0);

    getAutofillClientController().onActivityAttached(application);
    setContentCaptureOptions(application.getContentCaptureOptions());
}

1.3 初始化 PhoneWindow

在 attach 方法中创建了 PhoneWindow 对象,这里可以看出来每一个 Activity 都会对应一个 PhoneWindow。因为传入的 preservedWindow 为 null,所以不走大括号里的逻辑。

每个 Activity 都会创建一个 Window 用于承载 View 视图的显示,Window 是一个抽象类,PhoneWindow 是其唯一的实现类。看一下 PhoneWindow 的构造方法:

java 复制代码
public PhoneWindow(@UiContext Context context, Window preservedWindow,
        ActivityConfigCallback activityConfigCallback) {
    this(context);
    // Only main activity windows use decor context, all the other windows depend on whatever
    // context that was given to them.
    mUseDecorContext = true;
    if (preservedWindow != null) {
        // 创建 DecorView
        mDecor = (com.android.internal.policy.DecorView) preservedWindow.getDecorView();
        mElevation = preservedWindow.getElevation();
        mLoadElevation = false;
        mForceDecorInstall = true;
        // If we're preserving window, carry over the app token from the preserved
        // window, as we'll be skipping the addView in handleResumeActivity(), and
        // the token will not be updated as for a new window.
        getAttributes().token = preservedWindow.getAttributes().token;
        // 通过 DecorView 获取 ViewRootImpl 实例
        final ViewRootImpl viewRoot = mDecor.getViewRootImpl();
        if (viewRoot != null) {
            // Clear the old callbacks and attach to the new window.
            viewRoot.getOnBackInvokedDispatcher().clear();
            onViewRootImplSet(viewRoot);
        }
    }
    // Even though the device doesn't support picture-in-picture mode,
    // an user can force using it through developer options.
    boolean forceResizable = Settings.Global.getInt(context.getContentResolver(),
            DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES, 0) != 0;
    mSupportsPictureInPicture = forceResizable || context.getPackageManager().hasSystemFeature(
            PackageManager.FEATURE_PICTURE_IN_PICTURE);
    mActivityConfigCallback = activityConfigCallback;
}

1.4 初始化 WindowManagerImpl

继续看 attach 方法,调用 PhoneWindow 的 setWindowManager 方法,为 PhoneWindow 添加一个 WindowManager 实例,实际上是 WindowManagerImpl 对象:

java 复制代码
public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
        boolean hardwareAccelerated) {
    mAppToken = appToken;
    mAppName = appName;
    mHardwareAccelerated = hardwareAccelerated;
    if (wm == null) {
        wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
    }
    // WindowManagerImpl
    mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}

WindowManagerImpl 作为应用层面的接口实现类,将应用层对窗口的操作请求转发给系统层的 WindowManagerGlobal 进行处理:

java 复制代码
public final class WindowManagerImpl implements android.view.WindowManager {

2. Activity onCreate 回调执行中

Activity onCreate 回调执行中,做了以下几件事:

  • setContentView 流程
  • 创建 DecorView 对象

2.1 调用 Activity onCreate

运行完 Activity 的 attach 方法后,回到 performLaunchActivity 方法中,会调用 Instrumentation 的 callActivityOnCreate 方法执行 Activity onCreate() 方法:

java 复制代码
public void callActivityOnCreate(Activity activity, Bundle icicle) {
    prePerformCreate(activity);
    activity.performCreate(icicle);
    postPerformCreate(activity);
}
java 复制代码
final void performCreate(Bundle icicle) {
    performCreate(icicle, null);
}

final void performCreate(Bundle icicle, PersistableBundle persistentState) {
    ...
    if (persistentState != null) {
        onCreate(icicle, persistentState);
    } else {
        onCreate(icicle);
    }
}

protected void onCreate(@Nullable Bundle savedInstanceState) {
    ...
}

2.2 主动调用 setContentView

我们在实现 Activity 的时候都会在 onCreate 方法中调用 setContentView 方法传入布局。并且一般我们的 Activity 都会继承自 AppCompatActivity。

看一下 AppCompatActivity 的 setContentView 方法:

java 复制代码
@Override
public void setContentView(View view) {
    initViewTreeOwners();
    getDelegate().setContentView(view);
}

接着调用了 AppCompatDelegateImpl 的 setContentView 方法:

java 复制代码
@Override
public void setContentView(int resId) {
    ensureSubDecor();
    ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content);
    contentParent.removeAllViews();
    LayoutInflater.from(mContext).inflate(resId, contentParent);
    mAppCompatWindowCallback.bypassOnContentChanged(mWindow.getCallback());
}

private void ensureSubDecor() {
    if (!mSubDecorInstalled) {
        mSubDecor = createSubDecor();
    }
}

private ViewGroup createSubDecor() {
    mWindow.getDecorView();
}

2.3 创建 DecorView 对象

接着调用了 PhoneWindow 的 getDecorView 方法:

java 复制代码
@Override
public final @NonNull View getDecorView() {
    if (mDecor == null || mForceDecorInstall) {
        installDecor();
    }
    return mDecor;
}

private void installDecor() {
    mForceDecorInstall = false;
    if (mDecor == null) {
        mDecor = generateDecor(-1);
        mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
        mDecor.setIsRootNamespace(true);
        if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
            mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
        }
    } else {
        mDecor.setWindow(this);
    }
    ...
}

protected DecorView generateDecor(int featureId) {
    // System process doesn't have application context and in that case we need to directly use
    // the context we have. Otherwise we want the application context, so we don't cling to the
    // activity.
    Context context;
    if (mUseDecorContext) {
        Context applicationContext = getContext().getApplicationContext();
        if (applicationContext == null) {
            context = getContext();
        } else {
            context = new DecorContext(applicationContext, this);
            if (mTheme != -1) {
                context.setTheme(mTheme);
            }
        }
    } else {
        context = getContext();
    }
    return new DecorView(context, featureId, this, getAttributes());
}

Activity 首次调用 setContentView 时,会通过 installDecor 方法创建了一个 DecorView 对象,最终 getDecorView 将该对象返回。

通过 getDecorView 方法创建了 DecorView:

java 复制代码
@Override
public final @NonNull View getDecorView() {
    if (mDecor == null || mForceDecorInstall) {
        installDecor();
    }
    return mDecor;
}

通过 installDecor 初始化 DecorView 以及其内部的 content:

java 复制代码
private void installDecor() {
    mForceDecorInstall = false;
    if (mDecor == null) {
        // 创建 DecorView 实例,初始化 DecorView,是Activity的根View
        mDecor = generateDecor(-1);
        mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
        mDecor.setIsRootNamespace(true);
        if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
            mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
        }
    } else {
        mDecor.setWindow(this);
    }
    if (mContentParent == null) {
        // 返回 DecorView 中的 contentView
        mContentParent = generateLayout(mDecor);

        ...
    }
}

3. Activity onResume 回调执行后

在 Activity 的 onResume 生命周期回调执行完成后,WindowManagerImpl 的 addView 方法执行完成后,Activity 才会显示到屏幕上了。

Activity onResume 回调执行后,做了以下几件事:

  • 构建布局参数
  • 调用 WindowManagerGlobal 的 addView 方法
  • 创建 ViewRootImpl 对象,调用 setView 方法

3.1 构建布局参数

接着看一下 ActivityThread 的 handleResumeActivity 方法:

java 复制代码
@Override
public void handleResumeActivity(ActivityClientRecord r, boolean finalStateRequest,
        boolean isForward, boolean shouldSendCompatFakeFocus, String reason) {
    ...
    // performResumeActivity 方法内部会走 onResume
    if (!performResumeActivity(r, finalStateRequest, reason)) {
        return;
    }
    ...
    // r 为 ActivityClientRecord, ActivityClientRecord 中的 window 属性为 null,并且 Activity 没有完成且即将可见
    if (r.window == null && !a.mFinished && willBeVisible) {
        r.window = r.activity.getWindow();// PhoneWindow
        View decor = r.window.getDecorView();// 通过 PhoneWindow 获取到 DecorView
        decor.setVisibility(View.INVISIBLE);// DecorView 设置为不可见
        // WindowManagerImpl 实现了 ViewManager 接口
        ViewManager wm = a.getWindowManager();// 获取到 WindowManagerImpl 对象,在 Activity attach 中赋值给 mWindowManager
        WindowManager.LayoutParams l = r.window.getAttributes();
        a.mDecor = decor;// 将 DecorView 与 Activity 关联起来
        l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;// 应用程序最基本的窗口
        l.softInputMode |= forwardBit;
        if (r.mPreserveWindow) {
            a.mWindowAdded = true;
            r.mPreserveWindow = false;
            // Normally the ViewRoot sets up callbacks with the Activity
            // in addView->ViewRootImpl#setView. If we are instead reusing
            // the decor view we have to notify the view root that the
            // callbacks may have changed.
            ViewRootImpl impl = decor.getViewRootImpl();
            if (impl != null) {
                impl.notifyChildRebuilt();
            }
        }
        // 如果 Activity 可见
        if (a.mVisibleFromClient) {
            if (!a.mWindowAdded) {
                a.mWindowAdded = true;
                // WindowManagerImpl addView(DecorView, WindowManager.LayoutParams)
                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 the window has already been added, but during resume
        // we started another activity, then don't yet make the
        // window visible.
    }
}

3.2 WindowManagerImpl addView

构建好了布局参数 WindowManager.LayoutParams l 之后,调用了 WindowManagerImpl 的 addView 方法:

java 复制代码
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
    applyTokens(params);
    mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,
            mContext.getUserId());
}

3.3 创建 ViewRootImpl 对象,调用 setView 方法

接着调用了 WindowManagerGlobal 的 addView 方法:

java 复制代码
public void addView(View view, ViewGroup.LayoutParams params,
                    Display display, android.view.Window parentWindow, int userId) {
    ...

    ViewRootImpl root;
    View panelParentView = null;

        ...

        // 创建 ViewRootImpl 对象,一个 Activity 对应一个 Window 对应一个 ViewRootImpl。这句话不一定对,因为 ArrayList<ViewRootImpl> mRoots
        if (windowlessSession == null) {
            root = new ViewRootImpl(view.getContext(), display);
        } else {
            root = new ViewRootImpl(view.getContext(), display,
                    windowlessSession, new WindowlessWindowLayout());
        }

        view.setLayoutParams(wparams);

        mViews.add(view);
        mRoots.add(root);
        mParams.add(wparams);

        // do this last because it fires off messages to start doing things
        try {
            // DecorView
            root.setView(view, wparams, panelParentView, userId);
        } catch (RuntimeException e) {
            final int viewIndex = (index >= 0) ? index : (mViews.size() - 1);
            // BadTokenException or InvalidDisplayException, clean up.
            if (viewIndex >= 0) {
                removeViewLocked(viewIndex, true);
            }
            throw e;
        }
    }
}

最终调用了 ViewRootImpl 的 setView 方,在 Activity 的 onResume 生命周期回调执行完成后,WindowManagerImpl 的 addView 方法执行完成后,Activity 才会显示到屏幕上了。

相关推荐
爱装代码的小瓶子2 分钟前
字符操作函数续上
android·c语言·开发语言·数据结构·算法
用户2018792831672 分钟前
故事:你的“老式弹簧售货机”(Stack<E>)
android
用户20187928316718 分钟前
环形快递传送带大冒险:ArrayDeque 的奇幻之旅
android
前端小巷子18 分钟前
跨标签页通信(三):Web Storage
前端·面试·浏览器
工呈士18 分钟前
TCP 三次握手与四次挥手详解
前端·后端·面试
用户20187928316724 分钟前
故事:你的“急急急快递站”(PriorityQueue<E>)
android
烈焰晴天41 分钟前
新发布的一款使用ReactNative新架构加载Svga动画的开源插件[android/ios]
android·react native·ios
半桔1 小时前
【Linux手册】进程的状态:从创建到消亡的“生命百态”
linux·运维·服务器·汇编·深度学习·面试
wenchun0011 小时前
【面试经验】注册中心与配置中心选择CP还是AP
面试·职场和发展
掘金安东尼2 小时前
,别重复造轮子!新宣【宝藏工具】帮你提效
前端·面试·github