深入剖析 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
对象。PhoneWindow
是 Window
的具体实现类,它负责管理 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
对象。
以下是 Activity
的 attach
方法中创建 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 的创建与添加
DecorView
是 PhoneWindow
的根视图,它包含了窗口的标题栏、状态栏等元素。在 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);
}
// 后续还有大量根据窗口样式属性设置窗口特征的代码
上述代码中,如果窗口是浮动窗口(mIsFloating
为 true
),会将窗口大小设置为包裹内容,并清除特定的标志位;否则设置窗口布局相关的标志位。同时,根据从样式资源中获取的属性值,决定是否请求无标题栏(FEATURE_NO_TITLE
)或 ActionBar(FEATURE_ACTION_BAR
)等窗口特征。
而关联 WindowManager
与窗口主要在 Activity
的 attach
方法中完成:
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
实例与当前 Activity
的 Window
进行关联,并传入 Activity
的相关标识和硬件加速标志等参数。如果当前 Activity
有父 Activity
,还会设置窗口的容器关系。最后,将 Window
的 WindowManager
赋值给 Activity
的 mWindowManager
成员变量,以便后续通过 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);
}
上述代码中,mGlobal
是 WindowManagerGlobal
的实例,通过调用其 addView
方法,将 View
(实际上是 DecorView
)、布局参数、显示对象以及父窗口等信息传递过去。
下面来看 WindowManagerGlobal
的 addView
方法:
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;
}
}
}
在这个方法中,首先对传入的参数进行一系列校验,确保 View
、Display
和参数类型的正确性。如果有父窗口,会调整子窗口的布局参数。接着创建 ViewRootImpl
对象,它是连接 View
与 WindowManagerService
的桥梁。将 View
、ViewRootImpl
和布局参数分别添加到 mViews
、mRoots
和 mParams
列表中。最后通过 root.setView(view, wparams, panelParentView)
方法,将窗口添加到 WindowManagerService
中。
4.3 WMS 对 Activity 窗口的处理与显示
WindowManagerService
(WMS)是 Android 系统中负责窗口管理的核心服务,它接收来自 WindowManagerGlobal
的窗口添加请求,并对窗口进行处理和显示。
ViewRootImpl
的 setView
方法会通过 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 的生命周期方法,如 onCreate
、onResume
、onPause
、onDestroy
等,在很大程度上决定了窗口的状态变化;而窗口的一些关键状态,如是否可见、是否获得焦点等,也会反过来影响 Activity 的行为。
在 Activity 的 onCreate
方法中,会创建并初始化窗口相关资源,这在前面窗口创建过程中已有体现。而当 Activity 进入 onResume
阶段时,窗口也进入一个关键的显示与交互准备状态。
以下是 ActivityThread
中 handleResumeActivity
方法与窗口显示相关的部分源码:
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
方法时,窗口的状态也会相应地发生改变。在 ActivityThread
的 handlePauseActivity
方法中,会对窗口进行一些处理,使其进入暂停状态。以下是部分源码:
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.mVisibleFromClient
为 true
),并且窗口存在(r.window != null
),会将窗口的 DecorView
设置为不可见(View.INVISIBLE
),这意味着窗口在视觉上不再显示给用户。然后调用 performPauseActivityIfNeeded
方法来执行 Activity 的 onPause
方法,完成 Activity 的暂停操作。
当 Activity 执行 onDestroy
方法时,窗口会被销毁,相关资源也会被释放。在 ActivityThread
的 handleDestroyActivity
方法中,会进行窗口的销毁操作。以下是部分源码:
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
),会通过 WindowManager
的 removeViewImmediate
方法将窗口的 DecorView
从 WindowManager
中移除,这意味着窗口不再由 WindowManager
管理,其资源也会被逐步释放。然后将 r.window
和 r.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
状态时,窗口会从不可见状态转换到可见状态。在 ActivityThread
的 handleResumeActivity
方法中,会触发窗口状态的转换:
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 作为窗口管理的核心服务,负责将事件分发给相应的窗口。
输入子系统通过 InputReader
和 InputDispatcher
来处理输入事件。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);
}
在 Activity
的 dispatchTouchEvent
方法中,当接收到按下事件(MotionEvent.ACTION_DOWN
)时,会调用 onUserInteraction
方法,表示用户与应用进行了交互。然后调用 getWindow().superDispatchTouchEvent(ev)
方法将事件传递给窗口进行处理,如果窗口处理了事件,返回 true
;否则,调用 onTouchEvent
方法处理事件。
6.2 Activity 对窗口事件的处理
6.2.1 Activity 的事件回调方法
Activity
提供了一系列的事件回调方法,用于处理窗口事件。这些方法包括 dispatchTouchEvent
、onTouchEvent
、dispatchKeyEvent
、onKeyDown
、onKeyUp
等。
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
方法调用 Activity
的 onKeyDown
或 onKeyUp
方法处理事件。
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);
}
在 Activity
的 onCreate
方法中,通过 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
类实现的动画,通常会使用 ObjectAnimator
或 AnimatorSet
等类来创建和管理动画。例如,使用 ObjectAnimator
创建一个简单的缩放动画:
java
java
// 创建一个缩放动画
ObjectAnimator scaleXAnimator = ObjectAnimator.ofFloat(view, "scaleX", 1.0f, 2.0f);
scaleXAnimator.setDuration(300);
scaleXAnimator.start();
上述代码创建了一个 ObjectAnimator
对象,用于对 view
的 scaleX
属性进行缩放动画,从 1.0 放大到 2.0,动画时长为 300 毫秒,然后启动动画。
八、总结与展望
8.1 本文总结
通过对 Android Activity 窗口管理模块的深入分析,我们详细了解了该模块在 Android 系统中的重要地位和作用。从窗口的创建过程来看,Activity 启动时会触发一系列复杂的操作,包括 PhoneWindow
的创建与初始化、DecorView
的生成与添加等,这些操作确保了窗口的基本结构和布局能够正确构建。
在窗口的显示过程中,WindowManager
起到了关键的管理作用,它通过 WindowManagerGlobal
与 WindowManagerService
(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 窗口管理模块是一个复杂而重要的系统组件,未来的发展前景广阔。通过不断的研究和创新,我们可以期待它为用户带来更加优质和便捷的移动应用体验。