首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >深入解析Activity、Window、View三者之间的关系

深入解析Activity、Window、View三者之间的关系

作者头像
木易士心
发布2025-11-30 09:45:19
发布2025-11-30 09:45:19
1680
举报

一、概述

在 Android 系统中,ActivityWindowView 是构建用户界面的核心组件,它们之间的关系紧密且层次分明。理解它们的源码层级关系对于深入掌握 Android UI 机制至关重要。

二、核心概念

  1. Activity: 代表一个屏幕,是用户与应用交互的单一焦点。它是四大组件之一,负责管理 UI 的生命周期。
  2. Window: 是一个抽象类,代表一个具有装饰(如状态栏、标题栏)和管理视图层级的顶级窗口。WindowView 的直接管理者,负责提供一个 View 树可以依附的容器。PhoneWindow 是其在 Android 中的具体实现。
  3. View: 所有 UI 组件的基类。View 是屏幕上的一块矩形区域,负责绘制和事件处理。ViewGroupView 的子类,可以包含其他 View,形成视图树(View Hierarchy)。

三、架构和工作原理

用一句话概括三者关系:

一个 Activity 拥有一个 Window(通常是 PhoneWindow),这个 Window 拥有一个作为根视图的 DecorView,而开发者通过 setContentView() 设置的 View(或 View 树)则被添加到 DecorView 内部的一个特定容器 (mContentParent) 中。

1.层级架构图:

代码语言:javascript
复制
                              Android System
                                    |
                                    v
                                Activity
                                    | (持有 mWindow)
                                    v
                                Window (PhoneWindow)
                                    | (持有 mDecor)
                                    v
                             DecorView (根视图)
                                    |
        -----------------------------------------------------
        |                           |                         |
  状态栏/标题栏等系统装饰     mContentParent (FrameLayout)      其他装饰视图
                                    |
                                    v
                       开发者 setContentView 的布局 (View 树)
                                    |
                    --------------------------------
                    |              |               |
               TextView        Button          ImageView
               (子 View)       (子 View)         (子 View)

2.核心要点

  1. Activity 是管理者: 它负责生命周期,但不直接管理 UI 绘制。它通过 Window 间接管理 UI。
  2. Window 是桥梁: 它是 ActivityView 之间的桥梁。Activity 将 UI 相关的操作(如 setContentView)委托给 Window
  3. DecorView 是根: DecorView 是整个 Window 视图树的根节点,由 PhoneWindow 创建和管理。它包裹了系统 UI 和应用内容。
  4. mContentParent 是内容容器: 这是开发者真正可以控制的区域。setContentView() 设置的布局最终都成为 mContentParent 的子视图。
  5. 源码调用链: Activity.setContentView() -> PhoneWindow.setContentView() -> PhoneWindow.installDecor() (创建 DecorViewmContentParent) -> LayoutInflater.inflate() (将布局添加到 mContentParent)。
在这里插入图片描述
在这里插入图片描述

四、源码级解析

下面我们通过源码来详细拆解这个关系链。

1. 关系的起点:Activity 的创建和 Window 的附着

一切的起点从 ActivityThread#performLaunchActivity 方法开始,当系统要启动一个 Activity 时,会调用这个方法。

代码语言:javascript
复制
// ActivityThread.java
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    // ... 创建 Activity 实例 ...
    Activity activity = null;
    try {
        java.lang.ClassLoader cl = appContext.getClassLoader();
        activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
        // ...
    } catch (Exception e) {
        // ...
    }

    try {
        // ... 创建 Application 并调用 onCreate ...
        Application app = r.packageInfo.makeApplication(false, mInstrumentation);

        if (activity != null) {
            // ... 关联 Context 等 ...
            // **核心方法:调用 activity.attach()**
            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);

            // ... 调用 onCreate ...
            if (r.isPersistable()) {
                mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
            } else {
                mInstrumentation.callActivityOnCreate(activity, r.state);
            }
            // ...
        }
    }
    // ...
}

关键就在于 activity.attach() 方法。我们进入 Activity#attach 方法。

2. Activity.attach():Window 的创建与关联

代码语言:javascript
复制
// Activity.java
final void attach(Context context, ActivityThread aThread,
        Instrumentation instr, IBinder token, int ident,
        Application application, Intent intent, ActivityInfo info,
        CharSequence title, Activity parent, String id,
        NonConfigurationInstances lastNonConfigurationInstances,
        Configuration config, String referrer, IVoiceInteractor voiceInteractor,
        Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken) {
    // ... 其他初始化 ...

    // **1. 创建并初始化唯一的 Window 对象**
    mWindow = new PhoneWindow(this, window, activityConfigCallback);
    mWindow.setWindowControllerCallback(this);
    mWindow.setCallback(this); // 将 Activity 设置为 Window 的 Callback,用于接收事件分发(如按键、触摸)
    mWindow.setOnWindowDismissedCallback(this);
    mWindow.getLayoutInflater().setPrivateFactory(this);

    // ... 其他初始化 ...

    // **2. 为 Window 设置 WindowManager**
    mWindow.setWindowManager(
            (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
            mToken, mComponent.flattenToString(),
            (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
    if (mParent != null) {
        mWindow.setContainer(mParent.getWindow());
    }
    // **3. Activity 持有 WindowManager 的引用**
    mWindowManager = mWindow.getWindowManager();
}

在这里,我们清晰地看到:

  • Activity 持有一个 PhoneWindow(Window 的唯一实现类)实例 mWindow
  • PhoneWindow 被创建时,传入了 this,即当前 Activity 的引用。
  • Activity 将自己设置为 Window 的 Callback (mWindow.setCallback(this)),这样窗口级别的事件(如按键、菜单、屏幕触摸)就可以传递到 Activity。
  • Activity 通过 mWindow 获取了 WindowManager 并持有。

3. setContentView():View 的创建与添加

当我们通常在 Activity 的 onCreate 中调用 setContentView(R.layout.activity_main) 时,发生了什么?

代码语言:javascript
复制
// Activity.java
public void setContentView(@LayoutRes int layoutResID) {
    // **委托给 Window 的 setContentView 方法**
    getWindow().setContentView(layoutResID);
    // 初始化 ActionBar(如果存在)
    initWindowDecorActionBar();
}

getWindow() 返回的就是我们刚刚创建的 PhoneWindow 对象。我们进入 PhoneWindow#setContentView

代码语言:javascript
复制
// PhoneWindow.java
@Override
public void setContentView(int layoutResID) {
    // **1. 核心方法:installDecor()**
    if (mContentParent == null) {
        installDecor();
    } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        mContentParent.removeAllViews();
    }

    // **2. 将我们的布局文件 inflate 到 mContentParent 中**
    if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID, getContext());
        transitionTo(newScene);
    } else {
        // 通常走这个分支
        mLayoutInflater.inflate(layoutResID, mContentParent);
    }
    mContentParent.requestApplyInsets();
    final Callback cb = getCallback();
    if (cb != null && !isDestroyed()) {
        // **3. 通知 Activity 内容已经改变**
        cb.onContentChanged();
    }
    // ...
}

关键方法是 installDecor()

PhoneWindow.installDecor():创建 DecorView 和 ContentParent

代码语言:javascript
复制
// PhoneWindow.java
private void installDecor() {
    mForceDecorInstall = false;
    if (mDecor == null) {
        // **1. 生成 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) {
        // **2. 生成 mContentParent**
        mContentParent = generateLayout(mDecor);

        // ... 根据 Theme 和 Feature 设置标题栏、状态栏等 ...
    }
}
  • generateDecor(-1):创建了 DecorView,它是 FrameLayout 的子类,是整个窗口的根视图。
  • generateLayout(mDecor):根据主题和 Requested Feature(如 FEATURE_NO_TITLE),选择一个预定义的屏幕布局(如 R.layout.screen_simple),并将其加载到 DecorView 中。这个布局文件里会包含一个最重要的 ViewGroup,其 ID 为 android.R.id.content。这个方法会找到这个 ViewGroup 并返回,赋值给 mContentParent

所以,mContentParent 就是我们通过 setContentView 传进去的布局文件的父容器。

关系链到这里变成了: Activity -> PhoneWindow -> DecorView -> ContentParent (android.R.id.content) -> Your Custom Layout (通过 setContentView 设置)


4. 视图的显示:Window 与 WindowManager

创建好视图树后,如何将它显示到屏幕上?这发生在 ActivityThread#handleResumeActivity 中。

代码语言:javascript
复制
// ActivityThread.java
@Override
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward, String reason) {
    // ... 调用 onStart(), onResume() ...

    final Activity a = r.activity;
    // ...
    if (r.window == null && !a.mFinished && willBeVisible) {
        r.window = r.activity.getWindow();
        // **获取 DecorView**
        View decor = r.window.getDecorView();
        decor.setVisibility(View.INVISIBLE);
        // **获取 WindowManager**
        ViewManager wm = a.getWindowManager();
        WindowManager.LayoutParams l = r.window.getAttributes();
        a.mDecor = decor;
        l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
        // ...
        if (a.mVisibleFromClient) {
            if (!a.mWindowAdded) {
                a.mWindowAdded = true;
                // **核心:将 DecorView 添加到 WindowManager 中!**
                wm.addView(decor, l);
            } else {
                a.onWindowAttributesChanged(l);
            }
        }
    }
    // ...
    if (!r.activity.mFinished && willBeVisible && r.activity.mDecor != null && !r.hideForNow) {
        // ... 最终让 DecorView 可见 ...
        if (r.activity.mVisibleFromClient) {
            r.activity.makeVisible();
        }
    }
}
  • a.getWindowManager() 返回的就是在 attach() 方法中设置的 mWindowManager
  • wm.addView(decor, l) 这行代码是视图显示到屏幕上的最终步骤。

这里的 WindowManager 的实际实现是 WindowManagerImplWindowManagerImpl.addView() 会创建一个 ViewRootImpl 对象。

ViewRootImpl 是连接 WindowWMS(WindowManagerService)的桥梁。

  • 它负责执行 View 的测量、布局、绘制。
  • 它通过 Choreographer 来管理 VSync 信号,安排渲染工作。
  • 它通过 BinderWMS 通信,管理窗口的状态(如焦点、位置、动画等)。
  • 它也是输入事件的中转站,将触摸事件分发给正确的 View

五、关系图与总结

1. 关系总结

  1. Activity & Window
    • 一对一: 每个 Activity 都持有一个 PhoneWindow 对象。
    • 管理者与被管理者Activity 负责 Window 的生命周期和事件回调。WindowActivity 的 UI 承载器。
  2. Window & View
    • 一对多(树形): 一个 Window 持有一个根视图 DecorViewDecorView 内部包含一个 ContentView,我们自己的布局是 ContentView 的子视图。
    • 容器与内容Window 是视图的容器,它定义了窗口的样式和行为框架(如标题栏),而 View 是具体显示的内容。
  3. Activity & View
    • 间接关系Activity 通过 Window 来管理和控制 ViewActivity 不直接持有 View,但可以通过 setContentViewfindViewById 来间接操作 ViewfindViewById 内部也是通过 Window 来查找 DecorView,再在 DecorView 中查找目标 View

2.关系图

完整流程链Activity (创建和管理) -> PhoneWindow (承载) -> DecorView (根容器) -> ContentView (内容容器) -> Your View Hierarchy (你的 UI) -> ViewRootImpl (布局、绘制、通信) -> WMS (系统级窗口管理) -> SurfaceFlinger (最终渲染到屏幕)。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-11-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、概述
  • 二、核心概念
  • 三、架构和工作原理
    • 1.层级架构图:
    • 2.核心要点
  • 四、源码级解析
    • 1. 关系的起点:Activity 的创建和 Window 的附着
    • 2. Activity.attach():Window 的创建与关联
    • 3. setContentView():View 的创建与添加
    • 4. 视图的显示:Window 与 WindowManager
  • 五、关系图与总结
    • 1. 关系总结
    • 2.关系图
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档