1. 程式人生 > >【原始碼學習】window 新增 view

【原始碼學習】window 新增 view

此類文章主要是用來記錄學習原始碼的過程,更多的參考別人的分析過程自己去追蹤原始碼,然後做下的記錄。看 Android 原始碼是一個痛苦的過程,之前幾次嘗試都以失敗而告終,這裡把這個過程記錄下來,算是對自己的一種激勵。

下面的所有原始碼都是基於Android_7.1.1而來。

View 的新增過程

可以知道的是 Android 中的所有檢視都是通過 Window 來呈現的,Window 是 View 的管理者。

如果做過懸浮窗的話一定對下面這個程式碼不陌生

windowManager.addView(layout,params);

可以看到向 Window 新增 View 是通過 WindowManager 來實現的。那麼就從 WindowManager 作為入口來進行研究。

WindowManager

WindowManager 是一個介面,並且繼承自 ViewManager。關於 ViewManager

public interface ViewManager{
    public void addView(View view, ViewGroup.LayoutParams params);
    public void updateViewLayout(View view, ViewGroup.LayoutParams params);
    public void removeView(View vaiew);
}

ViewManager只有三個方法,新增、更新、刪除 View。

WindowManager 也是一個介面,它的實現類是 WindowManagerImpl(這裡可以通過activity.getWindowManager()追蹤到)。檢視 WindowManagerImpl:

public final class WindowManagerImpl implements WindowManager {
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();

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

    @Override
public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) { applyDefaultToken(params); mGlobal.updateViewLayout(view, params); } @Override public void removeView(View view) { mGlobal.removeView(view, false); } }

可以看到 WindowManagerImpl 也沒有真正的實現檢視的新增、更新、刪除,而是交給了 WindowManagerGlobal 來處理,繼續追蹤。找到 WindowManagerGlobal.addView()

WindowManagerGlobal.addView()

下面是 WindowManagerGlobal.addView() 的部分程式碼:

public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        //  首先檢查合法性
        if (view == null) {
            throw new IllegalArgumentException("view must not be null");
        }
        if (display == null) {
            throw new IllegalArgumentException("display must not be null");
        }
        if (!(params instanceof WindowManager.LayoutParams)) {
            throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
        }

        // 檢查父級 View 是否存在
        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
        if (parentWindow != null) {
            // adjustLayoutParamsForSubWindow 從字面意思是調整窗口布局引數
            parentWindow.adjustLayoutParamsForSubWindow(wparams);
        } else {
            // If there's no parent, then hardware acceleration for this view is
            // set from the application's hardware acceleration setting.
            final Context context = view.getContext();
            if (context != null
                    && (context.getApplicationInfo().flags
                            & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
                wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
            }
        }

        ViewRootImpl root;
        View panelParentView = null;

        synchronized (mLock) {
            // Start watching for system property changes.
            if (mSystemPropertyUpdater == null) {
                mSystemPropertyUpdater = new Runnable() {
                    @Override public void run() {
                        synchronized (mLock) {
                            for (int i = mRoots.size() - 1; i >= 0; --i) {
                                mRoots.get(i).loadSystemProperties();
                            }
                        }
                    }
                };
                SystemProperties.addChangeCallback(mSystemPropertyUpdater);
            }

            // 檢測是否已經存在這個 view
            int index = findViewLocked(view, false);
            if (index >= 0) {
                // 如果已經存在但是是快要移除的 view 就 移除它
                if (mDyingViews.contains(view)) {
                    // Don't wait for MSG_DIE to make it's way through root's queue.
                    mRoots.get(index).doDie();
                } else {
                    // 否則丟擲異常
                    throw new IllegalStateException("View " + view
                            + " has already been added to the window manager.");
                }
                // The previous removeView() had not completed executing. Now it has.
            }

            // If this is a panel window, then find the window it is being
            // attached to for future reference.
            if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
                    wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
                final int count = mViews.size();
                for (int i = 0; i < count; i++) {
                    if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
                        panelParentView = mViews.get(i);
                    }
                }
            }

            // 建立 ViewRootImpl
            root = new ViewRootImpl(view.getContext(), display);
            view.setLayoutParams(wparams);
            // 這裡的 mViews 是儲存所有 Window 所對應的 view
            mViews.add(view);
            // mRoots 是儲存所有 window 對應的 ViewRootImpl
            mRoots.add(root);
            // mParams 儲存所有 window 對應的佈局引數
            mParams.add(wparams);
        }

        // do this last because it fires off messages to start doing things
        try {
            // **最終是通過 ViewRootImpl 的 setView 方法來新增View**
            root.setView(view, wparams, panelParentView);
        } catch (RuntimeException e) {
            // BadTokenException or InvalidDisplayException, clean up.
            synchronized (mLock) {
                final int index = findViewLocked(view, false);
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
            }
            throw e;
        }
    }

ViewRootImpl.setView()

經過上面的分析可以知道,最終是通過 ViewRootImpl.setView() 方法來新增

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    ...
    // 在新增之前先進行了重新整理
    requestLayout();
    ...
    collectViewAttributes();
    // 通過mWindowSession.addToDisplay 來進行顯示
    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
            getHostVisibility(), mDisplay.getDisplayId(),
            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
            mAttachInfo.mOutsets, mInputChannel);
    ...
}

在 ViewRootImpl 中可以找到 mWindowSession 初始化過程。

IWindowSession mWindowSession = WindowManagerGlobal.getWindowSession();

IWindowSession 是一個 Binder 物件,所以在新增過程中進行了 IPC 呼叫,那麼 mWindowSession 的具體實現是什麼,繼續追蹤程式碼,WindowManagerGlobal.getWindowSession() 方法如下:

public static IWindowSession getWindowSession() {
        synchronized (WindowManagerGlobal.class) {
            ...
            // getWindowManagerService 就是獲取 WindowManagerService
            IWindowManager windowManager = getWindowManagerService();
            sWindowSession = windowManager.openSession()
            ...     
            }
            return sWindowSession;
        }
}

//WindowManagerService.openSession
 @Override
public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client,
        IInputContext inputContext) {
    if (client == null) throw new IllegalArgumentException("null client");
    if (inputContext == null) throw new IllegalArgumentException("null inputContext");
    Session session = new Session(this, callback, client, inputContext);
    return session;
}

可以看到上面的 mWindowSession 物件真正實現是一個 Session。那就可以繼續追蹤到 Session.addToDisplay()


    final WindowManagerService mService;

    @Override
    public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
            int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
            Rect outOutsets, InputChannel outInputChannel) {
        return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
                outContentInsets, outStableInsets, outOutsets, outInputChannel);
    }

通過上面的程式碼可以看到,最終是通過 WindowManagerService.addWindow 來進行新增。至於 WindowManagerService 怎麼處理的暫時不做深入了。

總結

經過上面的一通分析,向 window 新增 view 的過程已經基本清晰:

通過寫文章算是對 window 的新增過程再進行一邊梳理。水平所限可能不夠詳細,僅供參考。

參考:

  • 《Android 開發藝術探索》——任玉剛