对于Android UI的学习在之前也针对性的进行过一些博客记录,但是还是没能系统全面的进行深入,有些零散,而在Android应用开发中它又是非常之重要的“硬”技能,所以接下来重新定下目标,要对它进行全面系统深入的再学习总结之路,目标是不管是对于面试还是实际工作中只要是涉及到UI效果这块都能做到心中有数,来对这块的硬技能的护城河加固再加固,对于新目标的开篇少了不要对UI绘制流程有一个整体的了解,这里是参考https://www.jianshu.com/p/0f6b4bc86c7b这位大神的简书为向导进行学习,拿来不可耻,但是重点是要利用网络上的各种好资源为自己所用,形成自己的知识体系。费话不多说,全面开启UI的系统学习。
先来引用博主开篇所说:
![](https://img-blog.csdnimg.cn/img_convert/335f11cb3c080f7b540828f59a29bb62.png)
思路很清晰,所以接下来就来搞清楚这三个疑问则为这篇博文的全部内容。
分析前提示:
这次分析源码最好有以下基础:
1、Handler机制,之前总结过一篇:https://www.cnblogs.com/webor2006/p/11630538.html
2、Binder机制,之前总结过二篇:https://www.cnblogs.com/webor2006/p/11741743.html、https://www.cnblogs.com/webor2006/p/11811650.html
1.Android程序是如何启动,Activity生命周期如何调用?
对于APP启动时都会知道它会调用ActivityThread.main()方法,在之前的Android Framework层源码的分析中也多次看到了,下面还是从它分析起:
![](https://img-blog.csdnimg.cn/img_convert/b34b946ac6ea6a2911b08f6feed50178.png)
此时则会调用到这个类的attach()方法,查看里面的核心代码:
![](https://img-blog.csdnimg.cn/img_convert/a8ed96ada746cad6c9e818b310c21d2f.png)
此时咱们看一下ActivityManager.getService()的细节:
![](https://img-blog.csdnimg.cn/img_convert/70cf6ad278e46d15efae211e4572e11d.png)
关于Binder机制这块就不多说了,很明显可以看到ActivityManager是由系统服务所调用管理,并且通过在binder接口当中进行调用,这也是为什么我们讲Activity是跨进程访问的原因。下面咱们来看一下IActivityManager这个AIDL中的asInterface()的具体细节。发现在Android Studio中打不开打,这里引用博主的图:
![](https://img-blog.csdnimg.cn/img_convert/49950a1fb0973619862f54d735b92b7d.png)
此时再回到主流上来继续往下分析:
![](https://img-blog.csdnimg.cn/img_convert/959cdf9be64ff31831bff5c8e5eb3e18.png)
其中mAppThread为:
![](https://img-blog.csdnimg.cn/img_convert/9ae906ebcaf602553dce66e2e5789c25.png)
此时来看一下它的细节:
![](https://img-blog.csdnimg.cn/img_convert/17f187870bd782d7a44265409dcc0419.png)
还是看不到它的源码,这里继续引用博主的:
![](https://img-blog.csdnimg.cn/img_convert/ef704017382d3735f223529ad61e8b68.png)
![](https://img-blog.csdnimg.cn/img_convert/0eec2ceccaf6f84135af409fca7e508b.png)
然后大致纵览一下ApplicationThread中的方法,里面有很多schedule开头的方法:
![](https://img-blog.csdnimg.cn/img_convert/6f162ededd4a6d0870e5cb336d9dd762.png)
貌似跟咱们的Activity的生命周期有关系,其中有一个跟启动Activity相关的方法:
![](https://img-blog.csdnimg.cn/img_convert/61054f07bfc68aa7771097309f42b19c.png)
其中在发送Handler消息时生成了一个ActivityClientRecord,这其实就是我们的Activity,好,接下来得处理这个LAUNCH_ACTIVITY消息了,如下:
![](https://img-blog.csdnimg.cn/img_convert/8d21ba4d7e22c6ae466fa1345c073147.png)
也就是说咱们的Activity是由ApplicationThread来通过Handler来进行启动的,下面则来看一下这个handleLaunchActivity()的具体启动细节:
![](https://img-blog.csdnimg.cn/img_convert/bdaae8cc3f81d5a0e4346791878f971c.png)
好,再来看一下这个方法的细节:
![](https://img-blog.csdnimg.cn/img_convert/01b88ee21cedee6f30d12d3f3bd81c44.png)
然后再调用了Activity的attach方法:
![](https://img-blog.csdnimg.cn/img_convert/d78a06d9920273f34df6fee156608bb5.png)
然后再往下看,则会看到调用Activity.onCreate()的身影:
![](https://img-blog.csdnimg.cn/img_convert/15ab1f99807d9be1a2371c1e1f1b86e9.png)
![](https://img-blog.csdnimg.cn/img_convert/6798c8e9a9a1544ec1f7db524e4a1632.png)
![](https://img-blog.csdnimg.cn/img_convert/d12fe7ebd875b4f3ee3ed9a85d9dd994.png)
好,到目前咱们对于Application到Activity的启动流程已经有了一个整体的了解,接下来则需要将焦点关注在setContentView()是如何将我们的布局xml文件加载进来的了。
2.在Activity.onCreate()当中我们的setContentView是如何将UI文件加载?
咱们直接从入口开始分析:
![](https://img-blog.csdnimg.cn/img_convert/f4f1de44f023685f69bc741fcc7a0df8.png)
![](https://img-blog.csdnimg.cn/img_convert/4156175ed16e03151cceef21dc78dcf8.png)
其中这个getWindow()则是咱们都熟知的PhoneWindow,所以直接定位到PhoneWindow.setContentView(),看一下核心代码:
![](https://img-blog.csdnimg.cn/img_convert/3dc8a7e2339c059ea306c45510516515.png)
这里再一下installDecor()的细节:
![](https://img-blog.csdnimg.cn/img_convert/59d7dccd3b46cf7281c3084fae402c6a.png)
其中对于mDecor和mContentParent的定义:
![](https://img-blog.csdnimg.cn/img_convert/42dc4b97b5d5f108a1579bf2ae6f96d2.png)
好,下面来瞅一下生成的细节:
![](https://img-blog.csdnimg.cn/img_convert/a11d7299d675382149fb6379cd45177e.png)
没啥,直接new了一个DecorView,好再来看另一个生成细节:
![](https://img-blog.csdnimg.cn/img_convert/04ba196b17449a135bf735e8ba6e1497.png)
![](https://img-blog.csdnimg.cn/img_convert/d47c6dc7a55fde34ad9dbac99117fe46.png)
好,继续往下则可以看到貌似开始再选择xml布局文件了:
![](https://img-blog.csdnimg.cn/img_convert/11a3df8388472633a5282fdede91b145.png)
![](https://img-blog.csdnimg.cn/img_convert/8e7c395d86ffc4597e23429de212fe2d.png)
![](https://img-blog.csdnimg.cn/img_convert/521829f0e2e054615d56244c93e5cfe1.png)
而关于Activity、Window、PhoneWindow、DecorView、View之间的关系如博主描述的:
![](https://img-blog.csdnimg.cn/img_convert/c3a954b9288559bf674c036508c4aba9.png)
总结一下installDector()这个方法它其实实际上是在初始化两个视图容器,然后加载系统的R资源及特征,产生了一个基本布局。
好再回到主流程继续往下分析,也就是这块:
![](https://img-blog.csdnimg.cn/img_convert/506427479fe21422df764384ed49455b.png)
![](https://img-blog.csdnimg.cn/img_convert/ff56da47e225f56af7ddc793b3832dd5.png)
![](https://img-blog.csdnimg.cn/img_convert/5f7084d9af1eb6d22287578571639b37.png)
具体它的解析细节看一下流程图:
![](https://img-blog.csdnimg.cn/img_convert/2b496ce7b373ccfd2b622948578bfa9f.png)
具体解析细节这里就不看了,重点是对整个的UI绘制流程有一个了解。至此对于我们的xml布局文件是怎么加载的流程就清楚了,接下来则得看一下这些布局加载进来之后是如何呈现到手机上的。
3.UI是如何绘制的?
那绘制流程从哪分析起呢?很显然是从Activity.onResume()这块分析起,而起点当然还是在ActivityThread的那个H中了:
![](https://img-blog.csdnimg.cn/img_convert/13b32641df10939a9ea4862651c909d6.png)
其中它里面会将咱们的view添加到WindowManager中,如下:
![](https://img-blog.csdnimg.cn/img_convert/e9c3f001127b85729f8d7873e3a50cb1.png)
此时查看一下这个addView的细节:
![](https://img-blog.csdnimg.cn/img_convert/9b10acc7ac7dadeb198b5d8648080191.png)
其中WindowManager接口就继承了这个接口,而它的具体实现是WindowManagerImpl,所以:
![](https://img-blog.csdnimg.cn/img_convert/bdbd4f53415343b84b004cfd15cd061e.png)
其中mGlobal为:
![](https://img-blog.csdnimg.cn/img_convert/c080b2e0c0ef0c9c234802b462f8b265.png)
它的核心实现为:
![](https://img-blog.csdnimg.cn/img_convert/34da6d63a394532a089be59ef3f0d78c.png)
其中mView、mRoots、mParams三个成员变量为:
![](https://img-blog.csdnimg.cn/img_convert/7438844af5168f1011e724a169b67e6f.png)
mViews保存的是View对象,DecorView
mRoots保存和顶层View关联的ViewRootImpl对象
mParams保存的是创建顶层View的layout参数。
而WindowManagerGlobal类也负责和WMS通信。
好,回到主流程继续:
![](https://img-blog.csdnimg.cn/img_convert/0e8265c202868888a986ef613e880731.png)
跟进去看一下它的核心代码:
![](https://img-blog.csdnimg.cn/img_convert/bf98ede0e3dcd80b06675c56ea2324e7.png)
也就是说将ViewRoot作为了View的Parent,好,注意了,绘制的核心流程就要到了,此时需要回到setContentView的代码来了:
![](https://img-blog.csdnimg.cn/img_convert/f73b12f89000e9506461c9d876c69d68.png)
其实setLayoutParams它里面就涉及到绘制流程的代码了:
![](https://img-blog.csdnimg.cn/img_convert/b35c7d6a98b2ad9279eb7fd32667ccc5.png)
好,再来看一下它里面的细节:
![](https://img-blog.csdnimg.cn/img_convert/650dfe0242fd5b1e9a1297dee2453309.png)
而mParent在之前已经分析过了,叫ViewRootImpl,所以转到它里面来看一下:
![](https://img-blog.csdnimg.cn/img_convert/c857460698ddfdd8a9a8d86b99177d4f.png)
![](https://img-blog.csdnimg.cn/img_convert/018a9e231ef702d3e4b4cab4fe080954.png)
![](https://img-blog.csdnimg.cn/img_convert/7883d1820c26796cbdac6192d59d7afc.png)
![](https://img-blog.csdnimg.cn/img_convert/81c449b93543c050e05cb22ca3b8e7ca.png)
这里就正式进入UI绘制的流程了,所以接下来再来看一下它的细节:
![](https://img-blog.csdnimg.cn/img_convert/733e4456d5eb89abb672d62686adfa81.png)
![](https://img-blog.csdnimg.cn/img_convert/2e113b335af43981db035db2b57a5f48.png)
跟进去:
![](https://img-blog.csdnimg.cn/img_convert/644c1892431b6f038a79269756b99899.png)
![](https://img-blog.csdnimg.cn/img_convert/dc8a5604cdbe378c5e940ec42dd4663d.png)
关于这个View的测量细节下一次再来分析,目前先只把整个绘制流程给搞通,好,回到主流程继续分析:
![](https://img-blog.csdnimg.cn/img_convert/35e95210aeb326423487abb6c4246258.png)
接着再进行布局了,关于具体布局的系统实现细节这里也不分析了,待之后再进行,还是往下分析主流程,不然很容易绕晕了:
![](https://img-blog.csdnimg.cn/img_convert/ce46c922edf385efb8e67269f16e4738.png)
![](https://img-blog.csdnimg.cn/img_convert/75806abb88d898b50fe8a59b19d2e8c7.png)
![](https://img-blog.csdnimg.cn/img_convert/f38a7b0cf1743ce75250e0ea8bb26046.png)
对于上面绘制的大概流程用图再来回顾一下:
![](https://img-blog.csdnimg.cn/img_convert/ee531cc8f0f295c156ff5021b6adfca9.png)
至此,对于整个绘制的完整核心流程就分析完了,不过对于具体View的测量、布局、绘制的流程这里还没开始分析,待下次再继续。