0xA06 Android 10 源碼分析:WindowManager 視圖綁定以及體系結構

引言

  • 這是 Android 10 源碼分析系列的第 6 篇
  • 分支:android-10.0.0_r14
  • 全文閱讀大概 10 分鐘

經過這篇文章你將學習到如下內容,將在文末總結部分會給出相應的答案html

  • Acivity 和 Dialog 視圖解析綁定過程?
  • Activity 的視圖如何與 Window 關聯的?
  • Window 如何與 WindowManager 關聯?
  • Dialog 的視圖如何與 Window 關聯?

本文主要分析 Activity、Window、PhoneWindow、WindowManager 之間的關係,爲咱們後面的文章 「如何在 Andorid 系統裏添加自定義View」 等等文章奠基基礎,先來了解一下它們的基本概念java

windo

  • WindowManager:它是一個接口類,繼承自接口 ViewManager,對 Window 進行管理
  • Window:它是一個抽象類,它做爲一個頂級視圖添加到 WindowManager 中,對 View 進行管理
  • PhoneWindow:Window惟一實現類,Window是一個抽象概念,添加到WindowManager的根容器
  • DecorView: 它是 PhoneWindow 內部的一個成員變量,繼承自 FrameLayout,FrameLayout 繼承自 ViewGroup

在分析他們以前的關係以前,咱們先來回顧一下 Acivity 和 Dialog 視圖解析綁定的過程android

Acivity 和 Dialog 視圖解析綁定的過程

Acivity 和 Dialog 相關的文章:算法

在以前的文章 分別介紹了 Acivity 和 自定義 Dialog 視圖的解析和綁定,總的來講分爲三步編程

    1. 調用 LayoutInflater 的 inflate 方法,深度優先遍歷解析 View
    1. 調用 ViewGroup 的 addView 方法將子 View 添加到根佈局中
    1. 調用 WindowManager 的 addView 方法添加根佈局

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);
}
複製代碼
  • resource:要解析的 xml 佈局文件 Id
  • root:表示根佈局
  • attachToRoot:是否要添加到父佈局 root 中

resource 其實很好理解就是資源 Id,而 root 和 attachToRoot 分別表明什麼意思:bash

  • 當 attachToRoot == true 且 root != null 時,新解析出來的 View 會被 add 到 root 中去,而後將 root 做爲結果返回
  • 當 attachToRoot == false 且 root != null 時,新解析的 View 會直接做爲結果返回,並且 root 會爲新解析的 View 生成 LayoutParams 並設置到該 View 中去
  • 當 attachToRoot == false 且 root == null 時,新解析的 View 會直接做爲結果返回

當 View 解析完成以後,最後會調用 WindowManager 的 addView 方法,WindowManager 是一個接口類,繼承自接口 ViewManager,用來管理 Window,它的實現類爲 WindowManagerImpl,因此調用 WindowManager 的 addView 方法,實際上調用的是 WindowManagerImpl 的 addView 方法
frameworks/base/core/java/android/view/WindowManagerImpl.javaapp

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 是 WindowManagerGlobal 的實例
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }
    ......
    
}
複製代碼

mGlobal 是 WindowManagerGlobal 的實例,使用的單例設計模式,參數 mParentWindow 是 Window 的實例,其實是委託給 WindowManagerGlobal 去實現的
ide

到這裏咱們關於 Acivity 和 Dialog 視圖的解析和添加過程大概介紹完了,關於 Dialog 的視圖如何與 Window 綁定在 0xA05 Android 10 源碼分析:Dialog 加載繪製流程以及在 Kotlin、DataBinding 中的使用 文章中介紹了,接下來分析一下 Activity、Window、WindowManager 的關係工具

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
  • 調用 PhoneWindow 的 setWindowManager 方法,這個方法的具體實現發生在 Window 中,最終調用的是 Window 的 setWindowManager 方法
    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的初始設置,諸如主題相關的feature、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的初始設置,諸如主題相關的feature、DecorView的背景,同時也初始化 contentParent
  • 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 對象內部持有 mWindowManager 的實例,經過調用 setWindowManager 方法與 WindowManager 關聯在一塊兒
  • WindowManager 繼承自 ViewManager,WindowManagerImpl 是 WindowManager 接口的實現類,可是具體的功能都會委託給 WindowManagerGlobal 來實現
  • 調用 WindowManager 的 addView 方法,實際上調用的是 WindowManagerImpl 的 addView 方法

總結

Acivity 和 Dialog 視圖解析綁定過程?

    1. 調用 LayoutInflater 的 inflate 方法,深度優先遍歷解析 View
    1. 調用 ViewGroup 的 addView 方法將子 View 添加到根佈局中
    1. 調用 WindowManager 的 addView 方法添加根佈局

Activity 的視圖如何與 Window 關聯的?

  • 在 Activity 內部維護着一個 Window 的實例變量 mWindow
    frameworks/base/core/java/android/app/Activity.java
public class Activity extends ContextThemeWrappe{
  private Window mWindow;
}
複製代碼
  • 最後調用 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);
}
複製代碼

Window 如何與 WindowManager 關聯?

在 Activity 的 attach 方法中,調用 PhoneWindow 的 setWindowManager 方法,這個方法的具體實現發生在 Window 中,最終調用的是 Window 的 setWindowManager 方法,將 Window 和 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);
}
複製代碼

Dialog 的視圖如何與 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);
}
複製代碼

參考文獻

結語

致力於分享一系列 Android 系統源碼、逆向分析、算法相關的文章,每篇文章都會反覆推敲,若是你同我同樣喜歡 coding,一塊兒來學習,期待與你一塊兒成長

文章列表

Android 10 源碼系列

工具系列

逆向系列

相關文章
相關標籤/搜索