android WindowManage解析

前言

对Window、WM、WMS、WMG等一系列有一定深度的理解,个人认为对开发工作会有一定的帮助,是很重要的一部分,可以说创建一个Activity就离不开Window,所以还是要掌握~

一:WindowManage 关联类的名称以及作用

  • Window:窗口 ,抽象类
  • PhoneWindo:继承于Window,Window的实现类
  • ViewManage: 接口类,只有三个方法(addView、updateViewLayout、removeView),WindowManage 继承与它,入参都是View或者ViewGroup,说明Window是以View的形式存在的
  • WindowManage:接口类,顾名思义,window的管理类
  • WindowManageImpl:WindowManage的实现类
  • WindowManageGlobal: 全局单例,管理Window

二:WM系列类图

WM系列类图

三:从Activity的attach()说起

在Activity的启动流程中,调用到ActivityThread.class的performLaunchActivity()方法,这个方法我们很熟悉了,activity先attach()在onCreate()

   private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...省略
        Activity activity = null;
        try {
            java.lang.ClassLoader cl = appContext.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);

...省略
                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);
                //先attach了Activity
                if (customIntent != null) {
                    activity.mIntent = customIntent;
                }
                r.lastNonConfigurationInstances = null;
                checkAndBlockForNetworkAccess();
                activity.mStartedActivity = false;
                int theme = r.activityInfo.getThemeResource();
                if (theme != 0) {
                    activity.setTheme(theme);
                }

                activity.mCalled = false;
                if (r.isPersistable()) {
                   //Instrumentation再去调用的onCreate方法
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                } else {
                    mInstrumentation.callActivityOnCreate(activity, r.state);
...省略

Activity的attach方法中又调用了setWindowManager()方法

    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) {
         ...省略
        //这里创建了PhoneWindow对象
        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        ...省略
       //这里调用了setWindowManager()方法
        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
        ...省略
    }

Window的setWindowManager方法中创建了WindowManagerImpl实例,因为WindowManagerImpl是WindowManage的实现类,创建的时候又把Window传了进去,看前面的WindowManage继承ViewManage,是不是在Window这个类中,管理交给了mWindowManager成员了

    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);
        }
    //注意:这里把Windiw实例传了进来,所以为什么叫WindowManager
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
    }

所以之后Window的addView等操作,都是交给了WindowManage去实现,也就是WindowManageImpl去实现

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

可以看到WindowManageImpl又交给了mGlobal去实现,mGlobal就是WindowManagerGlobal,全局单例

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

再看之前的类图,是不是清楚了很多了

四:Window有哪些属性

Window属性无非就三个:

五:WindowManagerGlobal.class类解析

5.1 我们先看下WindowManagerGlobal中重要的成员有哪些:

    //单例的对象锁
    private final Object mLock = new Object();
    ...省略
    private final ArrayList<View> mViews = new ArrayList<View>();
    private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
    private final ArrayList<WindowManager.LayoutParams> mParams =
            new ArrayList<WindowManager.LayoutParams>();

可以看到有三个List,分别存放了View、ViewRootImpl、WindowManager.LayoutParams,这个后面会说到

5.2 WindowManagerGlobal的addView过程
在第三步中说到,WindowManageImpl最后交给了WindowManagerGlobal去addView

  public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
 ...省略
        //创建了布局参数对象
        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
        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) {
            if (mSystemPropertyUpdater == null) {
 ...省略
            root = new ViewRootImpl(view.getContext(), display);

            view.setLayoutParams(wparams); 

            //view,root,wparams放入对应的列表中
            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);
            try {
           //最后把相关参数都放入ViewRootImpl中
                root.setView(view, wparams, panelParentView);
            } catch (RuntimeException e) {
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
                throw e;
            }
        }
    }

可以看到最后把view,root,wparams放入对应的List中,再把参数ViewRootImpl中
5.3 ViewRootImpl 是干什么的

  • View树的根并管理着View树
  • 触发View的measure、layout、draw
  • 输入事件的中转站
  • 管理Surface
  • 负责与WMS进行进程间通讯

ViewRootImpl 的setView方法做了些什么

    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
 ...省略
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);
 ...省略
}

可看到mWindowSession是IWindowSession对象,它实在ViewRootImpl创建的时候WMG创建的

    public ViewRootImpl(Context context, Display display) {
        mContext = context;
        mWindowSession = WindowManagerGlobal.getWindowSession();
        mDisplay = display;
        mBasePackageName = context.getBasePackageName();
        mThread = Thread.currentThread();
        mLocation = new WindowLeaked(null);
        mLocation.fillInStackTrac
 ...省略

可以看到这里通过进程间通讯的技术获取到了WMS的Session对象

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

然后我们看Session做了什么

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

调用了AMS的addWindow方法完成了添加Window

5.4 ViewRootImpl 线程检测
关于ViewRootImpl 这个类还有一个要说的,很多操作UI的方法都会调用到checkThread(),如果此时创建ViewRootImpl 的线程和更新操作UI的线程不是同一个的话,就会抛出异常

如果不同的线程会抛出异常,后续Handler相关的文章会说到这点

 void checkThread() {
        if (mThread != Thread.currentThread()) {
            throw new CalledFromWrongThreadException(
                    "Only the original thread that created a view hierarchy can touch its views.");
        }   
 public ViewRootImpl(Context context, Display display) {
        mContext = context; 
     //可知是当前ViewRootImpl创建时候的显示
        mWindowSession = WindowManagerGlobal.getWindowSession();
        mDisplay = display;
        mBasePackageName = context.getBasePackageName();
        mThread = Thread.currentThread();
    }

六:Window的更新过程

和添加Window类似
调用ViewManage的updateViewLayout方法-> WindowManage->WindowManageImpl->WindowManageGrobal的updateViewLayout方法
看下这个方法做了什么:

    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) {
            int index = findViewLocked(view, true);
            ViewRootImpl root = mRoots.get(index);
            mParams.remove(index);
            mParams.add(index, wparams);
            root.setLayoutParams(wparams, false);
        }
    }

可知还是最后调用了ViewRootImpl 的相关方法,看下setLayoutParams()

    void setLayoutParams(WindowManager.LayoutParams attrs, boolean newView) {
        synchronized (this) {
 ...省略
            scheduleTraversals();
        }
    }

看下scheduleTraversals()方法

    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }

scheduleTraversals方法中的Choregrapher是一个用于接收显示系统的VSync信号,在下一帧渲染的时候执行一系列操作

  final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal();
        }
    }
    final TraversalRunnable mTraversalRunnable = new TraversalRunnable();

看下doTraversal()又调用了performTraversals()

    void doTraversal() {
 ...省略
            performTraversals();
 ...省略
    }

看下performTraversals()方法

private void performTraversals() {
 ...省略
    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
 ...省略
   performLayout(lp, mWidth, mHeight);
 ...省略
   performDraw();
 ...省略
}

可以看到这个方法完成了View的绘制流程

END

人的自由并不在于你想做什么就做什么,恰恰相反,自由是在于你可以不用去做你不想做的事情,我所追寻的是后一种自由

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。