WindowManager調用流程源碼分析

前兩天寫Activity啓動流程時挖個坑,ActivityThread調用Activity.resume後,緊接着調用WindowManager.addView()用來正在的顯示View,以前講的很草率,如今感受有必要寫一下WindowManager的調用流程。android

本文基於android8.1.0_r15分支分析緩存

WindowManager簡單介紹bash

首先WindowManager是一個接口繼承自ViewManager,它做用是用於窗口的管理,咱們平時的使用比較多的是addView方法和LayoutParams參數,addView定義在基類ViewManager中,源碼:session

ViewManagerapp

public interface ViewManager
{
    public void addView(View view, ViewGroup.LayoutParams params);
    public void updateViewLayout(View view, ViewGroup.LayoutParams params);
    public void removeView(View view);
}
複製代碼

一共三個方法比較簡單,注意看參數類型,咱們平時用WindowManager時候傳的是WindowManager.LayoutParams,這裏定義的是ViewGroup.LayoutParams params,我想確定前者繼承後者吧,啥都不說,看看WindowManager.LayoutParams定義;ide

果不其然,LayoutParams中還定義了一個十分重要的熟悉: type,type表明窗口的類型,系統一共提供三種類型的窗口

  • 應用程序窗口 :頂級窗口
  • 子窗口 :與一個頂級窗口關係的子窗口
  • 系統窗口 :系統窗口,好比toast

經常使用的type值ui

WindowManager畢竟只是一個接口,真正的實如今哪裏?回想在ActivityThread.handleResumeActivity()中,調用了activity.getWindowManager(),咱們定位到Activity:this

Activity#getWindowManagerspa

public WindowManager getWindowManager() {
    return mWindowManager;
}
複製代碼

在Activity#getSystemService()中一樣能夠獲得代理

@Override
 public Object getSystemService(@ServiceName @NonNull String name) {
     if (getBaseContext() == null) {
         throw new IllegalStateException(
                 "System services not available to Activities before onCreate()");
     }
    //WINDOW_SERVICE比較特殊,不調用super的
     if (WINDOW_SERVICE.equals(name)) {
         return mWindowManager;
     } else if (SEARCH_SERVICE.equals(name)) {
         ensureSearchManager();
         return mSearchManager;
     }
     return super.getSystemService(name);
 }
複製代碼

咱們注意到在調用getSystemService時,WINDOW_SERVICE比較特殊,沒有調用super,直接返回mWindowManager,爲何呢,咱們留到後文說;

mWindowManager何時賦值的呢,咱們在Activity.attach中找到了代碼

Activity.attach

//設置系統的WindowManager
 mWindow.setWindowManager(
        //獲取系統的WindowManager
         (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
         mToken, mComponent.flattenToString(),
         (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
 if (mParent != null) {
     mWindow.setContainer(mParent.getWindow());
 }
 //拿Window對象的
 mWindowManager = mWindow.getWindowManager();

複製代碼

也是直接獲取系統的,可是通過mWindow轉手了,這是爲何呢,Window關於setWindowManager()定義

Window#setWindowManager

public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
        boolean hardwareAccelerated) {
    mAppToken = appToken;
    mAppName = appName;
    mHardwareAccelerated = hardwareAccelerated
            || SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
    if (wm == null) {
        wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
    }
    mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}
複製代碼

正在的實現是WindowManagerImpl,咱們查看WindowManagerImpl.addView方法:

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

 @Override
 public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
     applyDefaultToken(params);
     mGlobal.updateViewLayout(view, params);
 }
 
private void applyDefaultToken(@NonNull ViewGroup.LayoutParams params) {
 if (mDefaultToken != null && mParentWindow == null) {
 if (!(params instanceof WindowManager.LayoutParams)) {
         throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
     }
     
     final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
     if (wparams.token == null) {
         wparams.token = mDefaultToken;
     }
 }
}
複製代碼

方法體首先執行applyDefaultToken,對wparams賦token值,而後直接調用mGlobal對應的方法,mGlobal是WindowManagerGlobal類型,它也繼承自WindowManager麼?接下來分析WindowManagerGlobal

WindowManagerGlobal

WindowManagerGlobal並非繼承WindowManager,可是有着同樣的方法名,這不是代理模式麼,它一樣有addView、updateViewLayout、removeView方法,咱們從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");
    }
    //轉成WindowManager.LayoutParams對象
    final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
    //顯示Activity的話window確定不爲空
    if (parentWindow != null) {
        parentWindow.adjustLayoutParamsForSubWindow(wparams);
    } else {
        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);
        }
        //從mViews集合查找有沒有添加過
        int index = findViewLocked(view, false);
        //有則調用對應的ViewRootImpl.doDie
        if (index >= 0) {
            if (mDyingViews.contains(view)) {
                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);
        //參數最終給了decor
        view.setLayoutParams(wparams);
        mViews.add(view);//把Decor存起來
        mRoots.add(root);//把ViewRootImpl純起來
        mParams.add(wparams);//把參數存起來
        // do this last because it fires off messages to start doing things
        try {
            //設置View
            root.setView(view, wparams, panelParentView);
        } catch (RuntimeException e) {
            // BadTokenException or InvalidDisplayException, clean up.
            if (index >= 0) {
                removeViewLocked(index, true);
            }
            throw e;
        }
    }
}
複製代碼

簡單分析addView做用:

  1. WindowManagerGlobal中定義了三個List,分別存放傳遞進來的View、params和生成的ViewRootImpl
  2. 若是判斷已經添加過,則調用該view對應的ViewRootImpl.doDie()
  3. 最後執行了root.setView();

繼續分析updateViewLayout和removeView方法:

WindowManagerGlobal#updateViewLayout#removeView

public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
    if (view == null) {
        throw new IllegalArgumentException("view must not be null");
    }
    if (!(params instanceof WindowManager.LayoutParams)) {
        throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
    }
    final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
    view.setLayoutParams(wparams);
    synchronized (mLock) {
        //去除以前的view對應的下標
        int index = findViewLocked(view, true);
        //緩存的root
        ViewRootImpl root = mRoots.get(index);
        //mParams集合從新添加
        mParams.remove(index);
        mParams.add(index, wparams);
        //最終調用viewRootImpl的setLayoutParams
        root.setLayoutParams(wparams, false);
    }
}

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 = InputMethodManager.getInstance();
        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的對應代碼

調用順序總結:

  1. Activity.getWindowManger()獲取,返回WindowManagerImpl;
  2. WindowManagerImpl調用WindowManagerGlobal對應方法;
  3. WindowManagerGlobal調用ViewRootImp對應方法;

意義總結:

  1. WindowManagerImpl做爲WindowManager接口的默認實現,順便處理WindowManager.LayoutParams.token
  2. WindowManagerGlobal保存每一次的View、Params、root,防止重複添加,最後調用RootViewImpl實現。

到這裏尚未真正的看到IPC調用,由於一層一層代理,到了RootViewImpl這一層,真假美猴王應該能分曉了吧。

RootViewImpl調用IPC

上文分析RootViewImpl在WindowManagerGlobal.addView中構造,而且有三個方法setView()、setLayoutParams()、doDie()被調用,可是看了RootViewImpl代碼頭髮有些發麻,代碼量太大了。

因爲鄙人水平有些,這篇博客只拿出它和WMS的IPC調用過程分析,這代碼只能一片一片剖析。見諒啊!開始吧。

RootViewImpl構造方法

咱們發現RootViewImpl構造方法建立了兩個window相關的對象,分別是

mWindowSession = WindowManagerGlobal.getWindowSession();

mWindow = new W(this);
複製代碼

跟進WindowManagerGlobal看個究竟

WindowManagerGlobal#getWindowSession#getWindowManagerService

//獲取WindowSession
public static IWindowSession getWindowSession() {
    synchronized (WindowManagerGlobal.class) {
        if (sWindowSession == null) {
            try {
                InputMethodManager imm = InputMethodManager.getInstance();
                IWindowManager windowManager = getWindowManagerService();
                //通知WMS開啓一個新的Session
                sWindowSession = windowManager.openSession(
                        new IWindowSessionCallback.Stub() {
                            @Override
                            public void onAnimatorScaleChanged(float scale) {
                                ValueAnimator.setDurationScale(scale);
                            }
                        },
                        imm.getClient(), imm.getInputContext());
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        }
        return sWindowSession;
    }
}

//獲取WindowManagerService
public static IWindowManager getWindowManagerService() {
    synchronized (WindowManagerGlobal.class) {
        if (sWindowManagerService == null) {
            sWindowManagerService = IWindowManager.Stub.asInterface(
                    ServiceManager.getService("window"));
            try {
                if (sWindowManagerService != null) {
                    ValueAnimator.setDurationScale(
                            sWindowManagerService.getCurrentAnimatorScale());
                }
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        }
        return sWindowManagerService;
    }
}
複製代碼

再看W這個類,它繼承自IWindow.Stub,若是看過Activity啓動流程的應該知道,這個W和ApplicationThread同樣的道理啊,咱們接着看ViewRootImpl關鍵是的三個方法有沒有mWindowSession和mWindow的調用

ViewRootImpl#setView

res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
        getHostVisibility(), mDisplay.getDisplayId(),
        mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
        mAttachInfo.mOutsets, mInputChannel);
複製代碼

ViewRootImpl#doDie

mWindowSession.finishDrawing(mWindow);
複製代碼

IWindowSession.aild

interface IWindowSession {
    int add(IWindow window, int seq, in WindowManager.LayoutParams attrs,
            in int viewVisibility, out Rect outContentInsets, out Rect outStableInsets,
            out InputChannel outInputChannel);
    int addToDisplay(IWindow window, int seq, in WindowManager.LayoutParams attrs,
            in int viewVisibility, in int layerStackId, out Rect outContentInsets,
            out Rect outStableInsets, out Rect outOutsets, out InputChannel outInputChannel);
    int addWithoutInputChannel(IWindow window, int seq, in WindowManager.LayoutParams attrs,
            in int viewVisibility, out Rect outContentInsets, out Rect outStableInsets);
    int addToDisplayWithoutInputChannel(IWindow window, int seq, in WindowManager.LayoutParams attrs,
            in int viewVisibility, in int layerStackId, out Rect outContentInsets,
            out Rect outStableInsets);
    void remove(IWindow window);
    
    ...
}
複製代碼

IWindow.aidl

oneway interface IWindow {
    void executeCommand(String command, String parameters, in ParcelFileDescriptor descriptor);
    void resized(in Rect frame, in Rect overscanInsets, in Rect contentInsets,
            in Rect visibleInsets, in Rect stableInsets, in Rect outsets, boolean reportDraw,
            in MergedConfiguration newMergedConfiguration, in Rect backDropFrame,
            boolean forceLayout, boolean alwaysConsumeNavBar, int displayId);
    void moved(int newX, int newY);
    void dispatchAppVisibility(boolean visible);
    void dispatchGetNewSurface();
    void windowFocusChanged(boolean hasFocus, boolean inTouchMode);
    void closeSystemDialogs(String reason);
    ...
}
複製代碼

Session#addToDisplay

@Override
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
        int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
        Rect outOutsets, InputChannel outInputChannel) {
    //調用WindowManagerService的addWindow
    return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
            outContentInsets, outStableInsets, outOutsets, outInputChannel);
}
複製代碼

WindowManagerService#addWindow

public int addWindow(Session session, IWindow client, int seq,
       WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
       Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
       InputChannel outInputChannel) {
   int[] appOp = new int[1];
   int res = mPolicy.checkAddPermission(attrs, appOp);
   if (res != WindowManagerGlobal.ADD_OKAY) {
       return res;
   }
   boolean reportNewConfig = false;
   WindowState parentWindow = null;
   long origId;
   final int callingUid = Binder.getCallingUid();
   final int type = attrs.type;
   synchronized(mWindowMap) {
       if (!mDisplayReady) {
           throw new IllegalStateException("Display has not been initialialized");
       }
       
   ...
   }
}

複製代碼

既然涉及到binder雙向通訊,app端確定得有handle來處理,果真在ViewRootImpl中定義了ViewRootHandler這個類。

ViewRootHandler

final class ViewRootHandler extends Handler {

    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
        case MSG_INVALIDATE:
            ((View) msg.obj).invalidate();
            break;
        case MSG_INVALIDATE_RECT:
            final View.AttachInfo.InvalidateInfo info = (View.AttachInfo.InvalidateInfo) msg.obj;
            info.target.invalidate(info.left, info.top, info.right, info.bottom);
            info.recycle();
            break;
        case MSG_PROCESS_INPUT_EVENTS:
            mProcessInputEventsScheduled = false;
            doProcessInputEvents();
            break;
        case MSG_DISPATCH_APP_VISIBILITY:
            handleAppVisibility(msg.arg1 != 0);
            break;
        case MSG_DISPATCH_GET_NEW_SURFACE:
            handleGetNewSurface();
            break;
        case MSG_RESIZED: {
            // Recycled in the fall through...
            SomeArgs args = (SomeArgs) msg.obj;
            if (mWinFrame.equals(args.arg1)
                    && mPendingOverscanInsets.equals(args.arg5)
                    && mPendingContentInsets.equals(args.arg2)
                    && mPendingStableInsets.equals(args.arg6)
                    && mPendingVisibleInsets.equals(args.arg3)
                    && mPendingOutsets.equals(args.arg7)
                    && mPendingBackDropFrame.equals(args.arg8)
                    && args.arg4 == null
                    && args.argi1 == 0
                    && mDisplay.getDisplayId() == args.argi3) {
                break;
            }
            }
           ... 
   }
}

複製代碼

畫一張圖標識記憶一下吧。

相關文章
相關標籤/搜索