Android 设计模式 笔记 - 深入了解WindowManager

来源:互联网 发布:iphone6s网络不可用 编辑:程序博客网 时间:2024/05/09 22:52

了解一:

所有的可以显示到屏幕上的内容都是通过windowManager来操作的。包括Activity等。

了解二:

WindowManager是一个非常重要的子系统。简称WMS

了解三:

和WindowManager联系上的第一步就是通过Context中的getSystemService()方法。

我们已经了解到各种系统的服务都会注册到ContextImpl的一个map容器里,然后通过该服务的字符串键进行获取,而WindowManager就是ContextImpl中注册的众多服务之一。

了解四:

WindowManager在java代码中的具体实现是WindowManagerImpl。


  • 实践 -- Dialog中的WindowManager

追踪Dialog代码的构造方法:

    Dialog(Context context, int theme, boolean createContextThemeWrapper) {        if (createContextThemeWrapper) {            if (theme == 0) {                TypedValue outValue = new TypedValue();                context.getTheme().resolveAttribute(com.android.internal.R.attr.dialogTheme,                        outValue, true);                theme = outValue.resourceId;            }            mContext = new ContextThemeWrapper(context, theme);        } else {            mContext = context;        }        mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);        Window w = PolicyManager.makeNewWindow(mContext);        mWindow = w;        w.setCallback(this);        w.setOnWindowDismissedCallback(this);        w.setWindowManager(mWindowManager, null, null);        w.setGravity(Gravity.CENTER);        mListenersHandler = new ListenersHandler(this);    }

可以看到Dialog最终是通过setWindowManager方法把window和WindowManager建立了关系。

追踪setWindowManager方法:

    public void setWindowManager(WindowManager wm, IBinder appToken, String appName) {        setWindowManager(wm, appToken, appName, false);    }    /**     * Set the window manager for use by this Window to, for example,     * display panels.  This is <em>not</em> used for displaying the     * Window itself -- that must be done by the client.     *     * @param wm The window manager for adding new windows.     */    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的方法,即createLocalWindowManager方法,继续跟踪这个方法:

   public WindowManagerImpl createLocalWindowManager(Window parentWindow) {        return new WindowManagerImpl(mDisplay, parentWindow);    }

单纯的构建了一个WindowManagerImpl对象,不过和ContextImpl里面注册WindowManager不同的是这里多了一个参数,
parentWindow
这个参数的增加说明构建WindowManagerImpl的这个方法是于具体的window相关联的。表示java层上的Window对象已经和WindowManager建立了第一步的联系。

好了,我们通过WindowManagerImpl和Window建立联系之后接下来进行分析WindowManagerImpl代码代码如下:

public final class WindowManagerImpl implements WindowManager {    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();    private final Display mDisplay;    private final Window mParentWindow;    public WindowManagerImpl(Display display) {        this(display, null);    }    private WindowManagerImpl(Display display, Window parentWindow) {        mDisplay = display;        mParentWindow = parentWindow;    }    public WindowManagerImpl createLocalWindowManager(Window parentWindow) {        return new WindowManagerImpl(mDisplay, parentWindow);    }    public WindowManagerImpl createPresentationWindowManager(Display display) {        return new WindowManagerImpl(display, mParentWindow);    }    @Override    public void addView(View view, ViewGroup.LayoutParams params) {        mGlobal.addView(view, params, mDisplay, mParentWindow);    }    @Override    public void updateViewLayout(View view, ViewGroup.LayoutParams params) {        mGlobal.updateViewLayout(view, params);    }    @Override    public void removeView(View view) {        mGlobal.removeView(view, false);    }    @Override    public void removeViewImmediate(View view) {        mGlobal.removeView(view, true);    }    @Override    public Display getDefaultDisplay() {        return mDisplay;    }}

我们看到在WindowManagerImpl中有一个WindowManagerGlobal的单例对象,而WindowManagerImpl的具体实现方法也是调用WindowManagerGlobal中的,也就是说WindowManagerGlobal是具体的实现类,而WindowManagerImpl只是一个架子罢了,我们又有了解的是,每一次的创建出一个界面显示到屏幕上所调用的是addView方法我,我们现在主要看的是addView方法,看看WindowManagerGlobal到底是怎么显示界面的:

 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");        }        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;        if (parentWindow != null) {            parentWindow.adjustLayoutParamsForSubWindow(wparams);        }        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);            }            int index = findViewLocked(view, false);            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);                    }                }            }            root = new ViewRootImpl(view.getContext(), display);            view.setLayoutParams(wparams);            mViews.add(view);            mRoots.add(root);            mParams.add(wparams);        }        // 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.            synchronized (mLock) {                final int index = findViewLocked(view, false);                if (index >= 0) {                    removeViewLocked(index, true);                }            }            throw e;        }    }

这一方法首先是判断参数的合法性,在合法性的同事给参数赋值。

而关于创建界面的主要步骤如下:

  • 构建ViewRootImpl对象
  • 把参数布局设置到view
  • 存储ViewRootImpl,View,LayoutParams到列表
  • 通过ViewRootImpl的setView方法把view显示到窗口界面上。

值得注意的是ViewRootImpl并不是一个view而是一个继承Handler类的,作为native和java层view系统的一个通信桥梁。

代码分析到这里我们就已经到了Android的Framework层了。

下面才是Framework和Native层建立通讯关系的具体类,即使ViewRootImpl

因为addView的第一步就是创建ViewRootImpl的对象root,我们可以看下他的代码:

    public ViewRootImpl(Context context, Display display) {        mContext = context;        mWindowSession = WindowManagerGlobal.getWindowSession();        mDisplay = display;        mBasePackageName = context.getBasePackageName();        mDisplayAdjustments = display.getDisplayAdjustments();        mThread = Thread.currentThread();  /** *省略代码 **/ }
我们看到代码里有一个WindowManagerGlobal的方法,我们不得不怀疑这个代码和ViewRootImpl的作用有很大的关系,所以我们去追踪这个方法的实现:

    public static IWindowSession getWindowSession() {        synchronized (WindowManagerGlobal.class) {            if (sWindowSession == null) {                try {                    InputMethodManager imm = InputMethodManager.getInstance();                    IWindowManager windowManager = getWindowManagerService();                    sWindowSession = windowManager.openSession(                            imm.getClient(), imm.getInputContext());                    float animatorScale = windowManager.getAnimationScale(2);                    ValueAnimator.setDurationScale(animatorScale);                } catch (RemoteException e) {                    Log.e(TAG, "Failed to open window session", e);                }            }            return sWindowSession;        }    }
    public static IWindowManager getWindowManagerService() {        synchronized (WindowManagerGlobal.class) {            if (sWindowManagerService == null) {                sWindowManagerService = IWindowManager.Stub.asInterface(                        ServiceManager.getService("window"));            }            return sWindowManagerService;        }    }

在getWindowSession方法中,首先根据getWindowManagerService方法获得了IWindowManager对象,而getWindowManagerService方法是通过ServiceManager.getService("window")方法获取WindowManagerService,并且通过IWindowManager.Stub.asInterface方法把这个函数直接转换成IWindowManager对象。

我们下面一步步来,先去看getService方法的实现:

    public static IBinder getService(String name) {        try {            IBinder service = sCache.get(name);            if (service != null) {                return service;            } else {                return getIServiceManager().getService(name);            }        } catch (RemoteException e) {            Log.e(TAG, "error in getService", e);        }        return null;    }
返回的是一个IBinder对象。也就是说Android Framework和WindowManagerService通过的是binder机制进行通讯的,我们这边到这一步才真正的和WindowManagerService建立初步的联系。

接下来我们继续分析这个getWindowSession方法的实现:

我们获取到IWindowManager对象之后,使用openSession方法来和WMS建立一个通信会话,以后如果有各种需求只需要通过这个session就好了。


和WMS建立了session关系之后,就是我们已经完成了ViewRootImpl对象的创建,之后我们就可以使用setView方法进行显示界面了,这个方法会向WMS发送显示界面的请求。

我们看下setView的代码:

    /**     * We have one child     */    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {        synchronized (this) {                       requestLayout();                try {                                     res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,                            getHostVisibility(), mDisplay.getDisplayId(),                            mAttachInfo.mContentInsets, mInputChannel);                }             }        }    }

代码简化之后如上,源代码非常复杂,但是我们主要关注的是setView通知显示界面,所以我们在setView上面只需要关注请求布局的方法(requestLayout),和发送请求的方法(  res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mInputChannel);)

请求布局的方法实现:

    @Override    public void requestLayout() {        if (!mHandlingLayoutInLayoutRequest) {            checkThread();            mLayoutRequested = true;            <pre name="code" class="java">    void scheduleTraversals() {        if (!mTraversalScheduled) {            mTraversalScheduled = true;            mTraversalBarrier = mHandler.getLooper().postSyncBarrier();            mChoreographer.postCallback(                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);            scheduleConsumeBatchedInput();        }    }

(); } }

    void scheduleConsumeBatchedInput() {        if (!mConsumeBatchedInputScheduled) {            mConsumeBatchedInputScheduled = true;            mChoreographer.postCallback(Choreographer.CALLBACK_INPUT,                    mConsumedBatchedInputRunnable, null);        }    }

也就是向handler中发送了一个消息,这个消息会触发整个视图的绘制操作。即performTraversals()方法,看下这个方法:

    private void performTraversals() {}

额...方法太复杂,我们要知道的是在这个方法里面总共做了四件事情:

  • 对象,获取Surface对象,用于图形的绘制
  • 丈量,丈量整个视图书的各个View的大小,performMeasure方法
  • 布局,使用performLayout方法布局整个视图树
  • 绘制,使用performDraw绘制真个视图树

绘制界面的代码就不专门的进行表现了,我们仔细研究了performDraw的代码之后发现具体实现绘制视图界面的方法是draw方法,在这个方法里面,我们想要显示图形到界面要经过一下几个步骤:

  • 判断是CPU绘制还是GPU绘制
  • 获取Surface对象
  • 通过surface对象获取并且锁住Canvas绘图对象
  • 从DecorView开始发起整个视图树的绘制
  • 从Surface解锁Canvas,并且通知SurfaceFlinger更新视图







0 0