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;
            }
            }
           ... 
   }
}

复制代码

画一张图标识记忆一下吧。

相关文章
相关标签/搜索