组件化
1 Gradle
1.1 打印信息
Gradle构建工具,Groovy脚本语言基本Java做了拓展,Gradle = JDK + Groovy
新建工程,在app的build.gradle中,添加:
println("hello gradle")
或
println "hello gradle"
同步时,在Build中可以看到打印出来的信息:
> Configure project :app
hello gradle
> Configure project :library
hello gradle
> Task :prepareKotlinBuildScriptModel UP-TO-DATE
BUILD SUCCESSFUL in 525ms
1.2 多module环境的管理
新建Android module:在项目上右键新建Android module,出现新的module叫做”library“,和app平级。
Gradle可以用来:
- 所有module的版本号和依赖的统一管理
- 生产环境和开发环境的分离
具体做法:
①在项目上右键,新建file,取名config.gradle:
// 添加多个自定义的属性,可以通过ext代码块
ext{
username = "simon"
}
②在根目录下的build.gradle头部加入自定义config.gradle,相当于layout布局中加入include
③在app和library的build.gradle中加入:
println "${username}"
或者
println "${rootProject.ext.username}"
④点击同步即可打印:
> Configure project :app
hello gradle
simon
> Configure project :library
hello gradle
simon
> Task :prepareKotlinBuildScriptModel UP-TO-DATE
BUILD SUCCESSFUL in 435ms
groovy是弱类型语言,体现在可以自由修改数据类型,并且打印:
rootProject.ext.username = 163
println "${rootProject.ext.username}"
真正的使用在项目中:
①扩展config.gradle的内容:
// 添加多个自定义的属性,可以通过ext代码块
ext{
username = "simon"
// 生产/开发环境
isRelease = true
// 建立Map存储,对象名,key都可以自定义,groovy糖果语法,灵活
androidId = [
compileSdkVersion : 30,
buildToolsVersion : "30.0.3",
minSdkVersion : 16,
targetSdkVersion : 30,
versionCode : 1,
versionName : "1.0"
]
appId = [
applicationId : "com.example.componentizationstudy",
library : "com.example.library",
]
// 生产/开发环境 URL
utl = [
"debug" : "https://11.22.33.44/debug",
"release" : "https://11.22.33.44/release"
]
supportLibrary = "28.0.0"
// 第三方库
dependencies = [
"appcompat":"androidx.appcompat:appcompat:1.1.0",
"material":"com.google.android.material:material:1.1.0",
"constraintlayout":"androidx.constraintlayout:constraintlayout:1.1.3",
"junit":"androidx.test.ext:junit:1.1.1",
"espresso":"androidx.test.espresso:espresso-core:3.2.0"
]
}
②在app和library的build.gradle中,定义变量:
// 赋值与引用
def androidId = rootProject.ext.androidId
def appId = rootProject.ext.appId
def support = rootProject.ext.dependencies
android部分:
android {
compileSdkVersion androidId.compileSdkVersion
buildToolsVersion androidId.buildToolsVersion
defaultConfig {
applicationId appId.applicationId
minSdkVersion androidId.minSdkVersion
targetSdkVersion androidId.targetSdkVersion
versionCode androidId.versionCode
versionName androidId.versionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies部分:
dependencies {
// 依赖library库
implementation project(":library")
testImplementation 'junit:junit:4.+'
implementation support.appcompat
implementation support.material
implementation support.constraintlayout
androidTestImplementation support.junit
androidTestImplementation support.espresso
或者:
implementation project(":library")
testImplementation 'junit:junit:4.+'
support.each {k, v->implementation v} // 最简洁的方法,一句话搞定
}
依赖的两种写法:
// 标准写法
implementation group:"androidx.appcompat", name:"appcompat", version:"1.1.0"
// 简写
implementation 'androidx.appcompat:appcompat:1.1.0'
1.3 在BuildConfig.java文件中添加一个变量
BuildConfig.java是一个配置文件,会打包到apk中,文件在工程中的位置是:
module下的build-->generated-->source-->buildConfig-->debug-->com.example.componentizationstudy-->BuildConfig.java
在app的build.gradle中:
主要是这句话:
buildConfigField("String", "url", "\"${url.debug}\"");
加上之后是:
android{
buildTypes {
debug{
buildConfigField("String", "url", "\"${url.debug}\"");
}
release {
minifyEnabled false
buildConfigField("String", "url", "\"${url.release}\"");
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
2 组件化项目详细部署
2.1 组件化的意义
1-面试
2-开发需求-不互相依赖,可以相互交互,任意组合,高度解耦
3-团队效率:分模块打包、测试,统一版本管理
2.2 Phone Module 和 Android Library的区别、切换
Phone Module:组件化
(1)是个模块,可以独立运行
(2)apply plugin:'com.android.application'
(3)有applicationId
Android Library: 集成化
(1)是个安卓库,不能独立运行
(2)apply plugin:'com.android.library'
(3)无applicationId
2.3 组件化和集成化:
组件化,多个模块作为Phone module可以独立运行,一个项目可以打包出多个apk
集成化,多个模块作为Android Library不可以独立运行,一个项目打包出一个apk
两者可以通过gradle切换
①扩展config.gradle的内容:
// 添加多个自定义的属性,可以通过ext代码块
ext{
username = "simon"
// 生产/开发环境
// false:组件化模式,子模块可以独立运行
// true:集成化模式,子模块不能独立运行
isRelease = true
// 建立Map存储,对象名,key都可以自定义,groovy糖果语法,灵活
androidId = [
compileSdkVersion : 30,
buildToolsVersion : "30.0.3",
minSdkVersion : 16,
targetSdkVersion : 30,
versionCode : 1,
versionName : "1.0"
]
appId = [
app : "com.example.componentizationstudy",
library : "com.example.library",
order : "com.example.order",
personal : "com.example.personal",
]
// 生产/开发环境 URL
url = [
"debug" : "https://11.22.33.44/debug",
"release" : "https://11.22.33.44/release"
]
// 第三方库
dependencies = [
"appcompat":"androidx.appcompat:appcompat:1.1.0",
"material":"com.google.android.material:material:1.1.0",
"constraintlayout":"androidx.constraintlayout:constraintlayout:1.1.3",
"junit":"androidx.test.ext:junit:1.1.1",
"espresso":"androidx.test.espresso:espresso-core:3.2.0"
]
}
② order模块build.gradle
if(isRelease) {
apply plugin:'com.android.library'
}else{
apply plugin:'com.android.application'
}
// 赋值与引用
def androidId = rootProject.ext.androidId
def appId = rootProject.ext.appId
def support = rootProject.ext.dependencies
android {
compileSdkVersion androidId.compileSdkVersion
buildToolsVersion androidId.buildToolsVersion
defaultConfig {
if(!isRelease){ // 组件化模式,可独立运行,有applicationId
applicationId appId.order
}
minSdkVersion androidId.minSdkVersion
targetSdkVersion androidId.targetSdkVersion
versionCode androidId.versionCode
versionName androidId.versionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
buildConfigField("boolean", "isRelease", String.valueOf(isRelease))
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
// 循环引入第三方库
support.each{k, v->implementation v}
// 公共基础库
implementation project(":common")
}
③ personal模块的build.gradle
if(isRelease){
apply plugin : 'com.android.library'
}else{
apply plugin : 'com.android.application'
}
// 赋值与引用
def androidId = rootProject.ext.androidId
def appId = rootProject.ext.appId
def support = rootProject.ext.dependencies
android {
compileSdkVersion 30
buildToolsVersion "30.0.3"
defaultConfig {
if(!isRelease){ // 组件化模式,可独立运行,有applicationId
applicationId appId.personal
}
minSdkVersion 16
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
buildConfigField("boolean", "isRelease", String.valueOf(isRelease))
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
// 循环引入第三方库
support.each{k, v->implementation v}
// 公共基础库
implementation project(":common")
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
}
④ 全局的build.gradle
// Top-level build file where you can add configuration options common to all sub-projects/modules.
// 根目录下的build.gradle头部加入自定义config.gradle,相当于layout布局中加入include
apply from:"config.gradle"
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath "com.android.tools.build:gradle:4.1.2"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
⑤ 组件化与集成化的切换,通过Gradle动态切换
isRelease = true 点同步后,切成集成化模式
isRelease = false 点同步后,切成组件化模式
2.4 动态隔离,不把一些代码加载到apk中
① order和personal的build.gradle
android{
sourceSets{
main{
if(!isRelease){
// 组件化模式,需要单独运行时
manifest.srcFile 'src/main/debug/AndroidManifest.xml'
}else{
// 集成化模式,整个项目打包apk时
manifest.srcFile 'src/main/AndroidManifest.xml'
java{
// release时,debug目录下的文件不需要合并到主工程
exclude '**/debug/**'
}
}
}
}
}
② 以order为例
在order模块的src的main上右键,新建debug文件夹,复制order的AndroidManifest.xml到这个文件夹下一份;
在java下的包上右键,新建package,取名debug,把测试代码都放在这个debug文件夹下。
③ 将config.gradle中的isRelease标签改成true,打包成apk,发现apk中没有debug中的代码
2.5 Phone module引用Android Module
在common中写的activity,可以在order中和personal中被继承。
3 Module和Module之间的交互
3.1 Module之间的交互方式
- EventBus。难以实现:EventBean非常多(一对一),因为一对多就会混乱不堪、难以维护
- 反射。可以实现,但维护成本高,且易出现高版本@hide限制
- 隐式意图。维护成本一般,但较为麻烦,需要维护Manifest中的action
- BroadCastReceiver。需要动态注册(7.0后),需求方发送广播
- 类加载。需要准确的全类目路径,维护成本高,易出错
建议的实现方案:
3.2 方案1:类加载
从app的MainActivity.java跳转到order和personal两个模块:
public void jumpOrder(View view){
Intent intent = new Intent(this, Order_MainActivity.class);
intent.putExtra("name", "simon");
startActivity(intent);
}
public void jumpPersonal(View view){
Intent intent = new Intent(this, Personal_MainActivity.class);
intent.putExtra("name", "simon");
startActivity(intent);
}
从order的Order_MainActivity跳到app和personal两个模块:
public void jumpApp(View view){
// 类加载方式交互
try {
Class targetClass = Class.forName("com.example.componentizationstudy.MainActivity");
Intent intent = new Intent(this, targetClass);
intent.putExtra("name", "simon");
startActivity(intent);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public void jumpPersonal(View view){
// 类加载方式交互
try {
Class targetClass = Class.forName("com.example.personal.Personal_MainActivity");
Intent intent = new Intent(this, targetClass);
intent.putExtra("name", "simon");
startActivity(intent);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
从personal的Personal_MainActivity跳到app和order两个模块:
public void jumpApp(View view){
// 类加载方式交互
try {
Class targetClass = Class.forName("com.example.componentizationstudy.MainActivity");
Intent intent = new Intent(this, targetClass);
intent.putExtra("name", "simon");
startActivity(intent);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public void jumpOrder(View view){
// 类加载方式交互
try {
Class targetClass = Class.forName("com.example.order.Order_MainActivity");
Intent intent = new Intent(this, targetClass);
intent.putExtra("name", "simon");
startActivity(intent);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
3.3 方案2:全局Map记录信息
在common中,新建PathBean.java
package com.example.common;
public class PathBean {
private String path;
private Class clazz;
public PathBean() {
}
public PathBean(String path, Class clazz) {
this.path = path;
this.clazz = clazz;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public Class getClazz() {
return clazz;
}
public void setClazz(Class clazz) {
this.clazz = clazz;
}
}
在common中,新建RecordPathManager.java
/**
* 全局路径记录器(根据子模块分组)
*/
public class RecordPathManager {
// key : "order"组
// value:order子模块下,对应所有的Activity路径信息
private static Map<String, List<PathBean>> groupMap = new HashMap<>();
public static void joinGroup(String groupName, String pathName, Class<?> clazz){
List<PathBean> list = groupMap.get(groupName);
if (list == null) {
list = new ArrayList<>();
list.add(new PathBean(pathName, clazz));
groupMap.put(groupName, list);
} else{
for (PathBean pathBean : list) {
if(!pathName.equalsIgnoreCase(pathBean.getPath())){
list.add(new PathBean(pathName, clazz));
groupMap.put(groupName, list);
}
}
}
}
/**
* 根据组名和路径名获取类对象,达到跳转目的
* @param groupName 组名
* @param pathName 路径名
* @return 跳转目标的class类对象
*/
public static Class<?> getTargetClass(String groupName, String pathName){
List<PathBean> list = groupMap.get(groupName);
if (list == null) {
return null;
}
for (PathBean pathBean : list) {
if(pathName.equalsIgnoreCase(pathBean.getPath())){
return pathBean.getClazz();
}
}
return null;
}
}
在app中新建AppApplication.java:
public class AppApplication extends BaseApplication {
@Override
public void onCreate() {
super.onCreate();
RecordPathManager.joinGroup("app", "MainActivity", MainActivity.class);
RecordPathManager.joinGroup("order", "Order_MainActivity", Order_MainActivity.class);
RecordPathManager.joinGroup("personal", "Personal_MainActivity", Personal_MainActivity.class);
}
}
并将其添加到AndroidMenifest.xml中:
android:name=".base.AppApplication"
在app中,修改跳转方式:
public void jumpApp(View view){
// 类加载方式交互
// try {
// Class targetClass = Class.forName("com.example.componentizationstudy.MainActivity");
// Intent intent = new Intent(this, targetClass);
// intent.putExtra("name", "simon");
// startActivity(intent);
// } catch (ClassNotFoundException e) {
// e.printStackTrace();
// }
// 全局Map加载
Class<?> targetClass = RecordPathManager.getTargetClass("app", "MainActivity");
if (targetClass == null) {
Log.e("djtest", "获取targetClass为空");
}
Intent intent = new Intent(this, targetClass);
intent.putExtra("name", "simon");
startActivity(intent);
}
public void jumpOrder(View view){
// Intent intent = new Intent(this, Order_MainActivity.class);
// intent.putExtra("name", "simon");
// startActivity(intent);
// 全局Map加载
Class<?> targetClass = RecordPathManager.getTargetClass("order", "Order_MainActivity");
if (targetClass == null) {
Log.e("djtest", "获取targetClass为空");
}
Intent intent = new Intent(this, targetClass);
intent.putExtra("name", "simon");
startActivity(intent);
}
public void jumpPersonal(View view){
// Intent intent = new Intent(this, Personal_MainActivity.class);
// intent.putExtra("name", "simon");
// startActivity(intent);
// 全局Map加载
Class<?> targetClass = RecordPathManager.getTargetClass("personal", "Personal_MainActivity");
if (targetClass == null) {
Log.e("djtest", "获取targetClass为空");
}
Intent intent = new Intent(this, targetClass);
intent.putExtra("name", "simon");
startActivity(intent);
}
4 组件化APT
注解处理器,Annotation Processing Tool
通俗理解:根据规则帮我们生成代码,生成类文件
结构体语言:element组成的结构体
<html>
<body>
<div>...</div>
<body>
<html>
java也是一种结构体语言:
package com.example.xxx; // PackageElement 包元素/节点
public class Main{ // TypeElement 类元素/节点
private int x; // VariableElement 属性元素/节点
private Main(){ // ExecuteableElement 方法元素/节点
}
private void print(String msg){
}
}
需要掌握的API
getEnclosedElements() 返回该元素直接包含的子元素
getEnclosingElement() 返回包含该element的父element,与上一个方法相反
getKind() 返回element的类型,判断是哪种element
getModifiers() 获取修饰关键字,如 public static final
getSimpleName() 获取名字,不带包名
getQualifiedName() 获取全名,如果是类的话,包含完整的包名路径
getParameters() 获取方法的参数元素,每个元素是一个VariableElement
getReturnType() 获取方法元素的返回值
getConstantValue() 如果属性变量被final修饰,则可以使用该方法获取它的值
4.1 手写ARouter注解
① 新建工程,在MainActivity上加注解,此时不会有用,因为还没写注解
@ARouter(path = "/app/MainActivity")
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
②新建Java Library,取名annotation,在其中新建ARouter,类型为Annotation
package com.example.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Activity使用的布局文件注解
*
* @Target(ElementType.TYPE) 接口、类、枚举、注解
* @Target(ElementType.FIELD) 属性、枚举的常量
* @Target(ElementType.METHOD) 方法
* @Target(ElementType.PARAMETER) 方法参数
* @Target(ElementType.CONSTRUCTOR) 构造函数
* @Target(ElementType.LOCAL_VARIABLE) 局部变量
* @Target(ElementType.ANNOTATION_TYPE) 该注解使用在另一个注解上
* @Target(ElementType.PACKAGE) 包
* @Retention(RetentionPolicy.RUNTIME) 该注解会在class字节码文件中存在,jvm加载时可以通过反射获取到该注解的内容
*
*
* 生命周期 SOURCE < CLASS < RUNTIME
* 1.一般如果需要在运行时去动态获取注解信息,用RUNTIME注解
* 2.要在编译时进行一些预处理操作,如ButterKnife,用CLASS注解,注解会在class文件中存在,但是在运行时会被丢弃
* 3.要做一些检查性的工作,如@Override,用SOURCE源码注解,注解仅存在源码级别,在编译的时候丢弃该注解
*
*/
@Target(ElementType.TYPE) // 作用在类上
@Retention(RetentionPolicy.CLASS) // 没有执行run之前,在编译期的预编译操作
public @interface ARouter {
// 详细路由路径(必填),如"/app/MainActivity"
String path();
// 从path中截取出来,规范开发者的编码
String group() default "";
}
③ 在app的build.gradle中添加依赖
④ 在根目录下的build.gradle中添加阿里云镜像,加快下载速度:
repositories {
// 超级实用:阿里云镜像更新
maven{
url "http://maven.aliyun.com/nexus/content/groups/public/"
}
google()
jcenter()
}
⑤ 新建Java Library,取名compiler,新建ARouterProcessor类用作ARouter注解的处理器,在注解处理器中读取注解内容,生成对应的类文件。
@AutoService(Processor.class)
@SupportedAnnotationTypes("com.example.annotation.ARouter") // 注解处理器支持的注解类型,需要把ARouter放进来,才能在process中处理, 必须是全类名
@SupportedSourceVersion(SourceVersion.RELEASE_7) // 你需要用到什么jdk的版本来进行编译,生成这个class,必填!
@SupportedOptions("content")
public class ARouterProcessor extends AbstractProcessor {
private Elements elementUtils; // 操作Element工具类
private Types typesUtils; // type类信息工具类
private Messager messager; // 用来输出警告、错误等日志
private Filer filer; // 文件生成器
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
// 初始化工作
super.init(processingEnvironment);
elementUtils = processingEnvironment.getElementUtils();
typesUtils = processingEnvironment.getTypeUtils();
messager = processingEnvironment.getMessager();
filer = processingEnvironment.getFiler();
String content = processingEnvironment.getOptions().get("content");
// 有坑,不能像Android中Log.e的写法
messager.printMessage(Diagnostic.Kind.NOTE, content);
}
// 通过注解替代
// @Override
// public Set<String> getSupportedAnnotationTypes() {
// // 注解处理器支持的注解类型,需要把ARouter放进来,才能在process中处理
// return super.getSupportedAnnotationTypes();
// }
//
// @Override
// public SourceVersion getSupportedSourceVersion() {
// // 你需要用到什么jdk的版本来进行编译,生成这个class,必填!
// return super.getSupportedSourceVersion();
// }
//
// @Override
// public Set<String> getSupportedOptions() {
// // 接收外面传来的参数
// return super.getSupportedOptions();
// }
/**
* 相当于main函数,开始处理注解
* 注解处理器的核心方法,处理具体的注解,生成java文件
*
* @param set 使用了支持处理注解的节点集合(类,上面写了注解)
* @param roundEnvironment 当前或是之前的运行环境,可以通过该对象查找找到的注解
* @return true 表示后续处理器不再处理(已经处理完成)
*/
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
if(set.isEmpty()){
return false;
}
// 获取项目中所有使用了ARouter注解的节点
Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(ARouter.class);
// 遍历所有的类节点
for (Element element : elements) {
// 类节点之上,就是包节点
String packageName = elementUtils.getPackageOf(element).getQualifiedName().toString();
// 获取简单类名
String className = element.getSimpleName().toString();
messager.printMessage(Diagnostic.Kind.NOTE, "备注接的类有:"+className);
// 最终想要生成的类文件,如“MainActivity$$ARouter”
String finalClassName = className + "$$ARouter";
try {
JavaFileObject sourceFile = filer.createSourceFile(packageName + "." + finalClassName); // 创建源文件
Writer writer = sourceFile.openWriter();
// 设置包名
writer.write("package "+packageName+";\n");
writer.write("public class "+finalClassName+" {\n");
writer.write("public static Class<?> findTargetClass(String path) {\n");
// 获取类之上@ARouter注解的path值
ARouter aRouter = element.getAnnotation(ARouter.class);
writer.write("if (path.equalsIgnoreCase(\""+aRouter.path()+"\")) {\n");
writer.write("return "+className+".class;\n}\n");
writer.write("return null;\n");
writer.write("}\n}");
// 非常重要
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return true;
}
}
compiler模块的build.gradle:
添加:
// 注册注解,并对其生成META-INF的配置信息
compileOnly 'com.google.auto.service:auto-service:1.0-rc4'
annotationProcessor 'com.google.auto.service:auto-service:1.0-rc4'
// 引入annotation,让注解处理器,处理注解
implementation project(':annotation')
// java控制台输出中文乱码
tasks.withType(JavaCompiler){
options.encoding = "UTF-8"
}
添加后全文如下:
import javax.tools.JavaCompiler
plugins {
id 'java-library'
}
dependencies{
implementation fileTree(dir:'libs', include:['*.jar'])
// 注册注解,并对其生成META-INF的配置信息
compileOnly 'com.google.auto.service:auto-service:1.0-rc4'
annotationProcessor 'com.google.auto.service:auto-service:1.0-rc4'
// 引入annotation,让注解处理器,处理注解
implementation project(':annotation')
}
// java控制台输出中文乱码
tasks.withType(JavaCompiler){
options.encoding = "UTF-8"
}
// Jdk编译的版本是1.7
java {
sourceCompatibility = JavaVersion.VERSION_1_7
targetCompatibility = JavaVersion.VERSION_1_7
}
app模块的build.gradle:
// 第一:在Gradle文件中配置选项参数值(用于APT传参接收)
defaultConfig {
// 切记:必须写在defaultConfig节点下
javaCompileOptions{
annotationProcessorOptions{
arguments = [content : 'hello apt']
}
}
}
// 第二:添加注解处理器
annotationProcessor project(':compiler')
⑥ 在app下,新建OrderActivity和PersonalActivity,在两者头上加ARouter注解
@ARouter(path = "/app/OrderActivity")
public class OrderActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_order);
Log.i("djtest", "OrderActivity");
}
public void jump(View view){
Class<?> targetClass = PersonalActivity$$ARouter.findTargetClass("/app/PersonalActivity");
startActivity(new Intent(this, targetClass));
}
}
@ARouter(path = "/app/PersonalActivity")
public class PersonalActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_personal);
Log.i("djtest", "PersonalActivity");
}
public void jump(View view){
Class<?> targetClass = MainActivity$$ARouter.findTargetClass("/app/MainActivity");
startActivity(new Intent(this, targetClass));
}
}
鼠标点击app,Build菜单中Make Module “ComponentAPT.app”,
在app/build/generated/ap_generated_sources/debug/out/com.example.componentapt/下生成:
(1)MainActivity$$ARouter.java
package com.example.componentapt;
public class MainActivity$$ARouter {
public static Class<?> findTargetClass(String path) {
if (path.equalsIgnoreCase("/app/MainActivity")) {
return MainActivity.class;
}
return null;
}
}
(2)OrderActivity$$ARouter.java
package com.example.componentapt;
public class OrderActivity$$ARouter {
public static Class<?> findTargetClass(String path) {
if (path.equalsIgnoreCase("/app/OrderActivity")) {
return OrderActivity.class;
}
return null;
}
}
(3)PersonalActivity$$ARouter.java
package com.example.componentapt;
public class PersonalActivity$$ARouter {
public static Class<?> findTargetClass(String path) {
if (path.equalsIgnoreCase("/app/PersonalActivity")) {
return PersonalActivity.class;
}
return null;
}
}
同理,在app/build/intermediates/javac/debug/classes/com/example/componentapt/下生成:
(1)MainActivity$$ARouter.class
package com.example.componentapt;
public class MainActivity$$ARouter {
public MainActivity$$ARouter() {
}
public static Class<?> findTargetClass(String path) {
return path.equalsIgnoreCase("/app/MainActivity") ? MainActivity.class : null;
}
}
(2)OrderActivity$$ARouter.class
package com.example.componentapt;
public class OrderActivity$$ARouter {
public OrderActivity$$ARouter() {
}
public static Class<?> findTargetClass(String path) {
return path.equalsIgnoreCase("/app/OrderActivity") ? OrderActivity.class : null;
}
}
(3)PersonalActivity$$ARouter.class
package com.example.componentapt;
public class PersonalActivity$$ARouter {
public PersonalActivity$$ARouter() {
}
public static Class<?> findTargetClass(String path) {
return path.equalsIgnoreCase("/app/PersonalActivity") ? PersonalActivity.class : null;
}
}
4.2 组件化APT高级用法JavaPoet
APT + JavaPoet = 超级利刃
JavaPoet的8个常用类
MethodSpec 代表一个构造函数或者方法声明
TypeSpec 代表一个类,接口,或者常量
FieldSpec 代表一个成员变量,一个字段声明
JavaFile 包含一个顶级类的Java文件
ParameterSpec 用来创建参数
AnnotationSpec 用来创建注解
ClassName 用来包装一个类
TypeName 类型,如在添加返回值类型是使用TypeName.VOID
JavaPoet字符串格式化规则:
$L 字面量, "int value=$L",10
$S 字符串, $S," hello"
$T 类、接口, $T,MainActivity
$N 变量, user.$N,name
① 新建工程,新建两个java Library,分别为annotation和compiler。
② 在annotation中,新建ARouter注解文件:
package com.example.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE) // 作用于类
@Retention(RetentionPolicy.CLASS) // 编译时进行一些预处理工作,存在于class文件中,运行时删除
public @interface ARouter {
String path();
}
③ 在compiler中,新建注ARouterProcessor解处理器文件:
package com.example.compiler;
import com.example.annotation.ARouter;
import com.google.auto.service.AutoService;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeSpec;
import java.io.IOException;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedOptions;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import javax.tools.Diagnostic;
@AutoService(Processor.class) // 注册注解处理器
@SupportedAnnotationTypes("com.example.annotation.ARouter") // 对哪种注解进行处理
@SupportedSourceVersion(SourceVersion.RELEASE_7) // 编译器类型
@SupportedOptions("content") // 额外的参数
public class ARouterProcessor extends AbstractProcessor {
private Elements eLementsUtils;
private Types typesUtils;
private Messager messager;
private Filer filer;
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
eLementsUtils = processingEnvironment.getElementUtils();
typesUtils = processingEnvironment.getTypeUtils();
messager = processingEnvironment.getMessager();
filer = processingEnvironment.getFiler();
String content = processingEnvironment.getOptions().get("content");
messager.printMessage(Diagnostic.Kind.NOTE, content); // 打印获得的参数
}
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
// 如果没有任何一处使用了@ARouter注解,那就不做任何处理
if (set.isEmpty()) {
return false;
}
// 获取所有被@ARouter注解的类节点
Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(ARouter.class);
// 遍历这些节点,每个节点生成一个类文件
for (Element element : elements) {
// 类节点的上一个节点:包节点
String packageName = eLementsUtils.getPackageOf(element).getQualifiedName().toString();
// 获取简单类名
String className = element.getSimpleName().toString();
messager.printMessage(Diagnostic.Kind.NOTE, "被@ARouter注解的类有:"+className);
// 最终生成的类文件名
String finalClassName = className + "$$ARouter";
ARouter aRouter = element.getAnnotation(ARouter.class);
// 方法:public static Class<?> findTargetClass(String path){}
MethodSpec methodSpec = MethodSpec.methodBuilder("findTargetClass") // 方法名
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.returns(Class.class) // 返回值类型是Class
.addParameter(String.class, "path") // 参数类型和参数名
// return path.equalsIgnoreCase("app/MainActivity") ? MainActivity.class : null;
.addStatement("return path.equalsIgnoreCase($S) ? $T.class : null",
aRouter.path(),
ClassName.get((TypeElement) element))
.build();
// 类:public class XActivity$$ARouter {}
TypeSpec typeSpec = TypeSpec.classBuilder(finalClassName) // 类名
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addMethod(methodSpec) // 将方法添加到类中
.build();
// 包:package com.example.componetaptjavapoet;
JavaFile javaFile = JavaFile.builder(packageName, typeSpec) // 包名,类名
.build();
// 写到文件中去
try {
javaFile.writeTo(filer);
} catch (IOException e) {
e.printStackTrace();
}
}
return true;
}
}
写注解生成类文件之前先将想要的类文件写出来:
在app下新建XActivity$$ARouter.java,之后就仿照这个类文件,在注解处理器中用代码生成:
package com.example.componetaptjavapoet;
public class XActivity$$ARouter {
public static Class<?> findTargetClass(String path){
return path.equalsIgnoreCase("app/MainActivity") ? MainActivity.class : null;
}
}
compiler的build.gradle:
plugins {
id 'java-library'
}
dependencies{
implementation fileTree(dir:'libs', include:['*.jar'])
// 注册注解,并对其生成META-INF的配置信息
compileOnly 'com.google.auto.service:auto-service:1.0-rc4'
annotationProcessor 'com.google.auto.service:auto-service:1.0-rc4'
// 帮助我们通过类调用的形式来生成JAVA代码
implementation 'com.squareup:javapoet:1.9.0'
// 依赖注解
implementation project(':annotation')
}
java {
sourceCompatibility = JavaVersion.VERSION_1_7
targetCompatibility = JavaVersion.VERSION_1_7
}
检查注解是否注册成功:
在compiler/build/classes/java/main/META-INF/services/下,打开文件,看到下列内容,说明注册成功。
com.example.compiler.ARouterProcessor
④ app中应用@ARouter注解
app的build.gradle中添加依赖
implementation project(':annotation')
annotationProcessor project(':compiler')
app中的MainActivity、OrderActivity和PersonalActivity:
MainActivity.java:
@ARouter(path="/app/MainActivity")
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.i("djtest", "MainActivity...");
}
public void jump(View view){
Class targetClass = OrderActivity$$ARouter.findTargetClass("/app/OrderActivity");
startActivity(new Intent(this, targetClass));
}
}
OrderActivity.java:
@ARouter(path="/app/OrderActivity")
public class OrderActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_order);
Log.i("djtest", "OrderActivity...");
}
public void jump(View view){
Class targetClass = PersonalActivity$$ARouter.findTargetClass("/app/PersonalActivity");
startActivity(new Intent(this, targetClass));
}
}
PersonalActivity.java:
@ARouter(path="/app/PersonalActivity")
public class PersonalActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_personal);
Log.i("djtest", "PersonalActivity...");
}
public void jump(View view){
Class targetClass = MainActivity$$ARouter.findTargetClass("/app/MainActivity");
startActivity(new Intent(this, targetClass));
}
}
5 组件化路由架构设计
组件化项目部署:
- 配置arouter_api/build.gradle
- 配置common/build.gradle
- 完成模拟APT生成的类代码
- app子模块中,模拟跳转代码
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAzNfRHLGZkRGZkRfJ3bs92YsYTMfVmepNHL4lERPFzZE90dNpHW4Z0MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnL3gjM2ITO1kTM3EzMwEjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
-
为什么需要组名?
只加载相关类,节省内存
-
生成这些文件有什么用?
根据path,获取类对象
根据组名,找到对应的list,根据从组名截取的path,获取类对象。