在Activity的启动过程中会执行ActivityThread#performLaunchActivity方法,其中调用Activity#attach。在attach()方法中实例化Activity持有的mWindow属性为Window的唯一实现类PhoneWindow。

    ActivityThread#performLaunchActivity    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {        ...        activity.attach(...);        ...    }    Activity#attach    final void attach(...) {        ...        mWindow = new PhoneWindow(this, window);        mWindow.setWindowManager(...);        mWindowManager = mWindow.getWindowManager();        ...    }

在ActivityThread#handleResumeActivity中,通过ActivityThread#performResumeActivity方法调用Activity#onResume之后,将会获取DecorView并通过WindowManager添加进ViewRootImpl。

    final void handleResumeActivity(...) {        ...        r = performResumeActivity(token, clearHide, reason);        r.window = r.activity.getWindow();        View decor = r.window.getDecorView();        decor.setVisibility(View.INVISIBLE);        ViewManager wm = a.getWindowManager();        a.mDecor = decor;        wm.addView(decor, l);        ...        r.activity.makeVisible();        ...    }     PhoneWindow#getDecorView     public final View getDecorView() {        if (mDecor == null || mForceDecorInstall) {            installDecor();        }        return mDecor;    }

r.activity.getWindow()返回的就是在Activity#attach中实例化的PhoneWindow对象。之后调用PhoneWindow#getDecorView去获取DecorView对象,具体过程参见 Android setContentView()源码解析。然后填充decorView但是设置为不可见(通过r.activity.makeVisible()设置可见),最后通过WindowManager#addView将decorView添加进ViewRootImpl。ViewRootImpl下文会仔细分析,关于addView的过程参考Android 使用WindowManager实现悬浮窗及源码解析。细心的同学可能会注意到这时的DecorView刚通过WindowManger#addView添加到ViewRoot,其实这也解释了为什么说“在onCreate至onResume过程中,Activity已经对系统可见,但是还没有展示到界面上”的原因。这时因为Activity被装载而且已经执行完attach、onCreate、onStart、甚至执行完onResume,但是DecorView始终没有被添加到Window上。onCreate、onStart、onResume中获取不到控件宽高也是因为这个原因,在WindowManager#addView(内部实例化ViewRoot)之后才会真正的执行layout、measure、draw。

DecorView是FrameLayout的子类,说白了也是继承自View。其余XML中的View/ViewGroup都是被添加进DecorView属于“将View/ViewGroup添加进ViewGroup”类型,不具有代表性,感兴趣的可以参考Android XML布局文件解析过程源码解析。

如果在Activity#onCreate中调用setContentView()方法,那么会直接创建DecorView。如果没调用setContentView()方法,那么在ActivityThread#handleResumeActivity中会通过r.window.getDecorView()自动创建DecorView。总之,不管你创建于否,Activity中总会存在由PhoneWindow创建的DecorView。PhoneWindow的作用就是操作View,Activity调用findViewById类似的方法都是通过PhoneWindow间接操作View。如此,DecorView作为View树的顶级视图通过PhoneWindow便和Activity关联了起来。

在WindowManger#addView的过程中,调用了ViewRootImpl#addView。

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

首先将传递进来的View赋值给全局属性mView,之后用mView属性来操作DecorView。mWindowSession.addToDisplay的过程在Android 使用WindowManager实现悬浮窗及源码解析中已经解析过,简述作用就是将window添加进WindowManagerService的mWindowMap属性进行管理。requestLayout()这个方法就厉害了,可能你会自定义View,可能你看过View绘制的三大流程(measure、layout、draw),可是你知道这些东西的源头吗?没错,都在requestLayout()这里。插个题外话,通过上述的代码可以发现,PhoneWindow也不是直接操作DecorView,中间还隔着个ViewRootImpl。

    @Override    public void requestLayout() {        if (!mHandlingLayoutInLayoutRequest) {            checkThread();            mLayoutRequested = true;            scheduleTraversals();        }    }    void checkThread() {        if (mThread != Thread.currentThread()) {            throw new CalledFromWrongThreadException(                    "Only the original thread that created a view hierarchy can touch its views.");        }    }

通常在子线程中更新UI,会爆出checkThread中的错误。但是仔细看这个方法,mThread是在初始化ViewRootImpl时实例化的,等于创建ViewRootImpl的线程。正常情况下我们是在主线程创建的ViewRootImpl,实际上在子线程中也是可以创建的,例如创建Toast。Toast也是一种window,参考Android 高级自定义Toast及源码解析。但是Thread.currentThread()为更新UI时的线程。这两个如果同时在子线程会怎么样呢?什么也不会发生。是的,子线程是可以更新UI的。但是这里对UI的创建线程有要求,只有子线程创建的ViewRootImpl可以在子线程中更新UI。子线程更新UI的示例代码如下:

        new Thread(new Runnable() {            @Override            public void run() {                Looper.prepare();                // Toast.makeText(MainActivity.this,"一口仨馍",Toast.LENGTH_SHORT).show();                new AlertDialog.Builder(MainActivity.this)                        .setTitle("Game of Thrones")                        .setMessage("winter is coming")                        .setPositiveButton("yes,my lord", null)                        .show();                Looper.loop();            }        }).start();

scheduleTraversals()方法经过层层调用(mTraversalRunnable->doTraversal->performTraversals)

    private void performTraversals() {        final View host = mView;        performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);        performLayout(lp, mWidth, mHeight);        performDraw();    }    private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {        mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);    }     private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,            int desiredWindowHeight) {         final View host = mView;         host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());           }    private void performDraw() {        // draw(fullRedrawNeeded) --> drawSoftware        mView.draw(canvas);    }

performTraversals方法巨长,这里只截取View绘制三大流程的起点。mView就是之前缓存的DecorView。之后便开始了View的measure、layout、draw、onMeasure、onLayout、ondraw。。。

更多Framework源码解析,请移步 Framework源码解析系列[目录]

更多相关文章

  1. ubuntu环境下反编译android apk
  2. android获取手机cpu是单核还是多核的方法
  3. Theme.Holo主题 中tab同时显示图片和文字
  4. Android中NDK的so文件产生和使用
  5. Fragment Management
  6. Fragment 在Android(安卓)SDK1.6上实现
  7. Error:The module 'app' is an Android(安卓)project without bu
  8. 【android】binder机制-servicemanager
  9. 转android项目开发 工作日志 2011.10.8--可获取值控件

随机推荐

  1. 【Android】树形菜单、扩展下拉菜单andro
  2. Android(安卓)基础知识之JobScheduler基
  3. 如何申请个人Google API Key用于Android
  4. Android(安卓)自定义对话框Dialog
  5. ContactsContract获取联系人信息
  6. android studio 3.5.1 使用第三方.so 库
  7. [Mac]Android(安卓)Studio使用 Gradle 进
  8. Unity与Android交互-案例演示接入穿山甲
  9. Android学习--深入探索RemoteViews
  10. Android(安卓)4.4堆叠结构的变化