Window系列 (一) — WindowManager 詳解

一、概述

由於 WindowManager 是與 WindowManagerService (WMS) 關聯緊密的類。因此,在分析 WMS前,我們先來分析一下 WindowManager 體系的相關知識。

下面從如下3個部分進行分析:

  1. WindowManager體系
  2. Window 的屬性
  3. Window 的操作

版本: Android SDK 29

關聯文章:

  1. 《源碼分析 — Context 詳解》
  2. 《源碼分析 — Activity的啓動流程》
  3. 《源碼分析 — SystemServer》

二、WindowManager 體系

1. 類圖

在這裏插入圖片描述

WindowManager 體系如上圖所示 (圖片來源於網絡)。

概念梳理:

  1. Window 是一個抽象概念,代表一個窗口,其具體的實現類爲 PhoneWindow ,它對 View進行管理。
  2. WindowManager 是一個接口類,繼承自接口 ViewManager,它是用來管理 Window 的。它的具體實現類爲 WindowManagerImpI。
  3. WindowManagerGlobal 是實際操作的類,是一個單例,每個進程中只有一個實例對象,該實例對象在 WindowManagerGlobal 中。
  4. 在 WindowManagerGlobal 中,會創建 ViewRootImpl 實例對象,每個根 View 對應一個 ViewRootImpl 實例對象。
  5. 想要對 Window (View)進行添加、更新和刪除操作,可以使用 WindowManager 來執行。最終的操作是通過 Binder 交給 WMS 來執行的。

2. 源碼分析

2.1 ViewManager 的結構

可以看到,ViewManager 提供的三個方法都是將 View 作爲參數傳遞進去的。

// 可以看到,ViewManager 提供的三個方法都是將 View 作爲參數傳遞進去的。
public interface ViewManager{
    public void addView(View view, ViewGroup.LayoutParams params);
    public void updateViewLayout(View view, ViewGroup.LayoutParams params);
    public void removeView(View view);
}

2.2 PhoneWindow 對象何時創建?

問: 已知 Window 的具體實現類是 PhoneWindow ,那它是何時創建的呢?
.
答:Activity.attach() 方法中創建了 PhoneWindow 對象, Activity.attach() 方法又是在 ActivityThread.performLaunchActivity() 方法中被觸發,這部分可以參考 《源碼分析 — Activity的啓動流程》

Activity

// 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*/);
	// 1.創建了PhoneWindow對象。
    mWindow = new PhoneWindow(this, window, activityConfigCallback);
    mWindow.setWindowControllerCallback(this);
    // 2.Activity 實現了 Window.Callback接口,會觸發 Window 操作的相關回調(如 dispatchTouchEvent方法)。
    mWindow.setCallback(this);
	// ...省略代碼...
	
	// 3.將 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());
    }
    // 4.在 Activity 中持有 WindowManager,便於在 Activity 中使用 WindowManager。
    mWindowManager = mWindow.getWindowManager();
    // ...省略代碼...
}

Window

// Window.class
public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
        boolean hardwareAccelerated) {
    mAppToken = appToken;
    mAppName = appName;
    mHardwareAccelerated = hardwareAccelerated;
    if (wm == null) {
    	/* 
    	 * 1.如果傳入的 WindowManager 爲空,就從服務裏面獲取。
    	 * 注:
    	 * Context.WINDOW_SERVICE = "window"。
    	 * getSystemService()真正的執行邏輯在ContextImpl中。
    	 * context.getSystemService(Context.WINDOW_SERVICE) 獲取到的是 WindowManagerImpl 實例對象。
    	 */
        wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
    }
    // 2.這裏會創建一個WindowManagerImpl對象。
    mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}

ContextImpl

// ContextImpl.class
public Object getSystemService(String name) {
	// 獲取名字爲 "window" 的服務
    return SystemServiceRegistry.getSystemService(this, name);
}

SystemServiceRegistry

// SystemServiceRegistry.class
final class SystemServiceRegistry {
	// 根據服務類型,保存服務名稱
    static Map<Class<?>, String> SYSTEM_SERVICE_NAMES = new ArrayMap<Class<?>, String>();
    // 根據服務名稱,獲取實際的服務
    static Map<String, ServiceFetcher<?>> SYSTEM_SERVICE_FETCHERS = new ArrayMap<String, ServiceFetcher<?>>();
    
	static {
		// ...省略一系列的服務註冊代碼...
		registerService(Context.WINDOW_SERVICE, WindowManager.class, new CachedServiceFetcher<WindowManager>() {
	    	@Override
            public WindowManager createService(ContextImpl ctx) {
            	// 實際獲取到的是 WindowManagerImpl 實例對象,注意這的構造參數沒有關聯Window,在createLocalWindowManager過程中會與此處有關聯。
                return new WindowManagerImpl(ctx);
            }});
	}
	
	// 根據服務名稱來獲取服務
	public static Object getSystemService(ContextImpl ctx, String name) {
		// 
	    ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
	    return fetcher != null ? fetcher.getService(ctx) : null;
	}
	
	// 註冊服務時,將服務保存在兩個 Map 集合中。
    private static <T> void registerService(String serviceName, Class<T> serviceClass,
            ServiceFetcher<T> serviceFetcher) {
        SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
        SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
    }
}

WindowManagerImpl

// WindowManagerImpl.class
public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
    return new WindowManagerImpl(mContext, parentWindow);
}

小結:

問: 在執行 ((WindowManagerImpl)wm).createLocalWindowManager(this) 方法時,參數 wm 已經是 WindowManagerImpl 對象了,爲什麼還要通過 createLocalWindowManager() 重新創建一個 WindowManagerImpl 對象呢?
.
答: wm 對象內部沒有關聯當前的 Window,而重新創建的 WindowManagerImpl 對象會將當前的 Window 作爲參數傳入,因此新的 WindowManagerImpl 對象可以對 Window 進行操作。

2.3 WindowManagerImpl 類

public final class WindowManagerImpl implements WindowManager {
	// 單例
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    
    @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
    	// 添加默認token
        applyDefaultToken(params);
        // WindowManagerImpl 類相當於 WindowManagerGlobal 的代理。
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }
}

三、Window 的屬性 (類型和顯示次序)

Window 的類型有很多種,總的來說分爲三大類型,分別是 Application Window(應用程序窗口)Sub Window (子窗口)System Window (系統窗口)

窗口類型 Type 值
應用程序窗口 1 ~ 99
子窗口 1000 ~ 1999
系統窗口 2000 ~ 2999

在一般情況下, Type 值越大則 Z-Oder 排序越靠前,就越靠近用戶。


四、Window 的操作

我們知道,系統對 Window 進行添加、更新、刪除操作,都是通過 WindowManager 來操作的。WindowManager 對 Window 的操作最後都交給 WindowManagerGlobal 來執行。

下面我們來分析一下 WindowManagerGlobal 是如何進行 Window 的添加、更新、刪除操作的。

1. Window 的添加過程

時序圖:
在這裏插入圖片描述

代碼流程分析:
WindowManagerImpl

// WindowManagerImpl.class
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();

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

WindowManagerGlobal

// WindowManagerGlobal.class
ArrayList<View> mViews = new ArrayList<View>();
ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
ArrayList<WindowManager.LayoutParams> mParams = new ArrayList<WindowManager.LayoutParams>();

public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
    // ...省略代碼...
    // 1.根節點的LayoutParams必須爲WindowManager.LayoutParams類型,因爲確定根View的大小需要使用。
    if (!(params instanceof WindowManager.LayoutParams)) {
        throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
    }

    final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
    if (parentWindow != null) {
    	// 2.如果這個窗口有父窗口,則需要調整 wparams 的大小,使 wparams 的大小不超過父容器的大小。
        parentWindow.adjustLayoutParamsForSubWindow(wparams);
    } else {
        // ...省略代碼...
    }
    
    ViewRootImpl root;
    View panelParentView = null;
    synchronized (mLock) {
    	// ...省略代碼...

		// 3.將傳入的根View添加到ViewRootImpl對象中(一個根View 對應一個 ViewRootImpl)。
        root = new ViewRootImpl(view.getContext(), display);
        // 4.將調整後的 wparams 賦值給根 View。
        view.setLayoutParams(wparams);
		
		// 5.將根 View、根View對應的ViewRootImpl、根View的佈局參數LayoutParams分別存入三個集合中。
        mViews.add(view);
        mRoots.add(root);
        mParams.add(wparams);

        try {
        	// 6.執行 ViewRootImpl.setView() 方法。
            root.setView(view, wparams, panelParentView);
        } catch (RuntimeException e) {
            // ...省略代碼...
        }
    }
}

ViewRootImpl

ViewRootImpl 類的主要職責:

  1. View 樹的根並管理 View 樹。
  2. 觸發 View 的測量、 佈局和繪製。
  3. 輸入事件的中轉站 。
  4. 管理 Surface 。
  5. 負責與 WMS 進行進程間通信 。
// ViewRootImpl.class
// 用於遠程通信的Binder
IWindowSession mWindowSession = WindowManagerGlobal.getWindowSession();

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    synchronized (this) {
        if (mView == null) {
            // ...省略代碼...
            // 1.調用requestLayout方法進行繪製。
            requestLayout();
            try {
                mOrigWindowType = mWindowAttributes.type;
                mAttachInfo.mRecomputeGlobalAttributes = true;
                collectViewAttributes();
                // 2.獲取遠程服務進行通信(IWindowSession對象的獲取在第4部分分析)
                res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                        getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
                        mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                        mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
                        mTempInsets);
                setFrame(mTmpFrame);
            } catch (RemoteException e) {
               // ...省略代碼...
            }
            // ...省略代碼...
		}
    }
}

public void requestLayout() {
    if (!mHandlingLayoutInLayoutRequest) {
        checkThread();
        mLayoutRequested = true;
        // 該方法的分析過程請看本文 “scheduleTraversals() 執行流程” 部分。
        scheduleTraversals();
    }
}

從代碼中我們可以看到,在 setView() 方法中,會調用 mWindowSession.addToDisplay() 來與遠程服務進行通信 (mWindowSession 是一個 Binder 對象)。那 mWindowSession 對象又是如何獲取的呢?這個在 Window 操作 - IWindowSession 對象的獲取流程 分析。

Session

// Session.class
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
        int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets,
        Rect outStableInsets, Rect outOutsets,
        DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
        InsetsState outInsetsState) {
    // mService 就是 WMS,這裏在操作 WMS.addView()過程中,將Session也傳遞進去,目的告訴WMS要操作哪個應用。
    // 每個應用都對應一個唯一的Session,系統服務就是通過Session來區分不同的應用的。
    return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,
            outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel,
            outInsetsState);
}

WindowManagerService

// WindowManagerService.class
public int addWindow(Session session, IWindow client, int seq,
        LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,
        Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
        DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
        InsetsState outInsetsState) {
	// ...省略代碼...
}

到這裏,Window 在應用進程的添加邏輯就已經分析完了,在 WMS 部分的邏輯將在 WMS 分析中介紹。


2. Window 的更新過程

WindowManagerImpl

// WindowManagerImpl.class
public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
    applyDefaultToken(params);
    mGlobal.updateViewLayout(view, params);
}

WindowManagerGlobal

// WindowManagerGlobal.class
public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
    if (view == null) {
        throw new IllegalArgumentException("view must not be null");
    }
    // 1.校驗 LayoutParams 類型
    if (!(params instanceof WindowManager.LayoutParams)) {
        throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
    }
	// 將 LayoutParams 添加到根 View 中。
    final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
    view.setLayoutParams(wparams);

    synchronized (mLock) {
    	// 3.替換 View 相關的參數(添加 View 時將 View,ViewRootImpl,LayoutParams 添加到三個集合中)
        int index = findViewLocked(view, true);
        ViewRootImpl root = mRoots.get(index);
        mParams.remove(index);
        mParams.add(index, wparams);
        // 這裏調用 ViewRootImpl.setLayoutParams 進行更新
        root.setLayoutParams(wparams, false);
    }
}
// 查詢指定 View 在 mViews 集合中的位置。
private int findViewLocked(View view, boolean required) {
    final int index = mViews.indexOf(view);
    if (required && index < 0) {
        throw new IllegalArgumentException("View=" + view + " not attached to window manager");
    }
    return index;
}

ViewRootImpl

// ViewRootImpl.class
void setLayoutParams(WindowManager.LayoutParams attrs, boolean newView) {
    synchronized (this) {
        // ...省略代碼...
        // 這裏的操作與 Window 的添加過程一樣。
        scheduleTraversals();
    }
}

到這裏,Window 在應用進程的更新邏輯就已經分析完了,在 WMS 部分的邏輯將在 WMS 分析中介紹。


3. Window 的刪除過程

WindowManagerImpl

// WindowManagerImpl.class
public void removeView(View view) {
    mGlobal.removeView(view, false);
}

WindowManagerGlobal

public void removeView(View view, boolean immediate) {
    if (view == null) {
        throw new IllegalArgumentException("view must not be null");
    }

    synchronized (mLock) {
        int index = findViewLocked(view, true);
        View curView = mRoots.get(index).getView();
        removeViewLocked(index, immediate);
        if (curView == view) {
            return;
        }
        throw new IllegalStateException("Calling with view " + view
                + " but the ViewAncestor is attached to " + curView);
    }
}

private void removeViewLocked(int index, boolean immediate) {
    ViewRootImpl root = mRoots.get(index);
    View view = root.getView();

    if (view != null) {
        InputMethodManager imm = view.getContext().getSystemService(InputMethodManager.class);
        if (imm != null) {
            imm.windowDismissed(mViews.get(index).getWindowToken());
        }
    }
    // 這裏執行了 ViewRootImpl.die() 邏輯。
    boolean deferred = root.die(immediate);
    if (view != null) {
        view.assignParent(null);
        if (deferred) {
            mDyingViews.add(view);
        }
    }
}

ViewRootImpl

// ViewRootImpl.class
boolean die(boolean immediate) {
    // Make sure we do execute immediately if we are in the middle of a traversal or the damage
    // done by dispatchDetachedFromWindow will cause havoc on return.
    if (immediate && !mIsInTraversal) {
        doDie();
        return false;
    }
    // ...省略代碼...
    
    // 這裏使用Handler發消息,其實也是調用了 doDie() 方法。
    mHandler.sendEmptyMessage(MSG_DIE);
    return true;
}

void doDie() {
	// ...省略代碼...
    synchronized (this) {
    	// 使用 mRemoved 標記位,防止進行重複移除。
        if (mRemoved) {
            return;
        }
        mRemoved = true;
        if (mAdded) {
        	// 1.將 View 從 Window 中移除。
            dispatchDetachedFromWindow();
        }

        if (mAdded && !mFirst) {
            destroyHardwareRenderer();

            if (mView != null) {
                int viewVisibility = mView.getVisibility();
                boolean viewVisibilityChanged = mViewVisibility != viewVisibility;
                if (mWindowAttributesChanged || viewVisibilityChanged) {
                    // If layout params have been changed, first give them
                    // to the window manager to make sure it has the correct
                    // animation info.
                    try {
                    	// 2.這個方法內部會與 WMS 進行通信。
                        if ((relayoutWindow(mWindowAttributes, viewVisibility, false)
                                & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
                            // 3.通過 IWindowSession 與 WMS 通信。
                            mWindowSession.finishDrawing(mWindow);
                        }
                    } catch (RemoteException e) {
                    }
                }
				// 4.釋放 Surface 資源。
                destroySurface();
            }
        }

        mAdded = false;
    }
    // 5.將 ViewRootImpl 從 WindowManagerGlobal 中移除。
    WindowManagerGlobal.getInstance().doRemoveView(this);
}

void dispatchDetachedFromWindow() {
    // ...省略代碼...
    try {
    	// 通過 IWindowSession 調用遠程 WMS 移除 Window。
        mWindowSession.remove(mWindow);
    }
    // ...省略代碼...
    unscheduleTraversals();
}

void unscheduleTraversals() {
    if (mTraversalScheduled) {
        mTraversalScheduled = false;
        mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
        // 這一段代碼與 Choreographer.postCallback() 方法類似(參考本文 "scheduleTraversals 執行流程" 部分的分析)。
        mChoreographer.removeCallbacks(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
    }
}

WindowManagerGlobal

void doRemoveView(ViewRootImpl root) {
    synchronized (mLock) {
        final int index = mRoots.indexOf(root);
        if (index >= 0) {
        	// 移除 root 相關的信息。
            mRoots.remove(index);
            mParams.remove(index);
            final View view = mViews.remove(index);
            mDyingViews.remove(view);
        }
    }
    if (ThreadedRenderer.sTrimForeground && ThreadedRenderer.isAvailable()) {
        doTrimForeground();
    }
}

到這裏,Window 在應用進程的移除邏輯就已經分析完了,在 WMS 部分的邏輯將在 WMS 分析中介紹。


4. IWindowSession 對象的獲取流程

IWindowSession 是一個 Binder 對象,用於進行進程間通信。IWindowSession 是 Client 端的代理 (運行在應用進程),它的 Server端的實現爲 Session (運行在 WMS 所在進程)。

時序圖:
在這裏插入圖片描述

代碼流程分析:

// WindowManagerGlobal.class
public static IWindowSession getWindowSession() {
    synchronized (WindowManagerGlobal.class) {
        if (sWindowSession == null) {
            try {
                InputMethodManager.ensureDefaultInstanceForDefaultDisplayIfNecessary();
                // 1.獲取 WMS 遠程代理。
                IWindowManager windowManager = getWindowManagerService();
                // 2.通過遠程代理類獲取WindowSession遠程代理。
                sWindowSession = windowManager.openSession(
                        new IWindowSessionCallback.Stub() {
                            @Override
                            public void onAnimatorScaleChanged(float scale) {
                                ValueAnimator.setDurationScale(scale);
                            }
                        });
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        }
        return sWindowSession;
    }
}

// WindowManagerGlobal.class
public static IWindowManager getWindowManagerService() {
    synchronized (WindowManagerGlobal.class) {
        if (sWindowManagerService == null) {
        	// 通過AIDL方式獲取 WMS 的遠程代理(WMS是在SystemServer.startOtherServices()中啓動的)。
            sWindowManagerService = IWindowManager.Stub.asInterface(ServiceManager.getService("window"));
           // ...省略代碼...
        }
        return sWindowManagerService;
    }
}

// WindowManagerService.class
public IWindowSession openSession(IWindowSessionCallback callback) {
    return new Session(this, callback);
}

// Session.class
public Session(WindowManagerService service, IWindowSessionCallback callback) {
    mService = service;
    mCallback = callback;
    // ...省略代碼...
    
    // 構建一個Session,每個應用都對應一個唯一的Session,系統服務就是通過Session來區分不同的應用的。
    StringBuilder sb = new StringBuilder();
    sb.append("Session{");
    sb.append(Integer.toHexString(System.identityHashCode(this)));
    sb.append(" ");
    sb.append(mPid);
    if (mUid < Process.FIRST_APPLICATION_UID) {
        sb.append(":");
        sb.append(mUid);
    } else {
        sb.append(":u");
        sb.append(UserHandle.getUserId(mUid));
        sb.append('a');
        sb.append(UserHandle.getAppId(mUid));
    }
    sb.append("}");
    mStringName = sb.toString();

    try {
        mCallback.asBinder().linkToDeath(this, 0);
    } catch (RemoteException e) {
        // The caller has died, so we can just forget about this.
        // Hmmm, should we call killSessionLocked()??
    }
}

Session 的作用: 每個應用都對應一個唯一的Session,系統服務就是通過Session來區分不同的應用的。


5. ViewRootImpl.scheduleTraversals() 執行流程

方法調用流程: scheduleTraversals() --> TraversalRunnable.run() --> doTraversal() --> performTraversals()

performTraversals() 方法中,會觸發 View 的 measure、layout、draw 三個過程,如下圖所示 (圖片來源於網絡):

在這裏插入圖片描述

代碼流程分析:

ViewRootImpl

// ViewRootImpl.class
void scheduleTraversals() {
    if (!mTraversalScheduled) {
        mTraversalScheduled = true;
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        // postCallback 添加回調,這個添加的回調將在下一幀被渲染時執行。
        // Choreographer 用了接收顯示系統的 VSync 信號。
        mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        if (!mUnbufferedInputDispatch) {
            scheduleConsumeBatchedInput();
        }
        notifyRendererOfFramePending();
        pokeDrawLockIfNeeded();
    }
}

final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
final class TraversalRunnable implements Runnable {
    @Override
    public void run() {
    	// 執行操作
        doTraversal();
    }
}

void doTraversal() {
    if (mTraversalScheduled) {
        mTraversalScheduled = false;
        mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

        if (mProfile) {
            Debug.startMethodTracing("ViewAncestor");
        }
		// 執行操作
        performTraversals();

        if (mProfile) {
            Debug.stopMethodTracing();
            mProfile = false;
        }
    }
}

public final WindowManager.LayoutParams mWindowAttributes = new WindowManager.LayoutParams();

private void performTraversals() {
	// ...省略代碼...
    WindowManager.LayoutParams lp = mWindowAttributes;
    // ...省略代碼...

    if (mFirst || windowShouldResize || insetsChanged ||
            viewVisibilityChanged || params != null || mForceNextWindowRelayout) {
        // ...省略代碼...
        try {
            // ...省略代碼...
            // 1.觸發 WMS 的 Window 更新操作。
            relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
            // ...省略代碼...
        }
        if (!mStopped || mReportNextDraw) {
            if (條件判斷) {
                int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
                int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
                // ...省略代碼...

                // 2.從根 View 開始測量操作
                performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);

				// ...省略代碼...
                // 如果WindowManager.LayoutParams有權重,需進行二次測量。
                boolean measureAgain = false;
                // ...省略代碼...
                if (measureAgain) {
                    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
                }
                layoutRequested = true;
            }
        }
    }
	// ...省略代碼...
    final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
    if (didLayout) {
     	// 3.從根 View 開始計算 View 的位置。
        performLayout(lp, mWidth, mHeight);
        
		// ...省略代碼...
    }
    // ...省略代碼...
    boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() || !isViewVisible;
    if (!cancelDraw) {
		// ...省略代碼...
		
		// 4.從根 View 開始繪製 View。
        performDraw();
    } else {
        // ...省略代碼...
    }
    mIsInTraversal = false;
}

private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
            boolean insetsPending) throws RemoteException {
    // ...省略代碼...
	// 通過 IWindowSession 與遠程服務進行通信。
    int relayoutResult = mWindowSession.relayout(mWindow, mSeq, params,
            (int) (mView.getMeasuredWidth() * appScale + 0.5f),
            (int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility,
            insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,
            mTmpFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
            mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame, mPendingDisplayCutout,
            mPendingMergedConfiguration, mSurfaceControl, mTempInsets);
            
    // ...省略代碼...
    return relayoutResult;
}

private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
	// ...省略代碼...
	// mView 是 DecorView,從根 View 開始測量 View 的大小。
	mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}

private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
            int desiredWindowHeight) {
    // ...省略代碼...
    
    final View host = mView;
    // host 是 DecorView,從根 View 開始計算 View 的位置。
    host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
	// ...省略代碼...
}

private void performDraw() {
    // ...省略代碼...
    try {
        boolean canUseAsync = draw(fullRedrawNeeded);
        // ...省略代碼...
    } 
    // ...省略代碼...
}

private boolean draw(boolean fullRedrawNeeded) {
    Surface surface = mSurface;
    // ...省略代碼...
    
    if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) {
        if (條件判斷) {
            // ...省略代碼...
        } else {
            // ...省略代碼...
            // 執行 View 的繪製邏輯。
            if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset,
                    scalingRequired, dirty, surfaceInsets)) {
                return false;
            }
        }
    }
	// ...省略代碼...
    return useAsyncReport;
}

private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
            boolean scalingRequired, Rect dirty, Rect surfaceInsets) {
    // Draw with software renderer.
    final Canvas canvas;

    // ...省略代碼...
    try {
        // ...省略代碼...
        
        // 1.從 Surface 中獲取畫布,其實真正的視圖是渲染在 Surface 上的。
        canvas = mSurface.lockCanvas(dirty);
        // TODO: Do this in native
        canvas.setDensity(mDensity);
    }
	// ...省略代碼...
    try {
        // ...省略代碼...
		// 2.mView是 DecorView,從根 View 開始繪製。
        mView.draw(canvas);
        drawAccessibilityFocusedDrawableIfNeeded(canvas);
    }
    // ...省略代碼...
    return true;
}

小結:

  1. performTraversals() 方法中,會觸發 View 的 measure、layout、draw 三個過程。
  2. relayoutWindow() 方法會觸發 WMS 的 Window 操作。

五、不同類型窗口的操作

Window 的類型主要分爲三種:Application Window(應用程序窗口)Sub Window (子窗口)System Window (系統窗口)。而不同的窗口類型,它的操作流程也有一些不同。下面分析一下系統窗口和應用窗口的添加過程。

1. 系統窗口的添加過程

我們以 StatusBar 爲例來分析系統窗口的添加過程。

// com.android.systemui.statusbar.phone.StatusBar.class
// 這是將 StatusBar 添加到 Window 的方法
public void createAndAddWindows(@Nullable RegisterStatusBarResult result) {
	// 1.創建一個 StatusBarView
    makeStatusBarView(result);
    // Dependency.get() 的操作和前面的 mContext.getSystemService(Context.WINDOW_SERVICE) 有點類似,
    // 都是預先在一個服務裏面註冊,然後在使用的地方通過指定類型來獲取的。
    mStatusBarWindowController = Dependency.get(StatusBarWindowController.class);
    // 2.將 StatusBar 和它的高度值傳給 StatusBarWindowController控制器(MVC模式)
    mStatusBarWindowController.add(mStatusBarWindow, getStatusBarHeight());
}

StatusBarWindowController

// StatusBarWindowController.class
public void add(ViewGroup statusBarView, int barHeight) {
	// 1.創建一個窗口的 LayoutParams 參數
    mLp = new WindowManager.LayoutParams(
            ViewGroup.LayoutParams.MATCH_PARENT,
            barHeight, //指定的高度值
            WindowManager.LayoutParams.TYPE_STATUS_BAR, //這裏指定窗口的類型爲StatusBar
            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                    | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
                    | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
                    | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
                    | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
            PixelFormat.TRANSLUCENT);
    mLp.token = new Binder();
    mLp.gravity = Gravity.TOP;
    mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
    mLp.setTitle("StatusBar");
    mLp.packageName = mContext.getPackageName();
    mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
    mStatusBarView = statusBarView;
    mBarHeight = barHeight;
    // 2.將創建的StatusBar 通過 WindowManager添加到 Window 中。
    mWindowManager.addView(mStatusBarView, mLp);
    mLpChanged.copyFrom(mLp);
    onThemeChanged();
}

代碼中第2步,調用 WindowManager.addView() 方法來添加系統的 Window,這個過程在上面已經分析過了,此處不再贅述。


2. Activity 的添加過程

在應用程序中,Activity 是最爲常見,我們以 Activity 爲例來分析應用窗口的添加過程。

當界面要與用戶進行交互時,會調用 ActivityThread. handleResumeActivity() 方法。

ActivityThread

// ActivityThread.class
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward, String reason) {
	// ...省略代碼...

    // TODO Push resumeArgs into the activity for consideration
    // 1.將 Activity 回覆到 RESUME 狀態。
    final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
	
	// ...省略代碼...
    final Activity a = r.activity;
	// ...省略代碼...

    if (r.window == null && !a.mFinished && willBeVisible) {
    	// 2.獲取在 Activity.attach() 方法中就創建了 PhoneWindow 對象。
        r.window = r.activity.getWindow();
        View decor = r.window.getDecorView();
        // 這裏使 Decor 不可見。
        decor.setVisibility(View.INVISIBLE);
        // 3.獲取 Activity 中持有的 WindowManager。
        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) {
            	//將 Activity.WindowAdded 標記爲true,避免在 Activity.makeVisible() 是重複進行 Window 添加操作。
                a.mWindowAdded = true;
                // 將根 View(DecorView)通過 WindowManager 添加到 Window 中。
                wm.addView(decor, l);
            } else {
                a.onWindowAttributesChanged(l);
            }
        }
    }
	// ...省略代碼...
	
	// The window is now visible if it has been added, we are not
    // simply finishing, and we are not starting another activity.
    if (!r.activity.mFinished && willBeVisible && r.activity.mDecor != null && !r.hideForNow) {
        // ...省略代碼...

        r.activity.mVisibleFromServer = true;
        mNumVisibleActivities++;
        if (r.activity.mVisibleFromClient) {
        	// 這個方法內部會是 DecorView 可見。
            r.activity.makeVisible();
        }
    }
}

public ActivityClientRecord performResumeActivity(IBinder token, boolean finalStateRequest, String reason) {
    // 1.每一個 ActivityClientRecord 都代表着一個 Activity 。
    final ActivityClientRecord r = mActivities.get(token);
    
    // ...省略代碼...
    try {
        r.activity.onStateNotSaved();
        r.activity.mFragments.noteStateNotSaved();
        checkAndBlockForNetworkAccess();
        if (r.pendingIntents != null) {
        	// 這裏會觸發 Activity.onNewIntent()方法。
            deliverNewIntents(r, r.pendingIntents);
            r.pendingIntents = null;
        }
        if (r.pendingResults != null) {
        	// 這裏會觸發 Activity.onActivityResult()方法。
            deliverResults(r, r.pendingResults, reason);
            r.pendingResults = null;
        }
        // 這裏會觸發 Activity.onResume()方法。
        r.activity.performResume(r.startsNotResumed, reason);

        r.state = null;
        r.persistentState = null;
        // 這裏將當前 Activity 的生命週期狀態設置爲 ON_RESUME。
        r.setState(ON_RESUME);

        reportTopResumedActivityChanged(r, r.isTopResumedActivity, "topWhenResuming");
    } catch (Exception e) {
        // ...省略代碼...
    }
    return r;
}

Activity

// Activity.class
void makeVisible() {
    if (!mWindowAdded) {
        ViewManager wm = getWindowManager();
        wm.addView(mDecor, getWindow().getAttributes());
        mWindowAdded = true;
    }
    // 將 DecorView設置爲可見。
    mDecor.setVisibility(View.VISIBLE);
}

在 Activity 執行 Resume 生命週期時,也會觸發 Window 的添加操作。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章