WindowMangerService(WMS)理解

1. WMS(WindowMangerService)的诞生

2. Window的创建

3. 为何在onCreate方法中不能获取到View的宽高?

4. 将View添加到WMS

以Android-23版本源码分析


1. WMS的诞生,WMS作为一个系统服务,在系统启动时创建,而这个创建就发生在SystemServer类中,
private void startOtherServices() {
    //省略代码....
    Slog.i(TAG, "Window Manager");
    //返回WMS实例
    wm = WindowManagerService.main(context, inputManager,
            mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
            !mFirstBoot, mOnlyCore);
    //添加到ServiceManger中       
    ServiceManager.addService(Context.WINDOW_SERVICE, wm);
    ServiceManager.addService(Context.INPUT_SERVICE, inputManager);

    mActivityManagerService.setWindowManager(wm);

    //省略代码....
}
2. Window的创建

在Android中,不论是Activity还是Dialog都是以Window作为一个界面展示的容器,我们这里以Activity中Window的创建来分析
Window在Android中使用到的实现类就只有PhoneWindow;而在Activity中PhoneWindow的创建是发生在attach()方法内,先于onCreate()方法的执行

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) {
        attachBaseContext(context);

        mFragments.attachHost(null /*parent*/);

        //PhoneWinodw构造函数只初始化了LayoutInflater对象
        mWindow = new PhoneWindow(this);
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(this);
        
        //省略代码....  都是实例变量的赋值
        
        //(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),拿到是我们之前创建的WMS在客户端App的代理对象
        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());
        }
        //mWindowManager 是WindowMangerImpl类型
        mWindowManager = mWindow.getWindowManager();
        mCurrentConfig = config;
    }
3. 为何在onCreate方法中不能获取到View的宽高?

在Activity的onCreate方法中只是创建了DecorView,并将我们的布局文件添加到DecorView中

//PhoneWindow的setContentView方法
@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) {
        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 {
        //将我们自定义的View添加到id为com.android.internal.R.id.content的ViewGroup子类(ContentFrameLayout)
        mLayoutInflater.inflate(layoutResID, mContentParent);
    }
    mContentParent.requestApplyInsets();
    final Callback cb = getCallback();
    if (cb != null && !isDestroyed()) {
        cb.onContentChanged();
    }
}

从以上代码可以看出,在这里只是完成了View的添加,并没有对View进行measure测量,所以无法获取到View的宽高

4. View是如何添加到WMS的?

4.1 这里我们直接从ActivityThreadhandleResumeActivity方法入手

final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume) {
        //省略代码....
        if (r != null) {
            //省略代码....
            // mStartedActivity表示这个Activity是否执行过startActivityForResult方法且requsetCode >=0 
            boolean willBeVisible = !a.mStartedActivity;
            if (!willBeVisible) {
                try {
                //告诉AMS自己将可见
                    willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible(
                            a.getActivityToken());
                } catch (RemoteException e) {
                }
            }
            if (r.window == null && !a.mFinished && willBeVisible) {
                r.window = r.activity.getWindow();
                View decor = r.window.getDecorView();
                decor.setVisibility(View.INVISIBLE);
                ViewManager wm = a.getWindowManager();
                WindowManager.LayoutParams l = r.window.getAttributes();
                a.mDecor = decor;
                l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
                l.softInputMode |= forwardBit;
                if (a.mVisibleFromClient) {
                    a.mWindowAdded = true;
                    //wm是在attach方法中创建的WindowMangerImpl对象
                    //decor是在onCreate方法中创建的DecorView
                    wm.addView(decor, l);
                }


                //省略代码....
            }
    }

4.2 进入WindowMangerImpladdView()方法查看

    @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        //代码很简单,直接使用WindowManagerGlobal对象
        //WindowMangerGlobal是一个单例
        mGlobal.addView(view, params, mDisplay, mParentWindow);
    }

4.3 直接看WindowManagerGlobal对象的addView方法

    public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        //省略代码....
        ViewRootImpl root;
        View panelParentView = null;

        synchronized (mLock) {
            //省略代码....
            //创建ViewRootImpl对象
            root = new ViewRootImpl(view.getContext(), display);

            view.setLayoutParams(wparams);
            //并将View,ViewRootImpl,WindowManger.LayoutParams分别保存在三个数组中
            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);
            //View[] mViews: 这里的每一个View对象都将成为WmS所认为的一个窗口;
            //ViewRootImpl[] mRoots:mViews中的每个View对象都对应一个ViewRootImpl对象;
            //WindowManager.LayoutParams[] mParams:当把mViews中的View当做一个窗口添加到WmS中时,WmS要求每个被添加的窗口都要对应一个LayoutParams对象,mParams正是保存了每个窗口对应的参数对象;
        }

        // do this last because it fires off messages to start doing things
        try {
            //setView内部执行添加到WMS的逻辑
            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;
        }
    }

4.4 进入ViewRootImpl类,首先查看ViewRootImpl的构造函数,这里只需要关心mWindowSession变量,mAttachInfo变量(View的invalidate,post等方法都会调用ViewRootImpl中的ViewRootHandler,在performTraversals()方法内会将mAttachInfo设置给View)

public ViewRootImpl(Context context, Display display) {
    //省略代码....
    mWindowSession = WindowManagerGlobal.getWindowSession();
    //省略代码....
    mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this);
    //省略代码....
    
    mChoreographer = Choreographer.getInstance();
    mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
    loadSystemProperties();
}

4.5 接着看ViewRootImplsetView()方法

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

            //省略代码....
            mAdded = true;
            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.
            // 在添加到WMS之前,先进行一次测量,内部会执行performTraversals()方法
            requestLayout();
            
            if ((mWindowAttributes.inputFeatures
                    & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                mInputChannel = new InputChannel();
            }
            try {
                mOrigWindowType = mWindowAttributes.type;
                mAttachInfo.mRecomputeGlobalAttributes = true;
                collectViewAttributes();
                //添加到WMS
                //mWindow 是一个Bindler类型的变量,用于WMS和客户端交互
                res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                        getHostVisibility(), mDisplay.getDisplayId(),
                        mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                        mAttachInfo.mOutsets, mInputChannel);
            } catch (RemoteException e) {
                //省略代码....
            } finally {
                if (restore) {
                    attrs.restore();
                }
            }

            //省略代码....

            if (view instanceof RootViewSurfaceTaker) {
                mInputQueueCallback =
                    ((RootViewSurfaceTaker)view).willYouTakeTheInputQueue();
            }
            if (mInputChannel != null) {
                if (mInputQueueCallback != null) {
                    mInputQueue = new InputQueue();
                    mInputQueueCallback.onInputQueueCreated(mInputQueue);
                }
                //接收输入事件
                mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
                        Looper.myLooper());
            }

            //省略代码....

            // Set up the input pipeline.
            CharSequence counterSuffix = attrs.getTitle();
            mSyntheticInputStage = new SyntheticInputStage();
            //ViewPostImeInputStage内完成了输入事件传递到View的过程
            InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage);
            InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,
                    "aq:native-post-ime:" + counterSuffix);
            InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);
            InputStage imeStage = new ImeInputStage(earlyPostImeStage,
                    "aq:ime:" + counterSuffix);
            InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage);
            InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage,
                    "aq:native-pre-ime:" + counterSuffix);

            mFirstInputStage = nativePreImeStage;
            mFirstPostImeInputStage = earlyPostImeStage;
            mPendingInputEventQueueLengthCounterName = "aq:pending:" + counterSuffix;
        }
    }
}
  1. IWindowSession在WMS服务端的实现类Session,addToDisplay()方法会直接调用WMS的addWindow()方法,
    直接看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;
        }

       //省略代码....

        synchronized(mWindowMap) {
            //省略代码....
            boolean addToken = false;
            //在AMS的startActivity过程中会调用WMS的addAppToken
            //因此这里拿到的token是不为空的
            WindowToken token = mTokenMap.get(attrs.token);
            if (token == null) {
                //省略代码....
            } else if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
                AppWindowToken atoken = token.appWindowToken;
                //以下条件都不满足
                if (atoken == null) {
                    Slog.w(TAG, "Attempted to add window with non-application token "
                          + token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_NOT_APP_TOKEN;
                } else if (atoken.removed) {
                    Slog.w(TAG, "Attempted to add window with exiting application token "
                          + token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_APP_EXITING;
                }
                if (type == TYPE_APPLICATION_STARTING && atoken.firstWindowDrawn) {
                    // No need for this guy!
                    if (localLOGV) Slog.v(
                            TAG, "**** NO NEED TO START: " + attrs.getTitle());
                    return WindowManagerGlobal.ADD_STARTING_NOT_NEEDED;
                }
            }
            
            //省略代码....
            //创建WindowState保存窗口的信息
            WindowState win = new WindowState(this, session, client, token,
                    attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent);
            if (win.mDeathRecipient == null) {
                // Client has apparently died, so there is no reason to
                // continue.
                Slog.w(TAG, "Adding window client " + client.asBinder()
                        + " that is dead, aborting.");
                return WindowManagerGlobal.ADD_APP_EXITING;
            }

           //省略代码....

            if (outInputChannel != null && (attrs.inputFeatures
                    & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                String name = win.makeInputChannelName();
                InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
                win.setInputChannel(inputChannels[0]);
                inputChannels[1].transferTo(outInputChannel);
                //注册输入系统事件的监听
                mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);
            }

            // From now on, no exceptions or errors allowed!
            //添加窗口成功
            res = WindowManagerGlobal.ADD_OKAY;

            origId = Binder.clearCallingIdentity();

            if (addToken) {
                mTokenMap.put(attrs.token, token);
            }
            win.attach();
            //保存WindowState
            mWindowMap.put(client.asBinder(), win);
            //省略代码....
        }

        if (reportNewConfig) {
            sendNewConfiguration();
        }

        Binder.restoreCallingIdentity(origId);

        return res;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值