大厂Android面试秘籍:Activity 窗口管理模块(四)

深入剖析 Android Activity 窗口管理模块:从源码洞悉原理

本人掘金号,欢迎点击关注:掘金号地址

本人公众号,欢迎点击关注:公众号地址

一、引言

在 Android 系统的庞大架构中,Activity 窗口管理模块扮演着至关重要的角色。它负责着应用界面的创建、显示、更新以及与用户交互的处理,是 Android 应用能够呈现出丰富多样界面的基础支撑。通过深入研究 Activity 窗口管理模块的源码,我们可以更加清晰地理解 Android 系统的运行机制,为开发出高质量、高性能的 Android 应用提供有力的理论支持。本文将围绕 Activity 窗口管理模块展开,对其各个关键环节进行源码级别的详细分析。

二、基础概念概述

2.1 Activity 与 Window 的关系

在 Android 中,Activity 是应用程序的一个重要组件,它代表着一个具有用户界面的屏幕。而 Window 则是一个抽象的概念,它是屏幕上的一个矩形区域,用于承载用户界面的内容。每个 Activity 都包含一个 Window 对象,Activity 通过这个 Window 对象来管理和显示界面。

以下是 Activity 中获取 Window 对象的源码示例:

java

java 复制代码
// Activity.java
// 此方法用于获取当前 Activity 所关联的 Window 对象
public Window getWindow() {
    return mWindow; // 返回成员变量 mWindow,它在 Activity 创建时被初始化
}

从上述代码可以看出,Activity 内部维护着一个 Window 对象的引用 mWindow,通过 getWindow() 方法可以方便地获取该对象。

2.2 WindowManager 与 Window 的交互

WindowManager 是一个用于管理 Window 的接口,它提供了添加、更新和删除 Window 的功能。每个 Window 都需要通过 WindowManager 来进行管理和显示。

下面是使用 WindowManager 添加 Window 的简单示例代码:

java

java 复制代码
// 获取 WindowManager 实例
WindowManager windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);

// 创建一个新的 Window 参数
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
        WindowManager.LayoutParams.WRAP_CONTENT, // 宽度为包裹内容
        WindowManager.LayoutParams.WRAP_CONTENT, // 高度为包裹内容
        WindowManager.LayoutParams.TYPE_APPLICATION, // Window 类型为应用程序窗口
        0, // 标志位,这里设置为 0
        PixelFormat.TRANSLUCENT // 像素格式为半透明
);

// 创建一个新的 View 对象,作为 Window 的内容
View view = new View(this);

// 通过 WindowManager 添加 Window
windowManager.addView(view, params); // 将 view 以 params 参数添加到 WindowManager 中进行管理和显示

在上述代码中,首先通过 getSystemService(Context.WINDOW_SERVICE) 方法获取 WindowManager 实例,然后创建 WindowManager.LayoutParams 对象来设置 Window 的参数,接着创建一个 View 对象作为 Window 的内容,最后调用 windowManager.addView(view, params) 方法将 View 添加到 WindowManager 中,从而实现 Window 的显示。

三、Activity 窗口的创建过程

3.1 Activity 启动时窗口创建的入口

当一个 Activity 被启动时,其窗口的创建过程会在 ActivityThread 类中触发。具体来说,ActivityThread 中的 handleLaunchActivity 方法是这个过程的入口。

以下是 handleLaunchActivity 方法的部分源码:

java

java 复制代码
// ActivityThread.java
// 处理启动 Activity 的方法
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
    // 调用 performLaunchActivity 方法来实际启动 Activity
    Activity a = performLaunchActivity(r, customIntent);
    if (a != null) {
        r.createdConfig = new Configuration(mConfiguration);
        reportSizeConfigurations(r);
        Bundle oldState = r.state;
        // 调用 handleResumeActivity 方法让 Activity 进入恢复状态
        handleResumeActivity(r.token, false, r.isForward,
                !r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);
        if (!r.activity.mFinished && r.startsNotResumed) {
            try {
                r.activity.mCalled = false;
                // 调用 Activity 的 onPause 方法
                mInstrumentation.callActivityOnPause(r.activity);
                if (!r.activity.mCalled) {
                    throw new SuperNotCalledException(
                            "Activity " + r.intent.getComponent().toShortString() +
                                    " did not call through to super.onPause()");
                }
                r.paused = true;
            } catch (SuperNotCalledException e) {
                throw e;
            } catch (Exception e) {
                if (!mInstrumentation.onException(r.activity, e)) {
                    throw new RuntimeException(
                            "Unable to pause activity "
                                    + r.intent.getComponent().toShortString() + ": " + e.toString(), e);
                }
            }
        }
    }
}

handleLaunchActivity 方法中,首先调用 performLaunchActivity 方法来实际启动 Activity,该方法会完成 Activity 的创建和初始化工作。然后调用 handleResumeActivity 方法让 Activity 进入恢复状态,在这个过程中会涉及到窗口的创建和显示操作。

3.2 PhoneWindow 的创建与初始化

performLaunchActivity 方法中,会创建并初始化 PhoneWindow 对象。PhoneWindowWindow 的具体实现类,它负责管理 Activity 的窗口界面。

以下是 performLaunchActivity 方法中创建 PhoneWindow 的部分源码:

java

java 复制代码
// ActivityThread.java
// 实际启动 Activity 的方法
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    ActivityInfo aInfo = r.activityInfo;
    if (r.packageInfo == null) {
        r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
                Context.CONTEXT_INCLUDE_CODE);
    }
    ComponentName component = r.intent.getComponent();
    if (component == null) {
        component = r.intent.resolveActivity(
            mInitialApplication.getPackageManager());
        r.intent.setComponent(component);
    }
    if (r.activityInfo.targetActivity != null) {
        component = new ComponentName(r.activityInfo.packageName,
                r.activityInfo.targetActivity);
    }
    // 创建 Activity 类的实例
    Activity activity = null;
    try {
        java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
        // 通过类加载器和 Activity 类名创建 Activity 实例
        activity = mInstrumentation.newActivity(
                cl, component.getClassName(), r.intent);
        StrictMode.incrementExpectedActivityCount(activity.getClass());
        r.intent.setExtrasClassLoader(cl);
        r.intent.prepareToEnterProcess();
        if (r.state != null) {
            r.state.setClassLoader(cl);
        }
    } catch (Exception e) {
        if (!mInstrumentation.onException(activity, e)) {
            throw new RuntimeException(
                "Unable to instantiate activity " + component
                + ": " + e.toString(), e);
        }
    }
    try {
        Application app = r.packageInfo.makeApplication(false, mInstrumentation);
        if (activity != null) {
            Context appContext = createBaseContextForActivity(r, activity);
            // 创建 Activity 的标题
            CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
            Configuration config = new Configuration(mCompatConfiguration);
            if (r.overrideConfig != null) {
                config.updateFrom(r.overrideConfig);
            }
            if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
                    + r.activityInfo.name + " with config " + config);
            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,
                    r.assistToken);
            if (customIntent != null) {
                activity.mIntent = customIntent;
            }
            r.lastNonConfigurationInstances = null;
            activity.mStartedActivity = false;
            int theme = r.activityInfo.getThemeResource();
            if (theme != 0) {
                // 设置 Activity 的主题
                activity.setTheme(theme);
            }
            // 调用 Activity 的 onCreate 方法
            activity.mCalled = false;
            if (r.state != null) {
                // 如果有保存的状态,恢复状态
                mInstrumentation.callActivityOnCreate(activity, r.state);
            } else {
                // 没有保存的状态,正常创建
                mInstrumentation.callActivityOnCreate(activity, null);
            }
            if (!activity.mCalled) {
                throw new SuperNotCalledException(
                    "Activity " + r.intent.getComponent().toShortString() +
                    " did not call through to super.onCreate()");
            }
            r.activity = activity;
            r.stopped = true;
            if (!r.activity.mFinished) {
                // 调用 Activity 的 onStart 方法
                activity.performStart();
                r.stopped = false;
            }
            if (!r.activity.mFinished) {
                if (r.state != null) {
                    // 如果有保存的状态,恢复状态
                    mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
                }
            }
            if (!r.activity.mFinished) {
                // 调用 Activity 的 onPostCreate 方法
                activity.mCalled = false;
                mInstrumentation.callActivityOnPostCreate(activity, r.state);
                if (!activity.mCalled) {
                    throw new SuperNotCalledException(
                        "Activity " + r.intent.getComponent().toShortString() +
                        " did not call through to super.onPostCreate()");
                }
            }
        }
        r.paused = true;
        mActivities.put(r.token, r);
    } catch (SuperNotCalledException e) {
        throw e;
    } catch (Exception e) {
        if (!mInstrumentation.onException(activity, e)) {
            throw new RuntimeException(
                "Unable to start activity " + component
                + ": " + e.toString(), e);
        }
    }
    return activity;
}

performLaunchActivity 方法中,首先通过反射创建 Activity 的实例,然后调用 activity.attach 方法进行 Activity 的初始化。在 attach 方法中会创建 PhoneWindow 对象。

以下是 Activityattach 方法中创建 PhoneWindow 的部分源码:

java

java 复制代码
// Activity.java
// Activity 的初始化方法
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) {
    attachBaseContext(context);
    mFragments.attachHost(null /*parent*/);
    // 创建 PhoneWindow 对象
    mWindow = new PhoneWindow(this, window, activityConfigCallback);
    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;
    mInstrumentation = instr;
    mToken = token;
    mIdent = ident;
    mApplication = application;
    mIntent = intent;
    mComponent = intent.getComponent();
    mActivityInfo = info;
    mTitle = title;
    mParent = parent;
    mEmbeddedID = id;
    mLastNonConfigurationInstances = lastNonConfigurationInstances;
    if (voiceInteractor != null) {
        if (lastNonConfigurationInstances != null) {
            mVoiceInteractor = lastNonConfigurationInstances.voiceInteractor;
        } else {
            mVoiceInteractor = new VoiceInteractor(voiceInteractor, this, this);
        }
    }
    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();
    mCurrentConfig = config;
    mWindow.setColorMode(info.colorMode);
    mReferrer = referrer;
    mAssistToken = assistToken;
    if (lastNonConfigurationInstances != null) {
        mFragments.restoreLoaderNonConfig(lastNonConfigurationInstances.loaders);
    }
    mWindow.setAllowEnterTransitionOverlap(info.allowEnterTransitionOverlap);
    mWindow.setAllowReturnTransitionOverlap(info.allowReturnTransitionOverlap);
    mWindow.setSharedElementEnterTransition(
            TransitionInflater.from(context).inflateTransition(info.sharedElementEnterTransition));
    mWindow.setSharedElementReturnTransition(
            TransitionInflater.from(context).inflateTransition(info.sharedElementReturnTransition));
    mWindow.setEnterTransition(
            TransitionInflater.from(context).inflateTransition(info.windowEnterTransition));
    mWindow.setReturnTransition(
            TransitionInflater.from(context).inflateTransition(info.windowReturnTransition));
    mWindow.setExitTransition(
            TransitionInflater.from(context).inflateTransition(info.windowExitTransition));
    mWindow.setReenterTransition(
            TransitionInflater.from(context).inflateTransition(info.windowReenterTransition));
    mWindow.setAllowEnterTransitionOverlap(info.allowEnterTransitionOverlap);
    mWindow.setAllowReturnTransitionOverlap(info.allowReturnTransitionOverlap);
    mWindow.setSharedElementEnterTransition(
            TransitionInflater.from(context).inflateTransition(info.sharedElementEnterTransition));
    mWindow.setSharedElementReturnTransition(
            TransitionInflater.from(context).inflateTransition(info.sharedElementReturnTransition));
    mWindow.setEnterTransition(
            TransitionInflater.from(context).inflateTransition(info.windowEnterTransition));
    mWindow.setReturnTransition(
            TransitionInflater.from(context).inflateTransition(info.windowReturnTransition));
    mWindow.setExitTransition(
            TransitionInflater.from(context).inflateTransition(info.windowExitTransition));
    mWindow.setReenterTransition(
            TransitionInflater.from(context).inflateTransition(info.windowReenterTransition));
}

attach 方法中,通过 mWindow = new PhoneWindow(this, window, activityConfigCallback); 语句创建了 PhoneWindow 对象,并进行了一系列的初始化设置,如设置回调、输入法模式等。

3.3 DecorView 的创建与添加

DecorViewPhoneWindow 的根视图,它包含了窗口的标题栏、状态栏等元素。在 PhoneWindow 初始化完成后,会创建并添加 DecorView

以下是 PhoneWindow 中创建和添加 DecorView 的部分源码:

java

java 复制代码
// PhoneWindow.java
// 设置窗口的内容视图
@Override
public void setContentView(int layoutResID) {
    if (mContentParent == null) {
        // 安装 DecorView
        installDecor();
    } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        mContentParent.removeAllViews();
    }
    if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                getContext());
        transitionTo(newScene);
    } else {
        // 加载布局资源到 mContentParent 中
        mLayoutInflater.inflate(layoutResID, mContentParent);
    }
    mContentParent.requestApplyInsets();
    final Callback cb = getCallback();
    if (cb != null && !isDestroyed()) {
        // 调用回调的 onContentChanged 方法
        cb.onContentChanged();
    }
    mContentParentExplicitlySet = true;
}

// 安装 DecorView 的方法
private void installDecor() {
    mForceDecorInstall = false;
    if (mDecor == null) {
        // 创建 DecorView 对象
        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) {
        // 获取内容视图的父容器
        mContentParent = generateLayout(mDecor);
        // 设置背景
        if (mDecor.getBackground() == null && mBackgroundFallbackResource != 0) {
            mDecor.setBackgroundFallback(mBackgroundFallbackResource);
        }
        if (hasFeature(FEATURE_SUPPORT_ACTION_BAR)) {
            mDecor.setActionBarVisibility(mActionBar == null || mActionBar.isHideOnContentScrollEnabled());
        }
        if (mDecor.getForeground() == null && mForegroundFallbackResource != 0) {
            mDecor.setForegroundFallback(mForegroundFallbackResource);
        }
        if (mDecor.getBackground() == null && mBackgroundFallbackResource == 0
                && mBackgroundResource != 0) {
            mDecor.setBackgroundResource(mBackgroundResource);
        }
        if (mDecor.getForeground() == null && mForegroundFallbackResource == 0
                && mForegroundResource != 0) {
            mDecor.setForegroundResource(mForegroundResource);
        }
        mDecor.setWindowCallback(mCallback);
        mDecor.setUiOptions(mUiOptions);
        mDecor.setWindowManager(mWindowManager, mAppToken, mComponentName,
                mEmbeddedID != null);
        if (mDecor.getParent() == null) {
            // 将 DecorView 添加到 WindowManager 中
            mWindowManager.addView(mDecor, getAttributes());
        }
        mDecor.setContentInsets(mContentInsets, mStableInsets);
        mDecor.setFitSystemWindows(mForceDecorFitSystemWindows);
        mDecor.requestApplyInsets();
    }
}

// 生成 DecorView 的方法
protected DecorView generateDecor(int featureId) {
    // 实例化 DecorView 对象
    return new DecorView(getContext(), featureId, this, getAttributes());
}

// 生成内容视图父容器的方法
protected ViewGroup generateLayout(DecorView decor) {
    // 应用窗口特征
    TypedArray a = getWindowStyle();
    try {
        mIsFloating = a.getBoolean(com.android.internal.R.styleable.Window_windowIsFloating, false);
        int flagsToUpdate = (FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR)
                & (~getForcedWindowFlags());
        if (mIsFloating) {
            setLayout(WRAP_CONTENT, WRAP_CONTENT);
            setFlags(0, flagsToUpdate);
        } else {
            setFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR, flagsToUpdate);
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowNoTitle, false)) {
            requestFeature(FEATURE_NO_TITLE);
        } else if (a.getBoolean(com.android.internal.R.styleable.Window_windowActionBar, false)) {
            // Don't allow an action bar if there is no title.
            requestFeature(FEATURE_ACTION_BAR);
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowActionBarOverlay, false)) {
            requestFeature(FEATURE_ACTION_BAR_OVERLAY);
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowActionModeOverlay, false)) {
            requestFeature(FEATURE_ACTION_MODE_OVERLAY);
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowTranslucentStatus, false)) {
            requestFeature(FEATURE_TRANSLUCENT_STATUS);
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowTranslucentNavigation, false)) {
            requestFeature(FEATURE_TRANSLUCENT_NAVIGATION);
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowLightStatusBar, false)) {
            requestFeature(FEATURE_LIGHT_STATUS_BAR);
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowDrawsSystemBarBackgrounds, false)) {
            requestFeature(FEATURE_DRAWS_SYSTEM_BAR_BACKGROUNDS);
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowContentTransitions, false)) {
            requestFeature(FEATURE_CONTENT_TRANSITIONS);
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowAllowEnterTransitionOverlap, false)) {
            setAllowEnterTransitionOverlap(true);
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowAllowReturnTransitionOverlap, false)) {
            setAllowReturnTransitionOverlap(true);
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowSharedElementsUseOverlay, false)) {
            setSharedElementsUseOverlay(true);
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowActivityTransitions, false)) {
            requestFeature(FEATURE_ACTIVITY_TRANSITIONS);
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowFixedWidthMajor, false)) {
            setFixedSizeMajor(true);
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowFixedWidthMinor, false)) {
            setFixedSizeMinor(true);
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowFixedHeightMajor, false)) {
            setFixedSizeMajor(true);
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowFixedHeightMinor, false)) {
            setFixedSizeMinor(true);
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowOverscanExitAnimation, false)) {
            setOverscanExitAnimationEnabled(true);
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowOverscanEnterAnimation, false)) {
            setOverscanEnterAnimationEnabled(true);
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowDisablePreview, false)) {
            setPreviewEnabled(false);
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowShowWallpaper, false)) {
            requestFeature(FEATURE_SHOW_WALLPAPER);
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowFullscreen, false)) {
            setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN);
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowContentOverlay, false)) {
            setFlags(FLAG_LAYOUT_NO_LIMITS, FLAG_LAYOUT_NO_LIMITS);
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowIsTranslucent, false)) {
            setFlags(FLAG_TRANSLUCENT_STATUS, FLAG_TRANSLUCENT_STATUS);
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowIsFloating, false)) {
            setFlags(FLAG_DIM_BEHIND, FLAG_DIM_BEHIND);
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowCloseOnTouchOutside, false)) {
            setCloseOnTouchOutside(true);
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowShowForAllUsers, false)) {
            setShowForAllUsers(true);
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowNoDisplay, false)) {
            setFlags(FLAG_NOT_VISIBLE, FLAG_NOT_VISIBLE);
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowSoftInputMode, false)) {
            setSoftInputMode(a.getInt(com.android.internal.R.styleable.Window_windowSoftInputMode, SOFT_INPUT_STATE_UNSPECIFIED));
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowUiOptions, false)) {
            setUiOptions(a.getInt(com.android.internal.R.styleable.Window_windowUiOptions, 0));
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowLightNavigationBar, false)) {
            setLightNavigationBar(true);
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowColorMode, false)) {
            setColorMode(a.getInt(com.android.internal.R.styleable.Window_windowColorMode, ColorMode.NO_MODE));
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowAnimationStyle, false)) {
            setWindowAnimations(a.getResourceId(com.android.internal.R.styleable.Window_windowAnimationStyle, 0));
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowAutoFitSystemWindows, false)) {
            setAutoFitSystemWindows(true);
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowDrawsSystemBarBackgrounds, false)) {
            setDrawsSystemBarBackgrounds(true);
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowStatusBarColor, false)) {
            setStatusBarColor(a.getColor(com.android.internal.R.styleable.Window_windowStatusBarColor, 0));
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowNavigationBarColor, false)) {
            setNavigationBarColor(a.getColor(com.android.internal.R.styleable.Window_windowNavigationBarColor, 0));
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowSharedElementEnterTransition, false)) {
            setSharedElementEnterTransition(TransitionInflater.from(getContext()).inflateTransition(
                    a.getResourceId(com.android.internal.R.styleable.Window_windowSharedElementEnterTransition, 0)));
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowSharedElementReturnTransition, false)) {
            setSharedElementReturnTransition(TransitionInflater.from(getContext()).inflateTransition(
                    a.getResourceId(com.android.internal.R.styleable.Window_windowSharedElementReturnTransition, 0)));
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowEnterTransition, false)) {
            setEnterTransition(TransitionInflater.from(getContext()).inflateTransition(
                    a.getResourceId(com.android.internal.R.styleable.Window_windowEnterTransition, 0)));
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowReturnTransition, false)) {
            setReturnTransition(TransitionInflater.from(getContext()).inflateTransition(
                    a.getResourceId(com.android.internal.R.styleable.Window_windowReturnTransition, 0)));
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowExitTransition, false)) {
            setExitTransition(TransitionInflater.from(getContext()).inflateTransition(
                    a.getResourceId(com.android.internal.R.styleable.Window_windowExitTransition, 0)));
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowReenterTransition, false)) {
            setReenterTransition(TransitionInflater.from(getContext()).inflateTransition(
                    a.getResourceId(com.android.internal.R.styleable.Window_windowReenterTransition, 0)));
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowAllowEnterTransitionOverlap, false)) {
            setAllowEnterTransitionOverlap(true);
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowAllowReturnTransitionOverlap, false)) {
            setAllowReturnTransitionOverlap(true);
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowSharedElementsUseOverlay, false)) {
            setSharedElementsUseOverlay(true);
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowActivityTransitions, false)) {
            requestFeature(FEATURE_ACTIVITY_TRANSITIONS);
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowFixedWidthMajor, false)) {
            setFixedSizeMajor(true);
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowFixedWidthMinor, false)) {
            setFixedSizeMinor(true);
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowFixedHeightMajor, false)) {
            setFixedSizeMajor(true);
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowFixedHeightMinor, false)) {
            setFixedSizeMinor(true);
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowOverscanExitAnimation, false)) {
            setOverscanExitAnimationEnabled(true);
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowOverscanEnterAnimation, false)) {
            setOverscanEnterAnimationEnabled(true);
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowDisablePreview, false)) {
            setPreviewEnabled(false);
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowShowWallpaper, false)) {
            requestFeature(FEATURE_SHOW_WALLPAPER);
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowFullscreen, false)) {
            setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN);
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowContentOverlay, false)) {
            setFlags(FLAG_LAYOUT_NO_LIMITS, FLAG_LAYOUT_NO_LIMITS);
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowIsTranslucent, false)) {
            setFlags(FLAG_TRANSLUCENT_STATUS, FLAG_TRANSLUCENT_STATUS);
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowIsFloating, false)) {
            setFlags(FLAG_DIM_BEHIND, FLAG_DIM_BEHIND);
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowCloseOnTouchOutside, false)) {
            setCloseOnTouchOutside(true);
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowShowForAllUsers, false)) {
            setShowForAllUsers(true);
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowNoDisplay, false)) {
            setFlags(FLAG_NOT_VISIBLE, FLAG_NOT_VISIBLE);
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowSoftInputMode, false)) {
            setSoftInputMode(a.getInt(com.android.internal.R.styleable.Window_windowSoftInputMode, SOFT_INPUT_STATE_UNSPECIFIED));
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowUiOptions, false)) {
            setUiOptions(a.getInt(com.android.internal.R.styleable.Window_windowUiOptions, 0));
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowLightNavigationBar, false)) {
            setLightNavigationBar(true);
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowColorMode, false)) {
            setColorMode(a.getInt(com.android.internal.R.styleable.Window_windowColorMode, ColorMode.NO_MODE));
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowAnimationStyle, false)) {
            setWindowAnimations(a.getResourceId(com.android.internal.R.styleable.Window_windowAnimationStyle, 0));
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowAutoFitSystemWindows, false)) {
            setAutoFitSystemWindows(true);
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowDrawsSystemBarBackgrounds, false)) {
            setDrawsSystemBarBackgrounds(true);
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowStatusBarColor, false)) {
            setStatusBarColor(a.getColor(com.android.internal.R.styleable.Window_windowStatusBarColor, 0));
        }
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowNavigationBarColor, false)) {
            setNavigationBarColor(a.getColor(com.android.internal.R.styleable.Window_windowNavigationBarColor, 0));
        }
        // 根据窗口特征选择合适的布局资源
        int layoutResource;
        int features = getLocalFeatures();
        if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
            layoutResource = R.layout.screen_swipe_dismiss;
        } else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
            if (mIsFloating) {
                TypedValue res = new TypedValue();
                getContext().getTheme().resolveAttribute(
                        R.attr.dialogTitleIconsDecorLayout, res, true);
                layoutResource = res.resourceId;
            } else {
                layoutResource = R.layout.screen_title_icons;
            }
        } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0 &&
                (features & (1 << FEATURE_ACTION_BAR)) == 0) {
            // Special case for a window with only a progress bar (and title).
            // XXX Need to have a no-title version of embedded windows.
            layoutResource = R.layout.screen_progress;
        } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
            // Special case for a window with a custom title.
            // If the window is floating, we need a dialog layout
            if (mIsFloating) {
                TypedValue res = new TypedValue();
                getContext().getTheme().resolveAttribute(
                        R.attr.dialogCustomTitleDecorLayout, res, true);
                layoutResource = res.resourceId;
            } else {
                layoutResource = R.layout.screen_custom_title;
            }
        } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
            // If no other features and not embedded, only need a title.
            // If the window is floating, we need a dialog layout
            if (mIsFloating) {
                TypedValue res = new TypedValue();
                getContext().getTheme().resolveAttribute(
                        R.attr.dialogTitleDecorLayout, res, true);
                layoutResource = res.resourceId;
            } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
                layoutResource = a.getResourceId(
                        com.android.internal.R.styleable.Window_windowActionBarFullscreenDecorLayout,
                        R.layout.screen_action_bar);
            } else {
                layoutResource = R.layout.screen_title;
            }
        } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
            layoutResource = R.layout.screen_simple_overlay_action_mode;
        } else {
            // Embedded, so no decoration is needed.
            layoutResource = R.layout.screen_simple;
        }
        mDecor.startChanging();
        // 加载布局资源到 DecorView 中
        mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
        // 获取内容视图的父容器
        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
        if (contentParent == null) {
            throw new RuntimeException("Window couldn

在上述代码中,setContentView 方法是 Activity 中设置界面布局的关键入口。当调用该方法时,如果 mContentParent 为空,就会执行 installDecor 方法。

installDecor 方法负责创建 DecorView 并进行一系列初始化操作。通过 generateDecor 方法创建 DecorView 实例,该实例根据传入的 featureId 等参数进行初始化,比如设置焦点相关属性以及是否为根命名空间等。接着,通过 generateLayout 方法生成内容视图的父容器 mContentParent,这个过程中会根据窗口的各种特征,如是否为浮动窗口、是否有标题栏、是否有 ActionBar 等,从资源文件中选择合适的布局资源,并加载到 DecorView 中。例如:

java

java 复制代码
// 根据窗口特征选择合适的布局资源
int layoutResource;
int features = getLocalFeatures();
if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
    layoutResource = R.layout.screen_swipe_dismiss;
} else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
    if (mIsFloating) {
        TypedValue res = new TypedValue();
        getContext().getTheme().resolveAttribute(
                R.attr.dialogTitleIconsDecorLayout, res, true);
        layoutResource = res.resourceId;
    } else {
        layoutResource = R.layout.screen_title_icons;
    }
} 
// 后续还有大量根据不同窗口特征选择布局资源的代码逻辑

在上述代码片段中,通过检查 features 中不同的标志位,来判断窗口具有哪些特征,从而选择对应的布局资源。如果窗口具有 FEATURE_SWIPE_TO_DISMISS 特征,就选择 R.layout.screen_swipe_dismiss 作为布局资源;如果有左右图标相关特征且为浮动窗口,通过从主题中解析特定属性来获取布局资源,否则选择另一种布局。

当所有初始化工作完成后,会将 DecorView 添加到 WindowManager 中,通过 mWindowManager.addView(mDecor, getAttributes()); 这一关键代码实现。这里的 getAttributes() 方法会返回 WindowManager.LayoutParams 对象,该对象包含了窗口的各种属性,如窗口的位置、大小、类型等,WindowManager 根据这些属性来正确地将 DecorView 显示在屏幕上。

四、Activity 窗口的显示过程

4.1 Activity 窗口显示前的准备工作

在 Activity 窗口真正显示之前,需要进行一系列准备工作,其中设置窗口属性与参数以及关联 WindowManager 与窗口是重要环节。

PhoneWindow 类中,设置窗口属性与参数的操作贯穿于多个方法中。例如在 setContentView 方法调用 installDecor 后,generateLayout 方法中除了选择布局资源,还会设置窗口的一些基本属性,如窗口的大小模式、是否全屏、是否有状态栏等:

java

java 复制代码
// 根据窗口特征设置窗口属性
if (mIsFloating) {
    setLayout(WRAP_CONTENT, WRAP_CONTENT);
    setFlags(0, flagsToUpdate);
} else {
    setFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR, flagsToUpdate);
}
if (a.getBoolean(com.android.internal.R.styleable.Window_windowNoTitle, false)) {
    requestFeature(FEATURE_NO_TITLE);
} else if (a.getBoolean(com.android.internal.R.styleable.Window_windowActionBar, false)) {
    requestFeature(FEATURE_ACTION_BAR);
}
// 后续还有大量根据窗口样式属性设置窗口特征的代码

上述代码中,如果窗口是浮动窗口(mIsFloatingtrue),会将窗口大小设置为包裹内容,并清除特定的标志位;否则设置窗口布局相关的标志位。同时,根据从样式资源中获取的属性值,决定是否请求无标题栏(FEATURE_NO_TITLE)或 ActionBar(FEATURE_ACTION_BAR)等窗口特征。

而关联 WindowManager 与窗口主要在 Activityattach 方法中完成:

java

java 复制代码
// Activity.java
// Activity 的 attach 方法部分代码
mWindow.setWindowManager(
        (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
        mToken, mComponentName,
        (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
if (mParent != null) {
    mWindow.setContainer(mParent.getWindow());
}
mWindowManager = mWindow.getWindowManager();

这里通过 mWindow.setWindowManager 方法,将从 Context 中获取的 WindowManager 实例与当前 ActivityWindow 进行关联,并传入 Activity 的相关标识和硬件加速标志等参数。如果当前 Activity 有父 Activity,还会设置窗口的容器关系。最后,将 WindowWindowManager 赋值给 ActivitymWindowManager 成员变量,以便后续通过 Activity 来操作窗口相关事宜。

4.2 WindowManager 添加窗口到显示列表

WindowManager 并不是直接管理窗口,而是通过 WindowManagerGlobal 来实现具体的窗口管理操作。WindowManagerGlobal 维护着一个窗口的显示列表,负责向 WindowManagerService(WMS)添加、移除和更新窗口。

WindowManagerImpl 类中,addView 方法会将请求转发给 WindowManagerGlobal

java

java 复制代码
// WindowManagerImpl.java
// WindowManager 的 addView 方法
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
    applyDefaultToken(params);
    mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}

上述代码中,mGlobalWindowManagerGlobal 的实例,通过调用其 addView 方法,将 View(实际上是 DecorView)、布局参数、显示对象以及父窗口等信息传递过去。

下面来看 WindowManagerGlobaladdView 方法:

java

java 复制代码
// WindowManagerGlobal.java
// WindowManagerGlobal 的 addView 方法
public void addView(View view, ViewGroup.LayoutParams params,
        Display display, Window parentWindow) {
    if (view == null) {
        throw new IllegalArgumentException("view must not be null");
    }
    if (display == null) {
        throw new IllegalArgumentException("display must not be null");
    }
    if (!(params instanceof WindowManager.LayoutParams)) {
        throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
    }
    final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
    if (parentWindow != null) {
        parentWindow.adjustLayoutParamsForSubWindow(wparams);
    } else {
        // If there's no parent, then hardware acceleration for this view is
        // set from the application's hardware acceleration setting.
        final Context context = view.getContext();
        if (context != null
                && (context.getApplicationInfo().flags
                        & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
            wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
        }
    }
    ViewRootImpl root;
    View panelParentView = null;
    synchronized (mLock) {
        // 检查窗口索引是否越界
        if (mViews.size() >= MAX_VIEWS) {
            throw new RuntimeException("Too many views added to the window manager.");
        }
        // 创建 ViewRootImpl 对象
        root = new ViewRootImpl(view.getContext(), display);
        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 {
            // 通过 ViewRootImpl 将窗口添加到 WMS
            root.setView(view, wparams, panelParentView);
        } catch (RuntimeException e) {
            // BadTokenException or InvalidDisplayException, clean up.
            if (index >= 0) {
                removeViewLocked(index, true);
            }
            throw e;
        }
    }
}

在这个方法中,首先对传入的参数进行一系列校验,确保 ViewDisplay 和参数类型的正确性。如果有父窗口,会调整子窗口的布局参数。接着创建 ViewRootImpl 对象,它是连接 ViewWindowManagerService 的桥梁。将 ViewViewRootImpl 和布局参数分别添加到 mViewsmRootsmParams 列表中。最后通过 root.setView(view, wparams, panelParentView) 方法,将窗口添加到 WindowManagerService 中。

4.3 WMS 对 Activity 窗口的处理与显示

WindowManagerService(WMS)是 Android 系统中负责窗口管理的核心服务,它接收来自 WindowManagerGlobal 的窗口添加请求,并对窗口进行处理和显示。

ViewRootImplsetView 方法会通过 IPC(进程间通信)向 WMS 发送添加窗口的请求:

java

java 复制代码
// ViewRootImpl.java
// 设置 View 的方法
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    synchronized (this) {
        if (mView == null) {
            mView = view;
            // 省略部分代码
            requestLayout();
            try {
                // 通过 WindowSession 将窗口添加到 WMS
                res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                        getHostVisibility(), mDisplay.getDisplayId(),
                        mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                        mAttachInfo.mOutsets, mInputChannel);
            } catch (RemoteException e) {
                mAdded = false;
                mView = null;
                mAttachInfo.mRootView = null;
                mInputChannel = null;
                setAccessibilityFocus(null, null);
                throw new RuntimeException("Adding window failed", e);
            }
            // 省略部分代码
        }
    }
}

这里的 mWindowSession 是通过 WindowManagerGlobal 获取的与 WMS 进行通信的接口。通过调用 mWindowSession.addToDisplay 方法,将窗口相关信息发送给 WMS

WindowManagerService 中,addToDisplay 方法接收请求并进行处理:

java

java 复制代码
// WindowManagerService.java
// 添加窗口到显示的方法
@Override
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
        int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
        Rect outOutsets, InputChannel outInputChannel) {
    // 检查窗口类型和权限
    if (window == null) {
        throw new IllegalArgumentException("window must not be null");
    }
    if (attrs == null) {
        throw new IllegalArgumentException("attrs must not be null");
    }
    final WindowState win = new WindowState(this, window, attrs, viewVisibility, displayId);
    // 省略部分代码
    synchronized (mWindowMap) {
        // 将窗口添加到窗口映射表中
        mWindowMap.put(win.mSession.mRemote, win);
        // 处理窗口的布局、绘制等操作
        win.attach();
        // 省略部分代码
        try {
            // 执行窗口的布局操作
            performLayoutAndPlaceSurfacesLocked();
        } catch (Exception e) {
            Slog.e(TAG, "Exception thrown during performLayoutAndPlaceSurfacesLocked", e);
        }
        // 省略部分代码
    }
    return WindowManagerGlobal.ADD_OKAY;
}

addToDisplay 方法中,首先创建 WindowState 对象来表示要添加的窗口,该对象包含了窗口的各种状态信息。然后将窗口添加到 mWindowMap 中,mWindowMap 用于维护所有已添加窗口的信息。接着调用 win.attach() 方法进行窗口的附加操作,之后通过 performLayoutAndPlaceSurfacesLocked 方法执行窗口的布局和表面放置操作。

performLayoutAndPlaceSurfacesLocked 方法中涉及复杂的布局计算和窗口层级排序等操作:

java

java 复制代码
// WindowManagerService.java
// 执行布局和放置表面的方法
private void performLayoutAndPlaceSurfacesLocked() {
    // 遍历所有窗口进行布局计算
    for (int displayNdx = mDisplayContents.size() - 1; displayNdx >= 0; --displayNdx) {
        final DisplayContent displayContent = mDisplayContents.valueAt(displayNdx);
        final ArrayList<WindowState> windows = displayContent.getWindowList();
        for (int i = windows.size() - 1; i >= 0; --i) {
            final WindowState win = windows.get(i);
            if (win.mVisible) {
                // 计算窗口的布局参数
                computeFrameLocked(win);
            }
        }
    }
    // 对窗口进行层级排序
    for (int displayNdx = mDisplayContents.size() - 1; displayNdx >= 0; --displayNdx) {
        final DisplayContent displayContent = mDisplayContents.valueAt(displayNdx);
        displayContent.sortWindows();
    }
    // 省略部分代码,进行窗口表面的放置等操作
}

上述代码中,首先遍历所有的显示内容(DisplayContent),对于每个显示内容中的窗口列表,对可见窗口执行 computeFrameLocked 方法来计算窗口的布局参数,比如窗口的位置、大小等。之后对每个显示内容中的窗口进行层级排序,确保窗口按照正确的层级关系进行显示。完成布局计算和层级排序后,会进行窗口表面的放置等操作,最终将窗口显示在屏幕上。整个过程涉及众多细节和复杂的逻辑,以保证窗口能够正确、高效地显示在屏幕的合适位置。

五、Activity 窗口的生命周期与状态管理

5.1 Activity 窗口生命周期与 Activity 生命周期的关联

Activity 的生命周期与窗口的生命周期紧密相连,二者相互影响、协同工作。Activity 的生命周期方法,如 onCreateonResumeonPauseonDestroy 等,在很大程度上决定了窗口的状态变化;而窗口的一些关键状态,如是否可见、是否获得焦点等,也会反过来影响 Activity 的行为。

在 Activity 的 onCreate 方法中,会创建并初始化窗口相关资源,这在前面窗口创建过程中已有体现。而当 Activity 进入 onResume 阶段时,窗口也进入一个关键的显示与交互准备状态。

以下是 ActivityThreadhandleResumeActivity 方法与窗口显示相关的部分源码:

java

java 复制代码
// ActivityThread.java
// 处理恢复 Activity 的方法
final void handleResumeActivity(IBinder token,
        boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
    ActivityClientRecord r = mActivities.get(token);
    // 省略部分代码
    if (r != null) {
        if (r.window == null &&!a.mFinished && willBeVisible) {
            r.window = r.activity.getWindow();
            View decor = r.window.getDecorView();
            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 (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();
                }
            }
            if (a.mVisibleFromClient) {
                if (!a.mWindowAdded) {
                    a.mWindowAdded = true;
                    // 将窗口添加到 WindowManager 进行显示
                    wm.addView(decor, l);
                } else {
                    // The activity will get a callback for this {@link LayoutParams} change
                    a.onWindowAttributesChanged(l);
                }
            }
        } else if (!willBeVisible) {
            if (localLOGV) Slog.v(
                TAG, "Launch " + r + " mStartedActivity set");
            r.hideForNow = true;
        }
        // 省略部分代码
    }
}

handleResumeActivity 方法中,首先获取与 token 对应的 ActivityClientRecord 对象 r。如果 r.window 为空且 Activity 未完成并且即将可见,会获取 Activity 的 Window 及其 DecorView,设置 DecorView 初始不可见,然后获取 WindowManager 和窗口属性 LayoutParams。若 Activity 是首次添加窗口(!a.mWindowAdded),则通过 wm.addView(decor, l)DecorView 添加到 WindowManager 中进行显示,从而让窗口出现在屏幕上,进入可见状态;若窗口已添加过,则调用 a.onWindowAttributesChanged(l) 通知 Activity 窗口属性发生了变化。

当 Activity 执行 onPause 方法时,窗口的状态也会相应地发生改变。在 ActivityThreadhandlePauseActivity 方法中,会对窗口进行一些处理,使其进入暂停状态。以下是部分源码:

java

java 复制代码
// ActivityThread.java
// 处理暂停 Activity 的方法
private void handlePauseActivity(IBinder token, boolean finished,
        boolean userLeaving, int configChanges, PendingTransactionActions pendingActions,
        String reason) {
    ActivityClientRecord r = mActivities.get(token);
    if (r != null) {
        // 省略部分代码
        if (r.activity.mVisibleFromClient) {
            if (r.window != null) {
                // 隐藏窗口的 DecorView
                r.window.getDecorView().setVisibility(View.INVISIBLE);
            }
        }
        // 调用 Activity 的 onPause 方法
        performPauseActivityIfNeeded(r, reason);
        // 省略部分代码
    }
}

handlePauseActivity 方法中,首先获取对应的 ActivityClientRecord 对象 r。如果 Activity 是可见的(r.activity.mVisibleFromClienttrue),并且窗口存在(r.window != null),会将窗口的 DecorView 设置为不可见(View.INVISIBLE),这意味着窗口在视觉上不再显示给用户。然后调用 performPauseActivityIfNeeded 方法来执行 Activity 的 onPause 方法,完成 Activity 的暂停操作。

当 Activity 执行 onDestroy 方法时,窗口会被销毁,相关资源也会被释放。在 ActivityThreadhandleDestroyActivity 方法中,会进行窗口的销毁操作。以下是部分源码:

java

java 复制代码
// ActivityThread.java
// 处理销毁 Activity 的方法
private void handleDestroyActivity(IBinder token, boolean finishing,
        int configChanges, boolean getNonConfigInstance, String reason) {
    ActivityClientRecord r = mActivities.get(token);
    if (r != null) {
        // 省略部分代码
        if (r.window != null) {
            ViewManager wm = r.activity.getWindowManager();
            if (r.decor != null) {
                try {
                    // 从 WindowManager 中移除窗口的 DecorView
                    wm.removeViewImmediate(r.decor);
                } catch (Exception e) {
                    Slog.e(TAG, "Exception when removing view: " + e);
                }
            }
            r.window = null;
            r.decor = null;
        }
        // 调用 Activity 的 onDestroy 方法
        performDestroyActivity(r, finishing, configChanges, getNonConfigInstance, reason);
        // 省略部分代码
    }
}

handleDestroyActivity 方法中,获取对应的 ActivityClientRecord 对象 r。如果窗口存在(r.window != null),会通过 WindowManagerremoveViewImmediate 方法将窗口的 DecorViewWindowManager 中移除,这意味着窗口不再由 WindowManager 管理,其资源也会被逐步释放。然后将 r.windowr.decor 置为 null,表示窗口已被销毁。最后调用 performDestroyActivity 方法来执行 Activity 的 onDestroy 方法,完成 Activity 的销毁操作。

5.2 Activity 窗口状态管理机制

5.2.1 窗口状态的定义与表示

在 Android 系统中,窗口的状态通过一系列的标志位和枚举值来定义和表示。这些状态信息存储在 WindowManager.LayoutParams 类以及 WindowState 类中,用于描述窗口的不同属性和状态。

WindowManager.LayoutParams 类中,有许多标志位用于表示窗口的状态和属性,例如:

java

java 复制代码
// WindowManager.LayoutParams.java
// 窗口标志位示例
public static final int FLAG_FULLSCREEN = 0x00000400; // 全屏标志
public static final int FLAG_NOT_FOCUSABLE = 0x00000008; // 不可获取焦点标志
public static final int FLAG_NOT_TOUCHABLE = 0x00000010; // 不可触摸标志

这些标志位可以通过位运算来组合使用,以表示窗口的复杂状态。例如,如果一个窗口要设置为全屏且不可获取焦点,可以这样设置:

java

java 复制代码
WindowManager.LayoutParams params = window.getAttributes();
params.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
window.setAttributes(params);

WindowState 类中,也有许多状态变量用于表示窗口的各种状态,例如:

java

java 复制代码
// WindowState.java
// 窗口状态变量示例
boolean mVisible; // 窗口是否可见
boolean mFocused; // 窗口是否获得焦点
boolean mAnimating; // 窗口是否正在进行动画

这些状态变量在窗口的生命周期中会不断更新,以反映窗口的当前状态。

5.2.2 状态转换的触发与处理

窗口状态的转换通常由系统事件或用户操作触发,例如 Activity 的生命周期方法调用、用户触摸屏幕等。系统会根据不同的事件和条件,对窗口的状态进行相应的转换和处理。

当 Activity 从 onPause 状态转换到 onResume 状态时,窗口会从不可见状态转换到可见状态。在 ActivityThreadhandleResumeActivity 方法中,会触发窗口状态的转换:

java

java 复制代码
// ActivityThread.java
// 处理恢复 Activity 的方法(部分代码)
if (r.window == null &&!a.mFinished && willBeVisible) {
    r.window = r.activity.getWindow();
    View decor = r.window.getDecorView();
    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 (r.mPreserveWindow) {
        a.mWindowAdded = true;
        r.mPreserveWindow = false;
        ViewRootImpl impl = decor.getViewRootImpl();
        if (impl != null) {
            impl.notifyChildRebuilt();
        }
    }
    if (a.mVisibleFromClient) {
        if (!a.mWindowAdded) {
            a.mWindowAdded = true;
            // 将窗口添加到 WindowManager 进行显示
            wm.addView(decor, l);
            // 设置窗口可见
            decor.setVisibility(View.VISIBLE);
        } else {
            a.onWindowAttributesChanged(l);
        }
    }
}

在上述代码中,当 Activity 恢复时,如果窗口即将可见且尚未添加到 WindowManager 中,会调用 wm.addView(decor, l) 将窗口添加到 WindowManager 中,然后将 DecorView 的可见性设置为 View.VISIBLE,从而实现窗口从不可见状态到可见状态的转换。

当用户触摸屏幕时,窗口的焦点状态可能会发生变化。在 WindowManagerService 中,会处理触摸事件并更新窗口的焦点状态:

java

java 复制代码
// WindowManagerService.java
// 处理触摸事件并更新焦点状态的方法示例
private boolean handleTouchEvent(MotionEvent event, int displayId) {
    // 省略部分代码
    WindowState focusedWindow = getFocusedWindowLocked();
    if (focusedWindow != null) {
        // 检查触摸事件是否在焦点窗口内
        if (focusedWindow.isTouchInside(event)) {
            // 焦点窗口保持焦点
            return true;
        } else {
            // 查找新的焦点窗口
            WindowState newFocusedWindow = findNewFocusedWindowLocked(event);
            if (newFocusedWindow != null) {
                // 切换焦点到新的窗口
                setFocusedWindowLocked(newFocusedWindow, "touch event");
                return true;
            }
        }
    }
    // 省略部分代码
    return false;
}

handleTouchEvent 方法中,首先获取当前的焦点窗口 focusedWindow。如果触摸事件发生在焦点窗口内,焦点窗口保持焦点;否则,通过 findNewFocusedWindowLocked 方法查找新的焦点窗口,如果找到新的焦点窗口,会调用 setFocusedWindowLocked 方法将焦点切换到新的窗口,从而实现窗口焦点状态的转换。

六、Activity 窗口的事件处理

6.1 窗口事件的接收与分发机制

6.1.1 事件从系统到窗口的传递

在 Android 系统中,事件(如触摸事件、按键事件等)首先由系统底层的输入子系统捕获,然后通过 IPC(进程间通信)机制传递到 WindowManagerService(WMS)。WMS 作为窗口管理的核心服务,负责将事件分发给相应的窗口。

输入子系统通过 InputReaderInputDispatcher 来处理输入事件。InputReader 负责从硬件设备(如触摸屏、键盘等)读取原始输入数据,并将其转换为 InputEvent 对象。InputDispatcher 则负责将 InputEvent 对象分发到合适的应用程序窗口。

以下是 InputDispatcher 分发事件的部分源码:

java

java 复制代码
// InputDispatcher.java
// 分发输入事件的方法
void dispatchInputEvent(int seq, InputEvent event, int displayId) {
    // 省略部分代码
    InputTarget inputTarget;
    // 查找合适的输入目标窗口
    inputTarget = findFocusedWindowTargetsLocked(currentTime,
            event, displayId, isPointerEvent, outInputTargets);
    if (inputTarget == null) {
        // 没有找到合适的目标窗口,丢弃事件
        handleTargetsNotReadyLocked(currentTime,
                event, displayId, isPointerEvent,
                false, false, false, false);
        return;
    }
    // 分发事件到目标窗口
    dispatchEventLocked(currentTime, seq, event, inputTarget);
    // 省略部分代码
}

dispatchInputEvent 方法中,首先通过 findFocusedWindowTargetsLocked 方法查找合适的输入目标窗口。如果找到目标窗口,会调用 dispatchEventLocked 方法将事件分发到目标窗口;如果没有找到合适的目标窗口,会调用 handleTargetsNotReadyLocked 方法处理事件未就绪的情况。

WMS 接收到 InputDispatcher 分发的事件后,会根据窗口的层级和焦点状态等信息,将事件进一步分发给具体的窗口。在 WindowManagerService 中,有相应的方法来处理事件的分发:

java

java 复制代码
// WindowManagerService.java
// 处理输入事件的方法
void handleInputEvent(InputEvent event, int displayId) {
    // 省略部分代码
    WindowState focusedWindow = getFocusedWindowLocked();
    if (focusedWindow != null) {
        // 将事件分发给焦点窗口
        focusedWindow.injectInputEvent(event);
    }
    // 省略部分代码
}

handleInputEvent 方法中,首先获取当前的焦点窗口 focusedWindow,如果焦点窗口存在,会调用 focusedWindow.injectInputEvent(event) 方法将事件注入到焦点窗口中。

6.1.2 Window 的事件分发逻辑

当窗口接收到事件后,Window 对象会将事件分发给其根视图 DecorView,然后由 DecorView 开始进行事件的分发。DecorView 继承自 FrameLayout,它会按照视图树的结构,将事件分发给子视图。

DecorView 中,dispatchTouchEvent 方法用于处理触摸事件的分发:

java

java 复制代码
// DecorView.java
// 分发触摸事件的方法
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    final Window.Callback cb = mWindow.getCallback();
    return cb != null &&!mWindow.isDestroyed() && mFeatureId < 0
            ? cb.dispatchTouchEvent(ev)
            : super.dispatchTouchEvent(ev);
}

dispatchTouchEvent 方法中,首先获取 Window 的回调对象 cb。如果回调对象存在且窗口未被销毁,会调用回调对象的 dispatchTouchEvent 方法来处理事件;否则,会调用父类的 dispatchTouchEvent 方法进行默认的事件分发。

Window 的回调对象通常是 Activity,因为 Activity 实现了 Window.Callback 接口。在 Activity 中,dispatchTouchEvent 方法会进一步处理事件:

java

java 复制代码
// Activity.java
// 分发触摸事件的方法
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
        // 处理按下事件
        onUserInteraction();
    }
    if (getWindow().superDispatchTouchEvent(ev)) {
        // 如果窗口的 superDispatchTouchEvent 方法处理了事件,返回 true
        return true;
    }
    // 否则,调用 Activity 的 onTouchEvent 方法处理事件
    return onTouchEvent(ev);
}

ActivitydispatchTouchEvent 方法中,当接收到按下事件(MotionEvent.ACTION_DOWN)时,会调用 onUserInteraction 方法,表示用户与应用进行了交互。然后调用 getWindow().superDispatchTouchEvent(ev) 方法将事件传递给窗口进行处理,如果窗口处理了事件,返回 true;否则,调用 onTouchEvent 方法处理事件。

6.2 Activity 对窗口事件的处理

6.2.1 Activity 的事件回调方法

Activity 提供了一系列的事件回调方法,用于处理窗口事件。这些方法包括 dispatchTouchEventonTouchEventdispatchKeyEventonKeyDownonKeyUp 等。

dispatchTouchEvent 方法用于分发触摸事件,前面已经详细介绍过。onTouchEvent 方法用于处理触摸事件,当 dispatchTouchEvent 方法没有将事件处理掉时,会调用 onTouchEvent 方法:

java

java 复制代码
// Activity.java
// 处理触摸事件的方法
@Override
public boolean onTouchEvent(MotionEvent event) {
    if (mWindow.shouldCloseOnTouch(this, event)) {
        // 如果窗口应该在触摸时关闭,调用 finish 方法关闭 Activity
        finish();
        return true;
    }
    return false;
}

onTouchEvent 方法中,如果窗口满足在触摸时关闭的条件(通过 mWindow.shouldCloseOnTouch 方法判断),会调用 finish 方法关闭 Activity,并返回 true 表示事件已处理;否则,返回 false 表示事件未处理。

dispatchKeyEvent 方法用于分发按键事件:

java

java 复制代码
// Activity.java
// 分发按键事件的方法
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
    final int keyCode = event.getKeyCode();
    final int action = event.getAction();
    final boolean isDown = action == KeyEvent.ACTION_DOWN;
    if (isDown) {
        // 处理按下按键事件
        onUserInteraction();
    }
    if (getWindow().superDispatchKeyEvent(event)) {
        // 如果窗口的 superDispatchKeyEvent 方法处理了事件,返回 true
        return true;
    }
    // 否则,调用 Activity 的 onKeyDown 或 onKeyUp 方法处理事件
    KeyEvent.DispatcherState dispatcherState = mKeyDispatcherState;
    if (dispatcherState == null) {
        dispatcherState = new KeyEvent.DispatcherState();
        mKeyDispatcherState = dispatcherState;
    }
    boolean handled = false;
    if (isDown) {
        handled = dispatcherState.dispatchKeyEvent(event, mHandler, null, this);
    } else {
        handled = dispatcherState.dispatchKeyEvent(event, mHandler, null, this);
    }
    return handled;
}

dispatchKeyEvent 方法中,当接收到按下按键事件时,会调用 onUserInteraction 方法。然后调用 getWindow().superDispatchKeyEvent(event) 方法将事件传递给窗口进行处理,如果窗口处理了事件,返回 true;否则,通过 KeyEvent.DispatcherState 对象的 dispatchKeyEvent 方法调用 ActivityonKeyDownonKeyUp 方法处理事件。

6.2.2 处理逻辑与源码分析

Activity 的事件处理逻辑主要围绕着这些事件回调方法展开。在实际应用中,开发者可以重写这些方法来实现自定义的事件处理逻辑。

例如,重写 onKeyDown 方法来处理按键按下事件:

java

java 复制代码
// 自定义 Activity 类
public class MyActivity extends Activity {
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            // 处理返回键按下事件
            Toast.makeText(this, "返回键被按下", Toast.LENGTH_SHORT).show();
            return true;
        }
        return super.onKeyDown(keyCode, event);
    }
}

在上述代码中,重写了 onKeyDown 方法,当检测到按下的是返回键(KeyEvent.KEYCODE_BACK)时,会显示一个 Toast 提示信息,并返回 true 表示事件已处理;否则,调用父类的 onKeyDown 方法进行默认处理。

通过重写这些事件回调方法,开发者可以根据应用的需求,灵活地处理窗口事件,实现各种交互效果。

七、Activity 窗口管理的高级特性

7.1 多窗口模式下的 Activity 窗口管理

7.1.1 多窗口模式的支持与实现

从 Android 7.0(API 级别 24)开始,Android 系统引入了多窗口模式,允许用户同时在屏幕上显示多个应用窗口。多窗口模式主要有分屏模式和自由窗口模式两种。

在系统层面,WindowManagerService(WMS)对多窗口模式提供了支持。WMS 会根据窗口的属性和用户的操作,对多个窗口进行布局和管理。当用户进入多窗口模式时,WMS 会调整窗口的大小和位置,确保多个窗口能够合理地显示在屏幕上。

在应用层面,Activity 需要进行相应的配置才能支持多窗口模式。在 AndroidManifest.xml 中,可以通过设置 android:resizeableActivity 属性来指定 Activity 是否支持多窗口模式:

xml

java 复制代码
<activity
    android:name=".MainActivity"
    android:resizeableActivity="true">
    <!-- 其他配置 -->
</activity>

android:resizeableActivity 设置为 true 时,该 Activity 支持多窗口模式;当设置为 false 时,不支持多窗口模式。

7.1.2 相关源码分析与关键逻辑

WindowManagerService 中,有一系列方法用于处理多窗口模式下的窗口布局和管理。例如,performLayoutAndPlaceSurfacesLocked 方法会在多窗口模式下对窗口进行重新布局:

java

java 复制代码
// WindowManagerService.java
// 执行布局和放置表面的方法
private void performLayoutAndPlaceSurfacesLocked() {
    // 省略部分代码
    for (int displayNdx = mDisplayContents.size() - 1; displayNdx >= 0; --displayNdx) {
        final DisplayContent displayContent = mDisplayContents.valueAt(displayNdx);
        final ArrayList<WindowState> windows = displayContent.getWindowList();
        for (int i = windows.size() - 1; i >= 0; --i) {
            final WindowState win = windows.get(i);
            if (win.mVisible) {
                // 计算窗口的布局参数
                computeFrameLocked(win);
            }
        }
        // 在多窗口模式下,对窗口进行特殊的布局处理
        if (displayContent.isInMultiWindowMode()) {
            layoutMultiWindowModeWindows(displayContent);
        }
    }
    // 省略部分代码
}

performLayoutAndPlaceSurfacesLocked 方法中,首先遍历所有的显示内容(DisplayContent),对每个显示内容中的窗口列表进行布局计算。如果显示内容处于多窗口模式(通过 isInMultiWindowMode 方法判断),会调用 layoutMultiWindowModeWindows 方法对窗口进行特殊的布局处理。

layoutMultiWindowModeWindows 方法会根据窗口的属性和当前的布局规则,调整窗口的大小和位置:

java

java 复制代码
// WindowManagerService.java
// 处理多窗口模式下窗口布局的方法
private void layoutMultiWindowModeWindows(DisplayContent displayContent) {
    // 省略部分代码
    final ArrayList<WindowState> windows = displayContent.getWindowList();
    for (int i = windows.size() - 1; i >= 0; --i) {
        final WindowState win = windows.get(i);
        if (win.isInMultiWindowMode()) {
            // 调整窗口的大小和位置
            adjustWindowSizeAndPosition(win);
        }
    }
    // 省略部分代码
}

layoutMultiWindowModeWindows 方法中,遍历显示内容中的窗口列表,对于处于多窗口模式的窗口(通过 isInMultiWindowMode 方法判断),会调用 adjustWindowSizeAndPosition 方法调整窗口的大小和位置。

7.2 窗口动画与过渡效果的实现

7.2.1 窗口动画的设置与启动

在 Android 中,可以为窗口设置各种动画和过渡效果,以增强用户体验。窗口动画可以通过 XML 文件或代码来定义和设置。

通过 XML 文件定义窗口动画的示例:

xml

java 复制代码
<!-- res/anim/slide_in_left.xml -->
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:fromXDelta="-100%p"
        android:toXDelta="0"
        android:duration="300" />
    <alpha
        android:fromAlpha="0.0"
        android:toAlpha="1.0"
        android:duration="300" />
</set>

上述 XML 文件定义了一个从左侧滑入并渐显的动画效果。

在代码中设置窗口动画的示例:

java

java 复制代码
// 在 Activity 中设置窗口动画
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    // 设置窗口进入动画
    getWindow().setWindowAnimations(R.style.WindowAnimation);
}

ActivityonCreate 方法中,通过 getWindow().setWindowAnimations 方法设置窗口的动画样式。R.style.WindowAnimation 是一个样式资源,它引用了定义好的动画 XML 文件。

7.2.2 动画实现的源码解析

窗口动画的实现主要涉及到 Animation 类和 Animator 类。Animation 类是 Android 早期的动画实现方式,而 Animator 类是 Android 3.0(API 级别 11)引入的新的动画框架,提供了更强大和灵活的动画功能。

Window 类中,setWindowAnimations 方法会将动画样式应用到窗口上:

java

java 复制代码
// Window.java
// 设置窗口动画的方法
public void setWindowAnimations(int resId) {
    mWindowAttributes.windowAnimations = resId;
    if (mCallback != null) {
        mCallback.onWindowAttributesChanged(mWindowAttributes);
    }
}

setWindowAnimations 方法中,将动画样式资源 ID 赋值给 mWindowAttributes.windowAnimations,然后调用 mCallback.onWindowAttributesChanged 方法通知窗口属性发生了变化。

当窗口显示或隐藏时,会根据设置的动画样式启动相应的动画。在 ViewRootImpl 类中,会处理窗口动画的启动:

java

java 复制代码
// ViewRootImpl.java
// 处理窗口显示或隐藏动画的方法
private void performAnimation() {
    // 省略部分代码
    if (mView.getAnimation() != null) {
        // 启动 View 的动画
        mView.startAnimation(mView.getAnimation());
    } else if (mWindowAttributes.windowAnimations != 0) {
        // 根据窗口动画样式启动动画
        Animation anim = AnimationUtils.loadAnimation(mContext, mWindowAttributes.windowAnimations);
        if (anim != null) {
            mView.startAnimation(anim);
        }
    }
    // 省略部分代码
}

performAnimation 方法中,如果 View 已经设置了动画,会直接启动该动画;否则,根据窗口动画样式资源 ID 加载动画,并启动动画。

对于 Animator 类实现的动画,通常会使用 ObjectAnimatorAnimatorSet 等类来创建和管理动画。例如,使用 ObjectAnimator 创建一个简单的缩放动画:

java

java 复制代码
// 创建一个缩放动画
ObjectAnimator scaleXAnimator = ObjectAnimator.ofFloat(view, "scaleX", 1.0f, 2.0f);
scaleXAnimator.setDuration(300);
scaleXAnimator.start();

上述代码创建了一个 ObjectAnimator 对象,用于对 viewscaleX 属性进行缩放动画,从 1.0 放大到 2.0,动画时长为 300 毫秒,然后启动动画。

八、总结与展望

8.1 本文总结

通过对 Android Activity 窗口管理模块的深入分析,我们详细了解了该模块在 Android 系统中的重要地位和作用。从窗口的创建过程来看,Activity 启动时会触发一系列复杂的操作,包括 PhoneWindow 的创建与初始化、DecorView 的生成与添加等,这些操作确保了窗口的基本结构和布局能够正确构建。

在窗口的显示过程中,WindowManager 起到了关键的管理作用,它通过 WindowManagerGlobalWindowManagerService(WMS)进行交互,将窗口添加到显示列表中。WMS 作为系统的核心窗口管理服务,负责接收窗口请求、进行布局计算、层级排序以及表面放置等操作,最终将窗口正确地显示在屏幕上。

Activity 窗口的生命周期与 Activity 的生命周期紧密关联,不同的 Activity 生命周期方法会导致窗口状态的相应变化,如可见性、焦点状态等。同时,窗口状态管理机制通过一系列的标志位和状态变量来定义和表示窗口的状态,并根据不同的事件和条件进行状态转换和处理。

在事件处理方面,事件从系统底层的输入子系统捕获后,通过 IPC 机制传递到 WMS,再由 WMS 分发给相应的窗口。窗口的 DecorView 作为事件分发的起点,将事件按照视图树的结构分发给子视图,Activity 则提供了一系列的事件回调方法,开发者可以重写这些方法来实现自定义的事件处理逻辑。

此外,我们还探讨了 Activity 窗口管理的高级特性,如多窗口模式和窗口动画与过渡效果。多窗口模式为用户提供了更便捷的多任务操作体验,WMS 在其中起到了关键的布局和管理作用;窗口动画与过渡效果则通过 Animation 类和 Animator 类等实现,能够增强应用的用户体验。

8.2 未来发展趋势与研究方向

随着 Android 系统的不断发展和更新,Activity 窗口管理模块也将面临新的挑战和机遇。以下是一些可能的未来发展趋势和研究方向:

8.2.1 更高效的窗口管理算法

随着移动设备屏幕分辨率的不断提高和多窗口模式的普及,窗口管理的复杂度也在增加。未来可能会研究和开发更高效的窗口布局算法和资源分配策略,以提高窗口显示的性能和响应速度,减少系统资源的占用。

8.2.2 增强的用户交互体验

为了提供更加流畅和自然的用户交互体验,未来的窗口管理模块可能会引入更多的手势识别、触摸反馈和动画效果。例如,支持更加复杂的手势操作来实现窗口的切换、缩放和拖动等功能,以及为窗口的显示和隐藏添加更加生动的动画过渡效果。

8.2.3 跨设备和多屏幕支持

随着智能设备的多样化,如折叠屏手机、平板电脑和智能电视等,未来的窗口管理模块需要更好地支持跨设备和多屏幕的显示和交互。例如,实现窗口在不同设备和屏幕之间的无缝迁移和共享,以及支持多屏幕之间的协同工作。

8.2.4 安全性和隐私保护

在窗口管理过程中,涉及到用户的隐私信息和敏感数据。未来的研究方向可能会更加注重窗口管理模块的安全性和隐私保护,例如,对窗口内容进行加密处理、防止窗口被恶意截屏和录屏等。

8.2.5 与新兴技术的融合

随着人工智能、虚拟现实和增强现实等新兴技术的发展,未来的窗口管理模块可能会与这些技术进行深度融合。例如,利用人工智能技术实现智能窗口布局和自适应调整,以及在虚拟现实和增强现实环境中实现更加自然和沉浸式的窗口交互体验。

总之,Android Activity 窗口管理模块是一个复杂而重要的系统组件,未来的发展前景广阔。通过不断的研究和创新,我们可以期待它为用户带来更加优质和便捷的移动应用体验。

相关推荐
jiet_h1 小时前
使用 Ktor 构建现代 Android 应用的后端服务
android
深漂阿碉4 小时前
Android studio2024的第一个安卓项目
android
zilong_zzz4 小时前
文件IO4(提高LCD显示效率/BMP图像原理与应用)
android
黑客老李5 小时前
面试经验分享 | 成都渗透测试工程师二面面经分享
经验分享·面试·职场和发展
狂炫一碗大米饭6 小时前
快刷每日面经😋
前端·面试
无极程序员8 小时前
远程主机可能不符合glibc和libstdc++ VS Code服务器的先决条件
android·java·运维·服务器·php
Yharim9 小时前
AllData数据中台源码分析
java·面试
快乐1019 小时前
Mac下FFmpeg编译和集成
android
就是我9 小时前
3种必须知道的JavaScript异步编程模型
前端·javascript·面试