博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Activity的显示之ViewRootImpl初探
阅读量:5962 次
发布时间:2019-06-19

本文共 10452 字,大约阅读时间需要 34 分钟。

该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,如果能给各位看官带来一丝启发或者帮助,那真是极好的。


前言

在上一篇文章中我们主要分析了android.app.ActivityThread的main函数以及setContentView。另外我们还稍微分析了一下我们自己的源码,通过WindowManager添加View。我们知道调用setContentView把我们自己的xml布局添加到了DecorView ID为ID_ANDROID_CONTENT的布局后,最终还是会调用WindowManager.addView把DecorView加入PhoneWindow。到这里呢,我们把流程梳理一下。还是上图:

相信读者根据上图再结合前面所讲的内容应该对Activity的创建和显示有了初步的认识。那么本章我们来继续讲Activity的显示。该注意的是本系列并不意在带领读者去看清每一步具体的源码。在前面的文章中我也很少贴出源码。本系列文章意在让读者对Android系统有个更整体的把握。我所写的每一章知识都有可能在实际工作中用到。就如前面所讲解的Android下的进程问题以及Activity的生命周期以及本章要讲解的View的五大过程的基础ViewRootImpl。而理解View的五大过程(一般文章里都是三大过程)以及View的事件体系是更好的去自定义View的基础。

WindowManager 与 ViewRootImpl

WindowManager.addView()源码解析

public interface WindowManager extends ViewManager {    //这里我们只列出了一部分函数,但是并没有addView、updateViewLayout、removeView这三个函数    public Display getDefaultDisplay();    public void removeViewImmediate(View view);    ...}

好吧,果然没有这么简单,WindowManager是个接口,而且在其方法中没有找到addView方法,那么我们只能看看ViewManager了

public interface ViewManager{    public void addView(View view, ViewGroup.LayoutParams params);    public void updateViewLayout(View view, ViewGroup.LayoutParams params);    public void removeView(View view);}

还好是找到了,ViewManager没有再继承其他接口了。(要不然真不知道要找到什么时候去。)

既然WindowManager是个接口,那肯定要找它的实现类了。(在这里安利一个比较简单的方法,在Android Studio中)

public final class WindowManagerImpl implements WindowManager {    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();    private final Context mContext;    private final Window mParentWindow;    private IBinder mDefaultToken;    public WindowManagerImpl(Context context) {        this(context, null);    }    private WindowManagerImpl(Context context, Window parentWindow) {        mContext = context;        mParentWindow = parentWindow;    }    public WindowManagerImpl createLocalWindowManager(Window parentWindow) {        return new WindowManagerImpl(mContext, parentWindow);    }    public WindowManagerImpl createPresentationWindowManager(Context displayContext) {        return new WindowManagerImpl(displayContext, mParentWindow);    }    /**     * Sets the window token to assign when none is specified by the client or     * available from the parent window.     *     * @param token The default token to assign.     */    public void setDefaultToken(IBinder token) {        mDefaultToken = token;    }    @Override    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {        applyDefaultToken(params);        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) {        // Only use the default token if we don't have a parent window.        if (mDefaultToken != null && mParentWindow == null) {            if (!(params instanceof WindowManager.LayoutParams)) {                throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");            }            // Only use the default token if we don't already have a token.            final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;            if (wparams.token == null) {                wparams.token = mDefaultToken;            }        }    }    @Override    public void removeView(View view) {        mGlobal.removeView(view, false);    }    @Override    public void removeViewImmediate(View view) {        mGlobal.removeView(view, true);    }    @Override    public void requestAppKeyboardShortcuts(            final KeyboardShortcutsReceiver receiver, int deviceId) {        IResultReceiver resultReceiver = new IResultReceiver.Stub() {            @Override            public void send(int resultCode, Bundle resultData) throws RemoteException {                List
result = resultData.getParcelableArrayList(PARCEL_KEY_SHORTCUTS_ARRAY); receiver.onKeyboardShortcutsReceived(result); } }; try { WindowManagerGlobal.getWindowManagerService() .requestAppKeyboardShortcuts(resultReceiver, deviceId); } catch (RemoteException e) { } } @Override public Display getDefaultDisplay() { return mContext.getDisplay(); }}

WindowManagerImpl的源码如上所示,我们可以看到WindowManagerImpl的addView方法,WindowManagerImpl把工作交给了WindowManagerGlobal

WindowManagerGlobal

/**WindowManagerGlobal 源码比较长,这里我们只列出了一部分*/public final class WindowManagerGlobal {    private WindowManagerGlobal() {    }    public static void initialize() {        getWindowManagerService();    }    public static WindowManagerGlobal getInstance() {        synchronized (WindowManagerGlobal.class) {            if (sDefaultWindowManager == null) {                sDefaultWindowManager = new WindowManagerGlobal();            }            return sDefaultWindowManager;        }    }    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();                    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;        }    }    public static IWindowSession peekWindowSession() {        synchronized (WindowManagerGlobal.class) {            return sWindowSession;        }    }    //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);        }        ViewRootImpl root;        View panelParentView = null;               int index = findViewLocked(view, false);          if (index >= 0) {              if (mDyingViews.contains(view)) {                  mRoots.get(index).doDie();              } else {              //同一个View不允许被添加2次                  throw new IllegalStateException("View " + view                          + " has already been added to the window manager.");              }          }        //② 创建一个ViewRootImpl对象并保存在root变量中         root = new ViewRootImpl(view.getContext(), display);         view.setLayoutParams(wparams);        //③ 保存作为窗口的控件、布局参数以及新建的ViewRootImpl         mViews.add(view);         mRoots.add(root);         mParams.add(wparams);         // do this last because it fires off messages to start doing things         try {             // ④ 将作为窗口的控件设置给ViewRootImpl.这个动作将导致ViewRootImpl向WMS添加新的窗口、申请Surface以及托管控件在Surface上的重绘工作。这才是真正意义上完成了窗口的添加工作。             root.setView(view, wparams, panelParentView);         } catch (RuntimeException e) {             // BadTokenException or InvalidDisplayException, clean up.             if (index >= 0) {                 removeViewLocked(index, true);             }             throw e;         }        }    }    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);        }    }    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);        }    }   }

我们可以看到WindowManagerGlobal的私有构造函数以及getInstance()这个熟悉的静态方法名字。可以看出WindowManagerGlobal是个典型的单例

WindowManagerGlobal 的addView方法并不复杂,其主要的关键点我们已经标注并写了注释。也就是说WindowManagerGlobal的职责如下:

  1. 同意管理整个进程中所有窗口的信息。包括控件、布局参数以及ViewRootImpl这三个元素。(这一点从第③个注释可以看出)
  2. WindowManagerGlobal将窗口的创建、销毁、布局更新等任务交给了ViewRootImpl完成。

本篇总结

本篇文章分析了WindowManager的addView的过程,WindowManager是个接口,它的实现类是WindowManagerImpl类,而WindowManagerImpl又把相关逻辑交给了WindowManagerGlobal处理。WindowManagerGlobal是个单例类,它在进程中只存在一个实例,是它内部的addView方法最终创建了我们的核心类ViewRootImpl。ViewRootImpl实现了ViewParent接口,作为整个控件树的根部,它是控件树正常运作的动力所在,控件的测量、布局、绘制以及输入事件的派发处理窦世友ViewRootImpl出发。它是WindowManagerGlobal的实际工作者。


下篇预告

在下一篇文章中我们将深入介绍ViewRootImpl的工作流程。测量、布局、以及绘制。


参考博文


此致,敬礼

转载地址:http://urjax.baihongyu.com/

你可能感兴趣的文章
Devexpress 15.1.8 Breaking Changes
查看>>
Java B2B2C多用户商城 springcloud架构- common-service 项目构建过程(七)
查看>>
杨老师课堂之ArrayList集合常用方法解析
查看>>
ElasticSearch Client详解
查看>>
新零售讲堂之时代下的传统零售业,何去何从?
查看>>
c++读取和写入TXT文件的整理
查看>>
linux安全问答(1)
查看>>
mybatis update返回值的意义
查看>>
expdp 详解及实例
查看>>
解读最具O2O属性—哈根达斯微信企业号的成功之道
查看>>
Extjs4.x (MVC)Controller中refs以及Ext.ComponentQuery解析
查看>>
Server-01 How to Find the Remote Desktop Port
查看>>
Java--接口、抽象与继承
查看>>
通过IP判断登录地址
查看>>
Oracle闪回技术
查看>>
利用单壁路由实现vlan间路由
查看>>
hello world
查看>>
CentOS 7 配置yum本地base源和阿里云epel源
查看>>
python 学习导图
查看>>
生成树
查看>>