【长篇】Android组件化细讲+手写实现
Android开发进阶必看之【组件化架构技术精讲合集】B站搜这个视频。
什么是组件化,为什么要组件化
耦合、维护、扩展、项目大了编译慢
![](https://img.haomeiwen.com/i14355128/646068a91db43444.png)
公共层:glide、第三方sdk
系统层:蓝牙、
将原本业务逻辑拆分成业务模块
![](https://img.haomeiwen.com/i14355128/dc29fd35edc082a8.png)
好处:
![](https://img.haomeiwen.com/i14355128/cd9402b345cc6a94.png)
编译速度:只编译单个模块就好了
超集解耦:以前是包名,现在每个都是application,无耦合
功能重用
便于团队:彼此只要关注自己模块,定位bug
组件化要注意的几个点:
1要注意包名和资源文件命名冲突问题
2Gradle中的版本号统一管理
3组件在Application和library之间如何做到随意切换
4AndroidManifest.xml文件的区别
5Library不能在Gradle文件中有applicationId
Gradle中的版本号统一管理
第一种方式,定义在gradle.properties里变量(手册)
第二种方式,定义在build.gradle里类似json方式;
第三种方式,自己创建一个gradle,在主gradle去加载,跟第二种意思差不多
以下是第一种方式:
![](https://img.haomeiwen.com/i14355128/526c069dc64730bf.png)
第三种:
![](https://img.haomeiwen.com/i14355128/bc029d9352361d75.png)
所有定义在gradle.properties里变量都是String类型的,使用时要转int
使用时:
![](https://img.haomeiwen.com/i14355128/ec461ce231f03b73.png)
组件在Application和library之间如何做到随意切换
定义的这个变量起作用
![](https://img.haomeiwen.com/i14355128/74a8e08bab0dbef2.png)
![](https://img.haomeiwen.com/i14355128/d3959a2d0c800437.png)
AndroidManifest.xml文件的区别
![](https://img.haomeiwen.com/i14355128/30f745db06a488c0.png)
如何在不同的需求下加载不同的manifest呢?
每个组件都新建文件夹、copy,建立符合lib的manifest:
![](https://img.haomeiwen.com/i14355128/6e0f6e468c94e3de.png)
然后在每个相对应组件的build.gradle里加上如下代码,就可动态切换:
![](https://img.haomeiwen.com/i14355128/f6a1a78a9422d9fe.png)
Library不能在Gradle文件中有applicationId
此点容易被忽略
![](https://img.haomeiwen.com/i14355128/c82bc36eb4d0986f.png)
主Application不用修改applicationId、lib切换等(因为一直会是App)
![](https://img.haomeiwen.com/i14355128/f8629fa2d8ef3e86.png)
最后在主Application中:
![](https://img.haomeiwen.com/i14355128/6d93338338e2949b.png)
当然还有些细节点,比如
![](https://img.haomeiwen.com/i14355128/1f5ffe8852c968da.png)
也要跟着版本号变化而变化。
![](https://img.haomeiwen.com/i14355128/f580e24d2c5311f6.png)
![](https://img.haomeiwen.com/i14355128/6df13c2d40f635de.png)
APT技术实现手写ARouter框架
组件化开发中路由框架究竟是什么
各个组件虽然划分好了,但是组件间的跳转、通信问题需要解决。没有任何耦合,彼此之间联系不上。
![](https://img.haomeiwen.com/i14355128/fcd1ccf62a50cd68.png)
路由框架就是解决这个问题。彼此没有任何依赖的模块又能得到其他Activity。
思路:App一启动的时候,就在Applicaiton里将所有的Activity Class对象(并不是具体实例)装到一个容器里,其他模块要用时,直接去这个容器里拿。
创建一个lib 名字叫arouter,并创建ARouter类
![](https://img.haomeiwen.com/i14355128/ed3a446bea5a3f79.png)
之后每隔模块都implement arouter模块
![](https://img.haomeiwen.com/i14355128/a1a496ae613f8af7.png)
现在map里还没有数据,需要放进来。
思路:每隔模块创建一个额工具类,把activity加进来。为了更加规范,在arouter下定义IRouter接口:
![](https://img.haomeiwen.com/i14355128/4860c057404d8013.png)
login模块工具类举例(下面的注解处理器实际上就是在每个模块中动态生成这里面的代码):
![](https://img.haomeiwen.com/i14355128/985b12e4758fa40a.png)
然后调用就可以了。
但是不合理:1每个都要创建、2每次添加新的activity,管理时容易忘记修改这里。需要花心思管理。
通过注解扫描解决!!!
Android编译时注解---APT技术
APT(Annotation Processing Tool)
即注解解释器,是一种处理注解的工具,确切说他是javac的一个工具,用来在编译时扫描和处理注解。
简单来说就是在应用处于编译期的时候,通过注解处理器解析注解生成文件。
![](https://img.haomeiwen.com/i14355128/78ad35c299b3892a.png)
我们用编译时技术去动态生成Util类
创建注解和注解处理器
创建两个java library(不是Android library,因为注解和注解处理器都是java概念)
![](https://img.haomeiwen.com/i14355128/2007fde43f09e97a.png)
然后让每个模块都去依赖这两个库。
![](https://img.haomeiwen.com/i14355128/a686e504bffb33b4.png)
注解处理器要用annotationProcessor
编译时,读取到这段回去找注解处理器执行代码
注解的作用就是标记需要加入到map里面的类。
在annotation下创建一个BindPath注解:
![](https://img.haomeiwen.com/i14355128/473eba262083e643.png)
在activity里添加我们的注解,参数就是key
![](https://img.haomeiwen.com/i14355128/9d3d49b160d4c1be.png)
注解有了,然后再写我们的注解处理器
自定义一个AnnotationCompiler类
1,继承AbstractProcessor类,实现process()方法
2,将我们的类注册成注解处理器(加上一个标记注解就行了):在build中先导入谷歌service库,然后在类上添加库中注解@AutoService(Processor.class)
![](https://img.haomeiwen.com/i14355128/09eaba15111919b7.png)
![](https://img.haomeiwen.com/i14355128/ad8a7632acceaf7d.png)
完成上面两步,我们定义的注解处理器才会在编译时找到。
接下来填补我们的注解处理器类:
注解处理器要重写方法来声明哪些注解需要被这个处理器识别,所以先添加下依赖:
![](https://img.haomeiwen.com/i14355128/b01da5325628c8c6.png)
要重写4个方法,前三个都是准备工作,最后的process是核心处理部分:
1init():创建filer文件
2getSuppertedSourceVersion() :支持的java版本
3getSupportedAnnotationTypes():声明要识别的注解
4process():处理注解
注解处理器能得到被BindPath所标记的内容
![](https://img.haomeiwen.com/i14355128/8ee92ab2ea3094c4.png)
最后编译一下,或者添加一个login2 activity
![](https://img.haomeiwen.com/i14355128/54c3f73783199c18.png)
动态生成了我们之前写的Utils里的代码。
其他模块的Activity添加上我们的BinderPath注解即可
Ctrl+n
![](https://img.haomeiwen.com/i14355128/1964a17a2d63dd85.png)
编译后,每个模块都动态生成了Utils类。
工具类现在生成好了,但里面的方法还没有执行。
我们在Arouter下添加方法:
![](https://img.haomeiwen.com/i14355128/914d700b8ebdc1d0.png)
通过包名获取这个包下面所有的类名
然后在初始化时:
![](https://img.haomeiwen.com/i14355128/21f72015e2ad57ce.png)
方法就会被调用了。
然后ARouter的init是在Application里执行的,创建MyApplication,初始化ARouter。
![](https://img.haomeiwen.com/i14355128/31b7312503113ebf.png)
别忘了在Manifest里修改。
接下来运行即可
![](https://img.haomeiwen.com/i14355128/6727ed2ff41b009b.png)
一个第三方库,每个模块都要添加一次的话(比如30个),工程量很大。可以通过gradle 遍历数组
组件间通信
(待续...)