一、组件化基础
1.1、组件化与模块化区别
模块:模块指的是独立的业务模块,比如首页模块、商城模块等。
组件:组件指的是单一的功能组件,如 登录组件、支付组件 等,每个组件都可以以一个单独的 module 开发。
由此来看,模块 和 组件 间最明显的区别就是模块相对与组件来说粒度更大,一个模块中可能包含多个组件。模块化是业务导向,组件化是功能导向。
1.2、Gradle
Gradle相信大家都很熟悉,作为Android的构建系统,每天的开发工作都在直接或间接的接触着。了解Gradle会帮助你更全面、更细致的理解组件化的构建过程。以下是我在学习Gradle相关书籍的笔记:
1.3、依赖
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')//1
implementation 'com.android.support:appcompat-v7:28.0.0'//2
implementation project(':annotation')//3
}
基本的三种依赖方式:
1)Jar dependency:通过Gradle配置引入lib文件夹下的所有.jar后缀的文件,还能引入.aar后缀的文件。读入自身目录使用的是fileTree。
2)仓库配置依赖网络库;
3)Base Module:实质是将其打包成aar文件,方便其他库进行依赖;“:annotation”冒号的意思是文件目录内与自己相同层级的其他Module。
1.4、重新认识AndroidManifest
每个lib Module作业完成后,编译器在build/outputs/aar目录中生成一个aar文件:
我们知道,Application Module依赖很多lib module,当编译主Module时,会将这些功能Module重新编译,然后将成果(aar)放到主Module的intermediates文件夹中。这也是为什么我们主Module要添加lib module依赖的原因,组件化开发依然如此。主Module会依赖各个业务Module组件,但是各个业务Module组件是完全解耦的,各个业务Module组件可能会依赖共同的基础功能组件。
a)AndroidManifest
每个module都有一份配合的AndroidManifest文件,最终生成的App时,其只有一份AndroidManifest文件。那么如何记录这么多个module独立的配置信息呢?答案就是将多个AndroidManifest文件合并成一个AndroidManifest文件。
b)AndroidManifest - Application
和AndroidManifest文件一样,最终生成的App只允许声明一个Application到AndroidManifest中。那么替换规则如下:
- 如果业务Module有Application,主Module没有自定义Application,这时会自然引用业务Module中的Application;
- 如果主Module有自定义Application,其他Module没有,则自动引动主Module的Application;
- 如果业务Module有多个自定义的Application,在解决冲突后,Application最终会载入编译后的Module的Application;
- 如果主Module有自定义的Application,其他业务Module也有自定义的Application,在解决冲突后,最后编译的主Module的Application会在AndroidManifest里面。
Application合并的过程中,一些重复属性(android:icon、android:theme等)的声明可能引发冲突导致合并失败,解决的方式就是toos:replace。
c)AndroidManifest - 权限
与上述类似,合并各个Module声明的所有权限。重复声明的权限只会被声明一次。
1.5、组件间跳转
a)隐式跳转
何为隐式跳转,相信大家都很熟悉了,这里就不详细解释了。需要说明的是,Android官网中提示,使用隐式Intent跳转需要验证是否会接收Intent,需要对Intent对象调用resolveActivity(),如果结果为非空,则至少有一个应用能够处理该Intent,且可以安全调用startActivity;如果结果为空,则不应该使用该Intent。
确保只有自己的App能启动组件,需要设置exported=false,其他app将无法跳转到我们的App中。
隐式跳转是原生的,它和广播一样,范围是整个Android系统都能接收到。
b)路由跳转
核心原理将在另一篇博客中详细说明,相关路由框架,比如ARouter。
1.6、Android打包流程
打包步骤如下:
- 通过aapt打包res资源文件,生成R.java、resources.arsc和res文件(二进制 & 非二进制如res/raw和pic保持原样)
- 处理.aidl文件,生成对应的Java接口文件
- 通过Java Compiler编译R.java、Java接口文件、Java源文件,生成.class文件
- 通过dex命令,将.class文件和第三方库中的.class文件处理生成classes.dex
- 打包:通过apkbuilder工具,将aapt生成的resources.arsc和res文件、assets文件和classes.dex一起打包生成apk
- 签名:通过Jarsigner工具,对上面的apk进行debug或release签名
- 对齐:通过zipalign工具,将签名后的apk进行对齐处理。
为什么要了解打包流程呢?
因为路由组件生成的路由表入口文件的查找是从如上图红框的位置进行的,即运行时扫描dex文件。此时的作用时机是已经生成dex字节码了。而另外还有一种方式,即通过gradle插件的方式来完成,时机是在class生成dex字节码之前(上图标注的2),Android允许我们直接修改或生成我们需要的class字节码文件,即ASM + Transform方式。下一篇分析路由组件的博客将采用第一种方式。
二、路由组件开发基础
开发类似Arouter的路由组件,需要做以下的技术准备:
- 编译时注解及注解处理器;
- JavaPoet及AutoService的使用;
- 本博文第一部分;