我眼中的Window创建/添加/删除/更新过程

        在Android中和我们打交道最多的就是Activity,因为我们会频繁的与界面进行交互,而Activity内部关于界面方面的操作都是由Window来实现的,因此我们有必要了解下Window的实现机制了;网上有挺多关于Window创建/添加/删除/更新方面的源码分析了,我这篇博客不会去大篇幅的贴出代码分析那些源码机制,取而代之的是以语言描述的方式展现出Window机制中的一些知识点;

        个人认为想要学习Window机制的实现原理,需要弄懂以下几个问题,这篇文章主要讲解的是应用级Window--->Activity的相关内容:

        (1):Android中Window的分类有哪些呢?

        (2):与Window实现机制有关的是哪些类或者接口呢?

        (3):一个Window是怎么创建出来的呢?

        (4):添加Window的过程中做了些什么事情?

        (5):删除Window的时候发生了什么?

        (6):更新Window的时候发生了什么?

        接下来,我一个一个的解答上面的问题:

        首先是Android中Window的分类

        Android中的Window分为三类:

        <1>:应用Window,我们通常见到的就是Activity了;

        <2>:子Window,比如Dialog;

        <3>:系统Window,比如Toast;

        那么与Window机制实现有关的类和接口有哪些呢?

        ViewManager(接口)、WindowManager(接口)、WindowManagerImpl(final类)、WindowManagerGlobal(final类)

        具体他们的关系见下图:

                                              

        接着便是一个Window是怎么创建出来的呢?

        因为Window是对Activity操作的真正执行者嘛,我们平常调用的setContentView实际上调用的也是Window的setContentView,那么很自然找Window是怎么创建的就该先了解下Activity的创建流程了,在这篇文章中,我有讲到过Activity的启动过程最终会执行到ApplicationThread的scheduleLaunchActivity方法上面,在这个方法的最后会发送一条消息来让H这个Handler进行处理,具体消息中携带的标志信息是LAUNCH_ACTIVITY,真正的处理就应该是在H这个Handler里面的handleMessage了,在他里面找到case为LAUNCH_ACTIVITY的语句块,执行handleLaunchActivity方法,而在handleLaunchActivity里面是会通过performLaunchActivity创建一个Activity对象出来的,具体Activity是怎么被创建出来的呢?我们稍微看下ActivityThread$performLaunchActivity源码就知道了:

java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
 public Activity newActivity(ClassLoader cl, String className,
            Intent intent)
            throws InstantiationException, IllegalAccessException,
            ClassNotFoundException {
        return (Activity)cl.loadClass(className).newInstance();
    }
        很明显的看出来是利用Instrumentation对象通过反射创建的;

        接着会创建一个Application对象出来,通过LoadedApk的makeApplication方法,这个方法里面创建Application的方式实际上也是通过Instrumentation对象反射创建的,在makeApplication中创建完Application之后会通过Instrumentation的callApplicationOnCreate方法回调Application的onCreate方法,这个就是我们Application的生命周期方法啦;

        现在我们仅仅只是创建了Activity对象了,但是我们知道Android程序的运行是需要上下文环境支持的,因此继续查看ActivityThread$performLaunchActivity源码你会看到有关创建应用上下文的代码:

if (activity != null) {
                Context appContext = createBaseContextForActivity(r, activity);
                CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
                Configuration config = new Configuration(mCompatConfiguration);
                if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
                        + r.activityInfo.name + " with config " + config);
                activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config);
}
        注意我仅仅截取了与我们分析有关的源码,在这段源码中首先就是创建一个ContextImpl对象了,至于为什么是ContextImpl类型,你查看createBaseContextForActivity方法就知道了,有了ContextImpl对象之后,会通过Activity的attach方法,将ContextImpl与我们的Activity绑定起来;

        到此,我们还没看到有关Window的任何相关代码,只看到了Activity的有关部分,可想而知,在Activity的attach里面必定存在有关Window的部分,简单把有用的源码copy如下:

        Activity$attach

mWindow = PolicyManager.makeNewWindow(this);
............
mWindow.setCallback(this);
.............
mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
............
 mWindowManager = mWindow.getWindowManager();
............

        可以看到首先就是通过PolicyManager的makeNewWindow方法创建了一个PhoneWindow对象出来,具体makeNewWindow的话是通过Policy类的makeNewWindow方法new出来一个PhoneWindow对象的;接着便是为创建的PhoneWindow对象设置回调;随后会通过setWindowManager方法为PhoneWindow对象设置WindowManager对象,具体WindowManager是怎么创建的就是通过setWindowManager的第一个参数(WindowManager)context.getSystemService(Context.WINDOW_SERVICE)创建的,那么这时候你肯定就想什么时候会创建Context.WINDOW_SERVICE类型的系统服务呢?其实就在我们创建ContextImpl对象的时候啦,上面已经讲到说在创建Activity的时候会绑定ContextImpl对象,即ContextImpl对象会在Activity之前创建的,查看ContextImpl源码,你会发现存在一个static类型的静态块代码区域,这个区域里面找到与Context.WINDOW_SERVICE有关的代码如下:

registerService(WINDOW_SERVICE, new ServiceFetcher() {
                Display mDefaultDisplay;
                public Object getService(ContextImpl ctx) {
                    Display display = ctx.mDisplay;
                    if (display == null) {
                        if (mDefaultDisplay == null) {
                            DisplayManager dm = (DisplayManager)ctx.getOuterContext().
                                    getSystemService(Context.DISPLAY_SERVICE);
                            mDefaultDisplay = dm.getDisplay(Display.DEFAULT_DISPLAY);
                        }
                        display = mDefaultDisplay;
                    }
                    return new WindowManagerImpl(display);
                }});
        可以看到首先创建了一个Display对象出来,随后调用了WindowManagerImpl构造函数,创建了一个WindowManagerImpl对象出来;

        如果你查看setWindowManager源码的话,你会发现他最后会执行我们刚刚创建的WindowManagerImpl对象的createLocalWindowManager语句,并且返回这个创建的值,查看createLocalWindowManager的源码会发现其实他也是创建一个WindowManagerImpl对象出来的,最初我对这个地方一直很费解,为什么要分两步来创建WindowManagerImpl对象呢?后来想明白是这样子的,首先创建一个WindowManagerImpl对象,此时的WindowManagerImpl是没有和具体的Window发生关联的,随后创建出来的WindowManagerImpl会和Window发生关联操作;

        最后呢,将创建的WindowManager对象赋给我们的Activity类中属性便可以啦,因为在Activity的别的地方可能会用到WindowManager嘛;

        这样子,Activity中的Window就创建成功啦,我们来做个小结:

        (1):首先是创建一个Activity对象出来;

        (2):接着是创建一个ContextImpl对象出来,ContextImpl是实现了Context上下文接口的,在创建ContextImpl的同时,会在他的static语句块中创建一个只包含有Display对象的WindowManager对象出来;

        (3):通过Activity的attach函数,将Activity与创建的ContextImpl对象绑定到一起,在attach里面会创建PhoneWindow对象,同时为其设置回调接口;

        (4):通过setWindowManager为当前创建的PhoneWindow对象绑定WindowManager对象,并且返回这个WindowManager对象对象,将这个WindowManager赋给Activity类中的变量;

       在创建了WindowManager对象之后,如果我们想要往Window里面添加一个View的话,需要调用WindowManager的addView方法,而WindowManager是接口,WindowManagerImpl是对WindowManager的实现,如果你查看WindowManagerImpl的实现的话,会发现它里面的所有方法都是通过WindowManagerGlobal对象实现的,因此像Window里面添加View实际上执行的是WindowManagerGlobal的addView,除了addView外,删除View和更新View也是在WindowManagerGlobal中实现的;

        在WindowManagerGlobal里的addView方法里面的伪代码实现如下:

//进行一些参数检查,不合法的话则抛出异常
............
root = new ViewRootImpl(view.getContext(), display);

            view.setLayoutParams(wparams);

            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);
root.setView(view, wparams, panelParentView);
        我们可以认为addView里面做了一下几件事情:

        (1):进行参数合法性的检查,不合法的话抛出相应的异常;

        (2):创建一个ViewRootImpl对象出来,为什么要创建一个ViewRootImpl对象呢?这么说吧,我们的Window实际上是一个抽象概念,他需要有View的存在而存在,而ViewRootImpl是Window与View之间进行交互的中介吧,了解View绘制过程的都清楚,View绘制的开始方法其实就是ViewRootImp里面的performTraversals,在创建ViewRootImp里面要注意下面这句代码:

mWindowSession = WindowManagerGlobal.getWindowSession();
他会为我们创建一个IWindowSession对象,这个IWindowSession对象具体来讲的话是通过WindowManagerGlobal的getWindowSession创建的,这段源码不算长,我们来看看:

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;
        }
    }
第6行看到通过getWindowManagerService创建了一个WindowManagerService对象出来,查看WindowManagerService源码会发现实际上是通过IPC的方式创建出来的,如下:
 public static IWindowManager getWindowManagerService() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowManagerService == null) {
                sWindowManagerService = IWindowManager.Stub.asInterface(
                        ServiceManager.getService("window"));
            }
            return sWindowManagerService;
        }
    }
有了WindowManagerService对象之后,会利用该对象调用openSession方法创建出来一个IWindowSession对象,有了这个Session对象之后,随后的添加操作实际上就是ViewRootImpl通过这个Session来进行添加的;

        (3):继续回到WindowManagerGlobal的addView方法,在创建完ViewRootImpl之后,为View设置布局参数,接下来会执行3个add操作,我们来看看这三个对象的定义:

 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嘛,其中mViews存储的是所有Window所对应的View,mRoots存储的是所有Window所对应的ViewRootImpl,mParams存储的是所有Window对应的布局参数,最后在所有的添加操作做完之后,执行了ViewRootImpl的setView方法,这里也印证了我们前面提到的ViewRootImpl其实上是Window与View之间交互的桥梁这个观点;

        (4):ViewRootImpl的setView方法比较长,我们仅说下和Window添加View有关联的部分,首先调用requestLayout从根View也就是DecorView开始进行重绘,随后会利用我们在创建ViewRootImpl的Session对象来将当前View添加到窗体中,调用的是addToDisplay方法,这个方法的内部会利用我们在创建ViewRootImpl的时候创建的WindowManagerService对象,调用WindowManagerService对象的addWindow方法;

        到此,一个View就被添加到Window上面啦,我们来对整个添加过程做个小结:

        <1>:首先利用之前创建Window的过程中创建的WindowManager对象,调用它的addView方法,实际上调用的是WindowManagerImpl的addView方法,而WindowManagerImpl里面是由WindowManagerGlobal实现的,也就是调用了WindowManagerGlobal的addView方法;

        <2>:在addView中首先会进行参数合法性的检查,不合法的话抛出相应的异常信息;

        <3>:接着会创建一个ViewRootImpl对象出来,他是Window与View进行交互的桥梁,在创建ViewRootImpl的时候会获取到WindowManagerService对象,同时利用WindowManagerService对象创建一个Session对象,有了Session对象之后,后面的添加操作就可以通过该Session进行了,相当于建立了一个通话过程一样了;

        <4>:接着便是将当前View、ViewRootImpl、布局参数LayoutParams添加到各自队列里面的操作了;

        <5>:最后调用ViewRootImpl的setView方法将View添加到Window上面,其实在setView方法里面真正的添加操作是通过Session来进行传递由WindowMangerService的addWindow方法实现的;

       接着我们看看删除Window的操作是什么样子的了?

        和添加Window的过程有点类似,其实本质上整个过程还是会交割给WindowManager去删除的,而WindowManagerImpl实现了WindowManager接口,因此任务相当于给了WindowManagerImpl,而WindowManagerImpl里面所有的操作都是通过桥接模式转交给WindowManagerGlobal来完成的,在WindowManagerImpl里面是存在两个与删除Window有关的方法的,一个是removeView一个是removeViewImmediate,两者最终都会执行WindowManagerGlobal的removeView,但是是有区别的,具体区别查看源码注释:

/**
     * Special variation of {@link #removeView} that immediately invokes
     * the given view hierarchy's {@link View#onDetachedFromWindow()
     * View.onDetachedFromWindow()} methods before returning.  This is not
     * for normal applications; using it correctly requires great care.
     * 
     * @param view The view to be removed.
     */
    public void removeViewImmediate(View view);
        意思是removeViewImmediateremoveView的特殊版本,使用它会在removeView返回之前触发View树中View的onDetachedFromWindow和onDetachedFromWindow方法,一般情况下我们的应用程序是不会用这个方法删除Window的,使用的时候要格外的注意;

        因为上面的removeView和removeViewImmediate都是执行的WindowManagerGlobal的removeView,只不过就是第二个boolean参数值不同而已,因此只需要查看WindowManagerGlobal的removeView方法就可以了:

        代码不长,我们看下源码:

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);
        }
    }
        第7行会通过findViewLocked方法获取到要删除View的index值,这个获取过程是通过数组遍历实现的,接着就调用removeViewLocked来删除这个View,那必须要知道 removeViewLocked干了些什么啦,代码也不长,我贴出来了:

 private void removeViewLocked(int index, boolean immediate) {
        ViewRootImpl root = mRoots.get(index);
        View view = root.getView();

        if (view != null) {
            InputMethodManager imm = InputMethodManager.getInstance();
            if (imm != null) {
                imm.windowDismissed(mViews.get(index).getWindowToken());
            }
        }
        boolean deferred = root.die(immediate);
        if (view != null) {
            view.assignParent(null);
            if (deferred) {
                mDyingViews.add(view);
            }
        }
    }
        首先会获取到要删除View的index对应的ViewRootImpl,此时你应该就有预感了,既然你添加View到Window的时候用到了ViewRootImpl作为桥梁,那么在删除的时候,你就应该也用到ViewRootImpl作为桥梁了,后面你会发现确实是这样子的,有了ViewRootImpl之后就调用他的die方法啦,是这样吧;

        在die方法里面会根据我们boolean类型的参数immediate来判断到底是直接执行doDie()方法还是通过Handler发送一条带有MSG_DIE标志的消息,至于immediate参数值是什么时候传入的在一开始我们已经讲了WindowManagerImpl里面存在两个删除Window的方法removeView和removeViewImmediate,前者传入的immediate值是false,后者传入的immediate值是true,true跟false的区别其实就是同步和异步删除的区别了,但是不管用哪种方法,最终执行的都是ViewRootImpl的doDie()方法;

        这个ViewRootImpl的doDie()方法里面会做一下几件事情:

        (1):调用dispatchDetachedFromWindow方法,在dispatchDetachedFromWindow方法里面你会见到我们很熟悉的Session操作身影了,在addView就已经说过添加操作是通过Session来相当于建立通信渠道一样,由WindowManagerService来真正执行添加操作,那么这里删除操作情况也一样,也是由Session来建立通信渠道的,调用的是Session的remove方法来移除Window,而你查看Session的remove源码的话会发现实际上执行的还是WindowManagerService的removeWindow方法的,情况和addView完全一致;

        (2):最后还会执行WindowManagerGlobal的doRemoveView方法,直接查看doRemoveView源码你会发现其实就是将当前View从mViews中移出,将当前ViewRootImpl从当前mRoots移出,将当前LayoutParams从mParams移出而已了,因为你在创建的时候添加进去了嘛,删除的时候就该移出出去啊!

void doRemoveView(ViewRootImpl root) {
        synchronized (mLock) {
            final int index = mRoots.indexOf(root);
            if (index >= 0) {
                mRoots.remove(index);
                mParams.remove(index);
                final View view = mViews.remove(index);
                mDyingViews.remove(view);
            }
        }
    }
        酱紫的话,删除操作过程也结束啦,我们也来个小结呐:

        <1>:调用WindowManagerImpl的removeView或者removeViewImmediate来进行删除,但是后者的话我们要慎用啦,官方都这么建议了还是小心点啦,但是两者最终执行的都是WindowManagerGlobal的removeView方法;

        <2>:在WindowManagerGlobal的removeView方法里面,首先先找到我们要删除View对应的index索引值,有了这个index值之后我们便可以得到该index值对应的是哪个ViewRootImpl了,因为Window上的View的删除操作实际上还是由ViewRootImpl作为中介桥梁完成的;

        <3>:接下来的删除操作就转交给ViewRootImpl来完成,ViewRootImpl的删除实际上是通过创建ViewRootImpl的时候创建出来的Session来完成的,他好像是建立了一个通信通道一样,具体最后的删除操作是由WindowManagerService来完成的,这个过程中是有涉及到IPC通信的;

        <4>:在最后删除结束准备返回之前一定要记得将当前View从mViews列表中删除,将当前ViewRootImpl从mRoots删除,将当前LayoutParams从mParams中删除;

       最后就是在更新Window的时候发生什么啦?

        不用说,和前面一样,更新Window操作方法最终执行到的是WindowManagerGlobal的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);
        }
    }
        其实我们可以想想什么情况下我们会进行更新Window操作,就是在我们Window里面的View的LayoutParams属性发生变化的时候嘛,要是让我们实现的话,肯定就是先把新属性赋值给View,随后重绘View就可以啦,其实源码里就是这么实现的,只不过做了点封装而已了;看第11行,将新的LayoutParams参数赋值给当前View,随后通过findViewLocked方法找到当前View的索引值,利用该索引值找到其对应的ViewRootImpl对象,然后更新我们的LayoutParams列表呀,其实就是把原来的删了,把新的填进去罢了;最后呢,执行了ViewRootImpl的setLayoutParams方法,怎么样,又看到了ViewRootImpl了吧,整个添加/删除/更新操作都是拿他作为中间桥梁的,在ViewRootImpl的setLayoutParams方法里面你会看到执行了scheduleTraversals方法,这个方法就会开启我们从DecorView的视图重绘工作,接着呢,还需要更新我们的Window视图呀,具体是通过scheduleTraversals调用performTraversals方法之后,在performTraversals方法里面执行的,在performTraversals方法里面执行了ViewRootImpl的relayoutWindow方法,而在relayoutWindow里面就会执行Session的relayout方法了,很可爱,我们又见到了Session啦,下一步不用想肯定就是执行的WindowManagerService的relayoutWindow方法来更新Window啦啦!

        至此,更新操作结束啦,我们也来小结一下:

        <1>:更新操作调用的是WindowManagerGlobal的updateViewLayout方法;

        <2>:这个方法里面首先会更新我们当前View的LayoutParams属性,接着会通过当前View找到片当前View所在的index索引值,有了这个索引值我们便可以找到当前View所处的ViewRootImpl啦,随后更新我们的LayoutParams列表;

        <3>:通过ViewRootImpl来进行具体的更新操作,具体实现是首先先是更新View啦,其实上就是重绘View而已,接着便是重绘Window了,重绘Window的话,实际上是通过创建ViewRootImpl的时候创建的Session对象来完成的,而Session的话只不过是提供了一个通道而已啦,具体的实现还是通过WindowManagerService来进行的;

        好了,至此对Window的创建/添加/删除/更新操作的源码机制分析完毕啦,当然我这里主要分析的是Activity这个应用级Window的整个过程把,实际上Dialog和Toast这两种Window的话个人感觉整个过程应该比较类似吧,以后有时间了再细细查看了,只要明白Window、ViewRootImpl、View、WindowManagerImpl、WindowManagerGlobal、Session、WindowManagerService之间的关系的话,感觉能理解的更好;

        本文只是简单的分析而已,不免会有疏漏错误之处,欢迎指正!!!!!!


    



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值