[Android] WindowManager? Activity?

250 阅读5分钟

几个基本概念

  • WindowManager: 一个接口类,继承自接口ViewManager,对Window进行管理
  • Window: 一个抽象类,作为顶级view添加到WindowManager中,对View进行管理
  • PhoneWindow: Window唯一实现类,Window是抽象概念,添加到WindowManager的根容器
  • DecorView: PhoneWindow内部的一个成员变量,继承自FrameLayout -> ViewGroup

几个问题

  • Activity的View如何与Window关联?
  • Window如何与WindowManager关联?
  • Activity和Dialog视图如何关联?
  • Dialog的视图如何与Window关联?

Activity/Window/WindowManager 的关系

在Activity内部维护者一个Window的实例变量mWindow

// frameworks/base/core/java/android/app/Activity.java
public class Activity extends ContextThemeWrappe{
  private Window mWindow;
}

Window 是一个抽象类,它的具体实现类为 PhoneWindow,在 Activity 的 attach 方法中给 Window 的实例变量 mWindow 赋值

// frameworks/base/core/java/android/app/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) {
    ......

    mWindow = new PhoneWindow(this, window, activityConfigCallback);
    ......

    mWindow.setWindowManager(
            (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
            mToken, mComponent.flattenToString(),
            (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
    ......
}
  • 创建了PhoneWindow实例并赋值给mWindow
  • 调用setWindowManager,具体实现是在Window中
// frameworks/base/core/java/android/view/Window.java
public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
        boolean hardwareAccelerated) {
    ......
    // mWindowManager 是 WindowManagerImpl的实例变量
    mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}

将 WindowManager 转换为 WindowManagerImpl,之后调用 createLocalWindowManager 方法,并传递当前的 Window 对象,构建 WindowManagerImpl 对象,之后赋值给 mWindowManager

// frameworks/base/core/java/android/view/WindowManagerImpl.java
public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
    return new WindowManagerImpl(mContext, parentWindow);
}

在 createLocalWindowManager 方法中,做了一件事,将 Window 作为参数构建了一个 WindowManagerImpl 对象返还给调用处。

即,在 Activity 的 attach 方法中,通过调用 Window 的 setWindowManager 方法将 Window 和 WindowManager 关联在了一起。

PhoneWindow 是 Window 的实现类,它是一个窗口,本身并不具备 View 相关的能力,实际上在 PhoneWindow 内部维护这一个变量 mDecor。

// frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java
public class PhoneWindow extends Window{
  // This is the top-level view of the window, containing the   window decor.
  private DecorView mDecor; 

  private void installDecor() {
        mForceDecorInstall = false;
        if (mDecor == null) {
            // 完成DecorView的实例化
            mDecor = generateDecor(-1);
            ......
        }

        if (mContentParent == null) {
            // 调用 generateLayout 方法完成DecorView的初始化
            mContentParent = generateLayout(mDecor);
        } 
        ......
    }

    // 完成DecorView的实例化
    protected DecorView generateDecor(int featureId) {
        ......
        return new DecorView(context, featureId, this, getAttributes());
    }

    // 调用 generateLayout 方法完成DecorView的初始化
    // 诸如主题相关的feature、DecorView的背景,同时也初始化 contentParent
    protected ViewGroup generateLayout(DecorView decor) {
        ......

        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);

        ......
    }

}
  • mDecor 是 window 的顶级视图,它继承自 FrameLayout,它的创建过程由 installDecor 完成,在 installDecor 方法中通过 generateDecor 方法来完成DecorView的实例化
  • 调用 generateLayout 方法 要负责了DecorView的初始设置
  • mDecor 它实际上是一个 ViewGroup,当在 Activity 中调用 setContentView 方法,通过调用 inflater 方法把布局资源转换为一个 View,然后添加到 DecorView 的 mContenParnent 中

当 View 初始化完成之后,最后会进入 ActivityThread 的 handlerResumeActivity 方法,执行 执行了r.activity.makeVisible()方法。

// frameworks/base/core/java/android/app/ActivityThread.java
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
            String reason) {
        ......

        if (r.activity.mVisibleFromClient) {
            r.activity.makeVisible();
        }
        ......
    }

最终调用 Activity 的 makeVisible 方法,把 decorView 添加到 WindowManage 中。

// frameworks/base/core/java/android/app/Activity.java
void makeVisible() {
    if (!mWindowAdded) {
        ViewManager wm = getWindowManager();
        wm.addView(mDecor, getWindow().getAttributes());
        mWindowAdded = true;
    }
    mDecor.setVisibility(View.VISIBLE);
}

至此,三者关系明确了:

  • 一个Activity持有一个PhoneWindow对象,而一个PhoneWindow对象持有一个DecorView实例
  • PhoneWindow继承自Window,一个Window对象内部持有WindowManager实例,通过setWindowManager将Window和WindowManager关联在一起
  • WindowManager继承自ViewManager,WindowManagerImpl是WindowManager接口实现类,具体功能都是委托为WindowManagerGlobal来实现
  • 调用WindowManager的addView方法,实际是WindowManagerImpl的addView方法

Activity和Dialog/View绑定过程

  1. LayoutInflaterinflate方法,深度优先遍历解析View
  2. ViewGroupaddView方法,将子View添加到跟布局中
  3. WindowManageraddView方法,添加根布局

LayoutInflater的inflate有多个重载方法,常用的是带三个参数的:

// frameworks/base/core/java/android/view/LayoutInflater.java
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
    return inflate(resource, root, root != null);
}
  • Params
    • resource: 要解析的xml布局文件id
    • root: 根布局
    • attachToRoot: 是否添加到根布局root中
  • Behaviors
    • when attachToRoot == true && root != null, 新解析出来的View会被add到root中去然后将root作为结果返回
    • when attachToRoot == false && root != null, 新解析出来的View会直接作为结果返回,且root会为新解析的View生成LayoutParams并设置到该View中去
    • when attachToRoot == false && root == null, 新解析的View会直接作为结果返回

当View解析完成之后,会调用WindowManager的addView方法;WindowManager是一个接口类,继承自接口ViewManager,用来管理Window,它的实现类是WindowManagerImpl,所以实际会调用到WindowManagerImpl的addView方法:

// frameworks/base/core/java/android/view/WindowManagerImpl.java
public final class WindowManagerImpl implements WindowManager {
    @UnsupportedAppUsage
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    private final Context mContext;
    private final Window mParentWindow;
    ......

    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }
    ......

}

mGlobalWindowManagerGlobal 的实例,使用的单例设计模式,参数 mParentWindowWindow 的实例,实际上是委托给 WindowManagerGlobal 去实现的。

至此,Dialog的View就跟Activity绑定起来了,但是内部还是通过window/windowmanager来管理的。


回答这些问题

Activity的View如何与Window关联?

  • 在Activity内部维护着一个Window实例变量mWindow
  • 在Activity的attach方法中,初始化mWindow为PhoneWindow实例,并与WindowManager关联
  • PhoneWindow内部维护mDecorView变量处理View
  • View初始化完成之后,最后会进入ActivityThread的handlerResumeActivity 方法,执行 执行了r.activity.makeVisible()方法
  • 在makeVisible方法中将decorView添加到WindowManager中
// frameworks/base/core/java/android/app/Activity.java
void makeVisible() {
    if (!mWindowAdded) {
        ViewManager wm = getWindowManager();
        wm.addView(mDecor, getWindow().getAttributes());
        mWindowAdded = true;
    }
    mDecor.setVisibility(View.VISIBLE);
}

Window如何与WindowManager关联?

  • activity的attach方法中,初始化mWindow为PhoneWindow实例
  • 通过调用setWindowManager将其与WindowManager关联
// frameworks/base/core/java/android/view/Window.java
public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
        boolean hardwareAccelerated) {
    ......
    // mWindowManager 是 WindowManagerImpl的实例变量
    mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}

Activity如何与Dialog视图关联?

  1. LayoutInflaterinflate方法,深度优先遍历解析View
  2. ViewGroupaddView方法,将子View添加到跟布局中
  3. WindowManageraddView方法,添加根布局

Dialog View如何与Window关联?

  • 在Dialog构造方法中初始化了Window对象

    // frameworks/base/core/java/android/app/Dialog.java
    Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
      ...
      // 获取WindowManager对象
      mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
      // 构建PhoneWindow
      final Window w = new PhoneWindow(mContext);
      // mWindow 是PhoneWindow实例
      mWindow = w;
     ...
     }
    
  • 通过Dialog的show方法,完成view的绘制和Dialog的显示

    // frameworks/base/core/java/android/app/Dialog.java
    public void show() {
      // 获取DecorView
      mDecor = mWindow.getDecorView();
      // 获取布局参数
      WindowManager.LayoutParams l = mWindow.getAttributes();
      // 将DecorView和布局参数添加到WindowManager中
      mWindowManager.addView(mDecor, l);
    }