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

一、概述

在 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.层级架构图:

                              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 时,会调用这个方法。

// 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 的创建与关联

// 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) 时,发生了什么?

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

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

// 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

// 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 中。

// 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.关系图

持有 (1:1)
持有 (1:1)
包含
包含
持有
通过 addView 添加
关联
Binder 通信
Activity
PhoneWindow
DecorView
ContentParent
(android.R.id.content)
Your Layout
(setContentView)
WindowManager
ViewRootImpl
WMS
WindowManagerService

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

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

木易 士心

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值