WMS浅谈

691 阅读8分钟

窗口简述:

什么叫窗口(Window)?在Android世界里,窗口是指屏幕上的一块用于绘制各种UI元素并可以响应用户输入的一个矩形区域。在Android系统中,PopupWindow、Dialog、Activity、Toast等都有窗口的概念,但又各有不同,Android将窗口大致分为三类:应用窗口、子窗口、系统窗口。其中,Activity与Dialog属于应用窗口、PopupWindow属于子窗口,必须依附到其他非子窗口才能存在,而Toast属于系统窗口,Dialog可能比较特殊,从表现上来说偏向于子窗口,必须依附Activity才能存在,但是从性质上来说,仍然是应用窗口。

WMS的创建过程

WMS 是在 SystemServer 进程中启动的,SystemServer 进程是 Android 系统启动的时候初始化的,我们首先来看一下 SystemServer 的入口函数 main()

public final class SystemServer {

    /**
     * The main entry point from zygote.
     */
    public static void main(String[] args) {
        new SystemServer().run();
    }
}

从上述代码可见,是创建了一个 SystemServer 对象并调用了 run() 方法



    private void run() {

        ......
        mSystemServiceManager = new SystemServiceManager(mSystemContext);  // 代码 1

        // Start services.
        try {
            traceBeginAndSlog("StartServices");
            startBootstrapServices();                              // 代码 2
            startCoreServices();                                   // 代码 3
            startOtherServices();                                  // 代码 4
            SystemServerInitThreadPool.shutdown();
        } catch (Throwable ex) {
            Slog.e("System", "******************************************");
            Slog.e("System", "************ Failure starting system services", ex);
            throw ex;
        } finally {
            traceEnd();
        }
    }
  • 代码1处,创建了一个 SystemServiceManager 对象,用于创建各种系统服务并管理他们的生命周期

  • 代码2处,调用 startBootstrapServices() 启动 ActivityManagerService、PackageManagerService 等服务进程

  • 代码3处,调用 startCoreServices() 启动BatteryService、WebViewUpdateService 等服务进程

  • 代码4处,调用 startOtherServices() 启动 WindowManagerService、InputManagerService 等服务进程

startOtherServices() 中,我们分析下其中和 WMS 相关的部分

    private void startOtherServices() {
        final Context context = mSystemContext;
        WindowManagerService wm = null;
        InputManagerService inputManager = null;
        
        ......
        try {
          
            traceBeginAndSlog("StartInputManagerService");
            inputManager = new InputManagerService(context);
            traceEnd();
          
            traceBeginAndSlog("StartWindowManagerService");
            // WMS needs sensor service ready
            ConcurrentUtils.waitForFutureNoInterrupt(mSensorServiceStart, START_SENSOR_SERVICE);
            mSensorServiceStart = null;
              // 代码 1
            wm = WindowManagerService.main(context, inputManager,
                    mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
                    !mFirstBoot, mOnlyCore, new PhoneWindowManager());
            ServiceManager.addService(Context.WINDOW_SERVICE, wm);
            ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
            traceEnd();
            ......
        } catch (RuntimeException e) {
            Slog.e("System", "******************************************");
            Slog.e("System", "************ Failure starting core service", e);
        }
        ......
 
    }

从上面代码中可以看出,在代码1处调用WindowManagerService.main方法创建 WindowManagerService 对象,我们来分析下这个方法

public class WindowManagerService extends IWindowManager.Stub
        implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {

    private static WindowManagerService sInstance;
    static WindowManagerService getInstance() {
        return sInstance;
    }

    public static WindowManagerService main(final Context context, final InputManagerService im,
            final boolean haveInputMethods, final boolean showBootMsgs, final boolean onlyCore,
            WindowManagerPolicy policy) {
        DisplayThread.getHandler().runWithScissors(() ->
                sInstance = new WindowManagerService(context, im, haveInputMethods, showBootMsgs,
                        onlyCore, policy), 0);
        return sInstance;
    }

}
  • 我们看到在 main() 方法中,在 DisplayThread 线程中通过 WMS 的构造方法创建一个 WMS 实例对象

  • DisplayThread 线程是一个系统前台线程,用于执行一些延时要非常小的关于显示的操作,一般只会在 WindowManager、DisplayManager 和 InputManager 中使用。

至此,我们只分析了WMS的创建过程,那么Window显示到屏幕上的过程是怎么样的呢,WMS又是怎么管理窗口的呢?接下我们从Activiy开始分析。

Activity中Window的创建:

Window 和 Activity 有着紧密的联系,一个 Activity 拥有至少一个 Window 对象,在创建 Activity 的时候,也会创建一个 Window 对象,并且 Activity 会实现 Window 的一些回调接口。在 Activity 对象创建完成之后,调用的 Activity 第一个方法并不是 onCreate 相关的方法,而是 attach 方法,在 attach 方法中我们创建了 Activity 对应的 Window 对象。

    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
           
           ....
           
            activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor, window, r.configCallback);
          ....
          
        return 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) {
             ...
        //创建了一个Window对象
        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        mWindow.setWindowControllerCallback(this);
        //设置了Window的Callback 回调,用户的触摸 & 键盘等输入事件就是通过此接口回调到Activity
        mWindow.setCallback(this);
        ...
        //设置WindowManager对象,WindowManager是一个Interface,其实现类是WindowManagerImpl
        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
      ...
    }

View添加到Window

我们在创建好一个 Activity 类,在 Activity 的 onCreate 方法中都会调用 setContentView 方法为 Activity 设置 View

class MainActivity : Activity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }
}

setContentView 的源码如下,其实是调用了 PhoneWindow 的 setContentView 方法

public class PhoneWindow extends Window implements MenuBuilder.Callback { 

    // This is the view in which the window contents are placed. It is either
    // mDecor itself, or a child of mDecor where the contents go.
    ViewGroup mContentParent;

    @Override
    public void setContentView(int layoutResID) {
        // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
        // decor, when theme attributes and the like are crystalized. Do not check the feature
        
        // before this happens.
        if (mContentParent == null) {
           //生成DecorView和contentParent
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();
        }

        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                    getContext());
            transitionTo(newScene);
        } else {
           // 会通过 mLayoutInflater 将传入的 layoutResID 解析并放入mContentParent内
            mLayoutInflater.inflate(layoutResID, mContentParent);
        }
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
        mContentParentExplicitlySet = true;
    }

上面分析了Activity的PhoneWindow、DecorView、mContentParent 的创建过程。接下来分析DecorView如何添加到PhoneWindow

向 PhoneWindow 添加 DecorView

在 ActivityThread 的 handleLaunchActivity 中执行完 create 阶段的方法之后,立即开始调用 handleResumeActivity 方法,开始执行 resume 过程,handleResumeActivity 方法如下



    final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
        ActivityClientRecord r = mActivities.get(token);
        if (!checkAndUpdateLifecycleSeq(seq, r, "resumeActivity")) {
            return;
        }

        // If we are getting ready to gc after going to the background, well
        // we are back active so skip it.
        unscheduleGcIdler();
        mSomeActivitiesChanged = true;

        // 代码 1,调用 performResumeActivity 间接地调用 Activity 的 onResume 方法
        // TODO Push resumeArgs into the activity for consideration
        r = performResumeActivity(token, clearHide, reason);

        if (r != null) {
            final Activity a = r.activity;

            if (localLOGV) Slog.v(
                TAG, "Resume " + r + " started activity: " +
                a.mStartedActivity + ", hideForNow: " + r.hideForNow
                + ", finished: " + a.mFinished);

            final int forwardBit = isForward ?
                    WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;

             ...
            if (r.window == null && !a.mFinished && willBeVisible) {
                r.window = r.activity.getWindow();
                View decor = r.window.getDecorView();
                // 代码 2,在添加 DecorView 到 Window 之前,将 DecorView 设置为不可见的
                decor.setVisibility(View.INVISIBLE);
                ViewManager wm = a.getWindowManager();
                WindowManager.LayoutParams l = r.window.getAttributes();
                a.mDecor = decor;
                // 代码 3,此处很重要,将 WindowManager.LayoutParams.type 属性设置为 TYPE_BASE_APPLICATION = 1,代表此 Window 是一个应用级的窗口,在所有窗口的最低下
                l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
                l.softInputMode |= forwardBit;
                ...
                ```
                 if (a.mVisibleFromClient) {
                    if (!a.mWindowAdded) {
                        a.mWindowAdded = true;
                        // 代码 4,在这儿调用 WindowManager 的 addView 方法,将 DecorView 添加到 Window 中
                        wm.addView(decor, l);
                    } else {
                        // The activity will get a callback for this {@link LayoutParams} change
                        // earlier. However, at that time the decor will not be set (this is set
                        // in this method), so no action will be taken. This call ensures the
                        // callback occurs with the decor set.
                        a.onWindowAttributesChanged(l);
                    }
                }

在代码 4 处,我们可以清楚的看到调用了 WindowManager 的 addView 方法,那我们接着分析,因为 WindowManager 是一个 Interface,其实现类是 WindowManagerImpl,我们来到 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);
    }

    // ......

 }

其实是调用了 WindowManagerGlobal 的 addView 方法,

public final class WindowManagerGlobal {


    public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        
        // ......

        ViewRootImpl root;
        View panelParentView = null;

        synchronized (mLock) {
            
            // ......
            // 代码 1,从 mViews 中找到此 DecorView 的 index 的索引
            int index = findViewLocked(view, false);
            //检查View是否已经被添加,如果有,直接抛出异常
            if (index >= 0) {
                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);
                    }
                }
            }
             
            // 代码 2,新建一个 ViewRootImpl 对象
            root = new ViewRootImpl(view.getContext(), display);

            view.setLayoutParams(wparams);
            
            // 代码 3,将 DecorView、ViewRootImple、WindowManager.LayoutParams 添加到对应的 List 中
            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);
        
            // 代码 4 调用 ViewRootImpl 的 setView 方法,开始执行 DecorView 的绘制过程
            // do this last because it fires off messages to start doing things
            try {
                root.setView(view, wparams, panelParentView);
            } catch (RuntimeException e) {
                // BadTokenException or InvalidDisplayException, clean up.
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
                throw e;
            }
        }
    }

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

DecorView的绘制流程

从上面的代码4处调用了ViewRootImpl 的 setView 方法,ViewRootImpl这个类非常重要,这个类在android的UI结构中扮演的是一个中间者的角色,连接的是PhoneWindow 和WindowManagerService。它的主要作用有两个:

1.向DecorView分发收到的用户发起的event事件,如按键,触屏等事件;

2.与WindowManagerService交互,完成整个Activity的GUI的绘制。

ViewRootImpl 的 setView 方法如下

public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {

    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {

                ......

                int res; /* = WindowManagerImpl.ADD_OKAY; */

                // Schedule the first layout -before- adding to the window
                // manager, to make sure we do the relayout before receiving
                // any other events from the system.
                requestLayout();
                if ((mWindowAttributes.inputFeatures
                        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                    mInputChannel = new InputChannel();
                }
                mForceDecorViewVisibility = (mWindowAttributes.privateFlags
                        & PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY) != 0;
                try {
                    mOrigWindowType = mWindowAttributes.type;
                    mAttachInfo.mRecomputeGlobalAttributes = true;
                    collectViewAttributes();
                    // 代码 1
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mInputChannel);
                } catch (RemoteException e) {
                    mAdded = false;
                    mView = null;
                    mAttachInfo.mRootView = null;
                    mInputChannel = null;
                    mFallbackEventHandler.setView(null);
                    unscheduleTraversals();
                    setAccessibilityFocus(null, null);
                    throw new RuntimeException("Adding window failed", e);
                } finally {
                    if (restore) {
                        attrs.restore();
                    }
                }

                ......

            }
        }
    }
}

在代码1处调用了mWindowSession.addToDisplay方法,mWindowSession是用于 ViewRootImpl 和 WMS 通信使用的,mWindowSession初始化代码如下:

public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
    ......
    final IWindowSession mWindowSession;
    final W mWindow;
    ......

    public ViewRootImpl(Context context, Display display) {
        mContext = context;
        // 代码 1,通过 WindowManagerGlobal.getWindowSession() 方法得到一个 IWindowSession 对象
        mWindowSession = WindowManagerGlobal.getWindowSession();   
        ......
                                         
    }

    ......
}

IWindowSession 是一个 AIDL 接口,其服务端进程是 WMS,客户端进程是应用进程,mWindowSession是通过WindowManagerGlobal.getWindowSession获得的一个Binder服务代理,是App端向WMS发送消息的通道。 IWindowSession 的创建是在 WindowManagerGlobal 中,如下所示:

    public final class WindowManagerGlobal {

        private static IWindowManager sWindowManagerService;
        private static IWindowSession sWindowSession;
        ......
        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;
            }
        }

        public static IWindowSession getWindowSession() {
            synchronized (WindowManagerGlobal.class) {
                if (sWindowSession == null) {
                    try {
                        InputMethodManager imm = InputMethodManager.getInstance();
                        IWindowManager windowManager = getWindowManagerService();
                        //代码1
                        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;
            }
        }
        ......
    }

在代码1处调用windowManager.openSession, IWindowManager是WMS的代理对象,其实最终会走到WMS的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;
}

其实就是返回了一个Session对象,结合上面分析的mWindowSession.addToDisplay方法,看下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) {
    return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
            outContentInsets, outStableInsets, outOutsets, outInputChannel);
}

可以发现,会调用mService.addWindow方法,mService是WindowManagerService对象,因此会走到WMS的 addWindow方法,addWindow的源码如下:

public class WindowManagerService extends IWindowManager.Stub
        implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {

    final WindowHashMap mWindowMap = new WindowHashMap();

    public int addWindow(Session session, IWindow client, int seq,
            WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
            Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
            InputChannel outInputChannel) {
            ......

            final WindowState win = new WindowState(this, session, client, token, parentWindow,
                    appOp[0], seq, attrs, viewVisibility, session.mUid,
                    session.mCanAddInternalSystemWindow);
            ......
            win.attach();
            mWindowMap.put(client.asBinder(), win);
            ......
            win.mToken.addWindow(win);
    }
}

在 WindowManagerService.addWindow 方法中,会创建一个与 Window 对象对应的 WindowState 对象并调用 WindowState.attach 方法,然后将该 WindowState 对象添加到 WMS 的 mWindowMap Map 中, 在WindowState对象创建后会利用 win.attach()函数为当前APP申请建立SurfaceFlinger的链接,attach()函数源码如下:

void attach() {
    if (localLOGV) Slog.v(TAG, "Attaching " + this + " token=" + mToken);
    mSession.windowAddedLocked(mAttrs.packageName);
}

接下来调用mSession.windowAddedLocked方法,

void windowAddedLocked(String packageName) {
    mPackageName = packageName;
    mRelayoutTag = "relayoutWindow: " + mPackageName;
    if (mSurfaceSession == null) {
        if (WindowManagerService.localLOGV) Slog.v(
            TAG_WM, "First window added to " + this + ", creating SurfaceSession");
            ```
         // SurfaceSession新建
        mSurfaceSession = new SurfaceSession();
        if (SHOW_TRANSACTIONS) Slog.i(
                TAG_WM, "  NEW SURFACE SESSION " + mSurfaceSession);
        mService.mSessions.add(this);
        if (mLastReportedAnimatorScale != mService.getCurrentAnimatorScale()) {
            mService.dispatchNewAnimatorScaleLocked(this);
        }
    }
    mNumWindow++;
}

SurfaceSession是WMS为APP向SurfaceFlinger申请的通信通道,最终通知SurfaceFlinger进行了绘制。

参考的文章链接:

www.jianshu.com/p/a907f663c…

baijiahao.baidu.com/s?id=170917…