Andorid组件化跳转路由

Andorid组件化跳转路由

简单介绍

路由在组件化工程中有非常重要的作用,两个没有相互引用的module之间怎么通信呢。

可以使用EventBus,使用广播,使用类加载,使用反射,scheme,隐式意图等等,这些方法各自都有优缺点,现在开源用的比较多的路由框架中ARouter中使用的是类加载的方法下面我们也使用类加载的方式自己封装一个小路由。

那怎么使用类加载的方法来进行不同组件之间通信呢。很简单只要我们能拿到一个类的全类名就可以啦

比如,新建一个工程appcom.chs.mymodule.MainActivity,并创建两个module,一个订单组件com.chs.order.OrderActivity,一个积分组件com.chs.integral.IntegralActivity。现在从app模块跳到订单模块只需如下操作就可以啦

1
2
3
4
5
6
7
try {
Class<?> clzz = Class.forName("com.chs.order.OrderActivity");
Intent intent = new Intent(this,clzz);
startActivity(intent);
} catch (Exception e) {
e.printStackTrace();
}

如果每次都这么写类名的话有点麻烦,各种点容易写错。那可不可以封装一下,其实我们只要拿到OrderActivity中的类名就可以了。

所以我们可以把这些需要跳转的类保存起来,比如保存到一个map中,跳转的时候就通过key拿到类名,然后继续通过Intent跳转。

简单实现

首先新建一个实体类用来保存路径和对应的类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class PathBean {
private String path;
private Class<?> clzz;

public PathBean() {
}

public PathBean(String path, Class<?> clzz) {
this.path = path;
this.clzz = clzz;
}

public String getPath() {
return path;
}

public void setPath(String path) {
this.path = path;
}

public Class<?> getClzz() {
return clzz;
}

public void setClzz(Class<?> clzz) {
this.clzz = clzz;
}
}

然后新建一个管理类,用来保存注册过来的路径,和类信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class RecordPathManager {

public static Map<String, List<PathBean>> groupMap = new HashMap<>();

public static void joinGroup(String groupName,String pathName,Class<?> clzz){
List<PathBean> pathBeans = groupMap.get(groupName);
if(pathBeans == null){
pathBeans = new ArrayList<>();
pathBeans.add(new PathBean(pathName,clzz));
groupMap.put(groupName,pathBeans);
}else {
if(!isExit(pathName,pathBeans)){
pathBeans.add(new PathBean(pathName,clzz));
}
groupMap.put(groupName,pathBeans);
}
}
private static boolean isExit(String pathName,List<PathBean> list){
for (PathBean bean : list) {
if(pathName.equals(bean.getPath())){
return true;
}
}
return false;
}

public static Class<?> getTargetClass(String groupName,String pathName){
List<PathBean> list = groupMap.get(groupName);
if(list == null){
return null;
}else {
for (PathBean bean : list) {
if(pathName.equalsIgnoreCase(bean.getPath())){
return bean.getClzz();
}
}
}
return null;
}
}

该类非常简单,一个Map,一个存的方法和一个取的方法。为了提高效率这里分一下组,每一个module是一个组,key就是这个module的名字,value就是该组下面的类信息的集合。

最后是怎么存呢,当然是存的越早越好,要不然还没存好就跳转显然拿不到相关的类。所以我们在app模块中的application中存

应用打包的时候,app模块肯定是依赖的所有的模块,所以子模块中的类它肯定也能拿到。所以注册的代码如下

1
2
3
4
5
6
7
8
9
10
public class App extends BaseApplication {

@Override
public void onCreate() {
super.onCreate();
RecordPathManager.joinGroup("order","OrderActivity", OrderActivity.class);
RecordPathManager.joinGroup("integral","IntegralActivity", IntegralActivity.class);
......
}
}

将需要跳转的类activity的模块名,类名和类信息保存到管理类的Map中。使用的时候如下

1
2
3
4
5
6
7
try {
Class<?> clzz = RecordPathManager.getTargetClass("integral","IntegralActivity");
Intent intent = new Intent(this,clzz);
startActivity(intent);
} catch (Exception e) {
e.printStackTrace();
}

看起来比开始简单了一点点,不过这个在activity中一个一个的注册也是太麻烦,就几个类还好,几十上百的类估计会把自己写吐了,那能不能让系统帮我们写注册的代码呢,当然可以啦,这时候就用到APT的技术来实现啦。

使用APT自动完成注册。

下面仿照ARouter来实现一个简单跳转的路由,

项目结构如上图:

  • annotation是注解module 是个java module
  • compiler是注解处理器 必须是个java module
  • order和integral是两个字module
  • arouter用来定义存储的规范

先在annotation中定义一个编译时注解

1
2
3
4
5
6
7
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.TYPE)
public @interface ARouter {
String path();

String group() default "";
}

在定义一个RouterBean,相当于前面简单实现中的PathBean对象,用来存储路径,类,组名等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
public class RouterBean {

public enum Type{
/**
* activity类型
*/
ACTIVITY;
}

private Type mType;

/**
* 类结点
*/
private Element mElement;
/**
* 组名
*/
private String group;
/**
* 路由的地址
*/
private String path;

private Class<?> mClazz;


private RouterBean() {
}
private RouterBean(Type type, Class<?> clazz, String path, String group) {
this.mType = type;
this.mClazz = clazz;
this.path = path;
this.group = group;
}
public static RouterBean create(Type type,Class<?> clazz,String path,String group){
return new RouterBean(type,clazz,path,group);
}
private RouterBean(Builder builder) {
mElement = builder.mElement;
group = builder.group;
path = builder.path;
}

public Type getType() {
return mType;
}

public Element getElement() {
return mElement;
}

public String getGroup() {
return group;
}

public String getPath() {
return path;
}

public Class<?> getClzz() {
return mClazz;
}

public void setType(Type type) {
mType = type;
}

public void setGroup(String group) {
this.group = group;
}

public void setPath(String path) {
this.path = path;
}

public static final class Builder{
private Element mElement;
private String group;
private String path;

public Builder setElement(Element element) {
mElement = element;
return this;
}

public Builder setGroup(String group) {
this.group = group;
return this;
}

public Builder setPath(String path) {
this.path = path;
return this;
}
public RouterBean build(){
if(path==null||path.length()==0){
throw new IllegalArgumentException("path 为空 比如 /app/MainActivity");
}
return new RouterBean(this);
}
}

@Override
public String toString() {
return "RouterBean{" +
"group='" + group + '\'' +
", path='" + path + '\'' +
'}';
}
}

下面在arouter_api 这个module中定义两个接口ARouterLoadGroup和ARouterLoadPath。

一个工程中会有很多个module,如果每次把说有的类都加载比较耗费内存,所以设计一个组来管理,组名就是当前的module的名字,它下面管理着该module下的所有注册的activity类,只有用户到了该module的时候才会加载

我们生成类会继承自这两个接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
*key:"app", value:"app"分组对应的路由详细对象类
*/
public interface ARouterLoadGroup {

Map<String,Class<? extends ARouterLoadPath>> loadGroup();

}
public interface ARouterLoadPath {
/**
* 比如传入 /app/MainActivity 通过app找到对应的组,然后通过/app/MainActivity找到对应的class
*/
Map<String, RouterBean> loadPath();
}

下面来到了重头戏注解管理器compiler中。

先在其gradle文件中引入相关的工具

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
apply plugin: 'java-library'

dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])

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控制台输出中文乱码
tasks.withType(JavaCompile) {
options.encoding = "UTF-8"
}

sourceCompatibility = "7"
targetCompatibility = "7"

auto-service是谷歌提供的可以用来自动触发注解管理器运行。

javapoet是著名的square公司出品,用来生成java类的工具

javapoet有8个常用的类

类对象 说明
MethodSpec 代表一个构造函数或者方法声明
TypeSpec 代表一个类,接口,或者枚举声明
FieldSpec 代表一个成员变量,一个字段的声明
JavaFile 包含一个顶级的Java文件
ParameterSpec 用来创建参数
AnnotationSpec 用来创建注解
ClassName 用来包装一个类
TypeName 类型,比如添加返回值类型使用TypeName.VOID

JavaPort字符串的格式化规则

符号 描述

$L|字面量 比如 “int value=$L”,10
$S|字符串 比如 $S,”hello”
$T|类、接口 比如 $T,MainActivity
$N|变量 比如 user.$N,name

上面两个表中的数据比较重要,下面写注解管理器的时候都会用到。

具体用法可以到https://github.com/square/javapoet中查看

因为相关的类是我们自己生成的,所以事先我们肯定知道它最后的样子,先看看需要生成的类的最后的样子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class ARouter$$Group$$app implements ARouterLoadGroup {
@Override
public Map<String, Class<? extends ARouterLoadPath>> loadGroup() {
Map<String,Class<? extends ARouterLoadPath>> groupMap = new HashMap<>();
groupMap.put("app",ARouter$$Path$$app.class);
return groupMap;
}
}
public class ARouter$$Path$$app implements ARouterLoadPath {
@Override
public Map<String, RouterBean> loadPath() {
Map<String,RouterBean> pathMap = new HashMap<>();
pathMap.put("/app/Main2Activity",RouterBean.create(RouterBean.Type.ACTIVITY,Main2Activity.class,"/app/Main2Activity","app"));
pathMap.put("/app/MainActivity",RouterBean.create(RouterBean.Type.ACTIVITY,MainActivity.class,"/app/MainActivity","app"));
return pathMap;
}
}

分别继承前面定义的两个接口,一个用来初始化该组的map,并存入路径信息,一个是路径信息类,初始化并保存相关的路径和类信息。

需要注意的是第一个类中使用了第二个类,所以生成类的时候要先生成第二个类,然后在生成第一个类

下面比对着上面的两个类开始编写注解管理器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
@AutoService(Processor.class)
//这里是注解的全类名比如 com.chs.annotation.ARouter,这里保存到一个常量中了
@SupportedAnnotationTypes(Const.ACTIVITY_ANNOTATION_TYPE)
@SupportedSourceVersion(SourceVersion.RELEASE_7)
@SupportedOptions({Const.MODULE_NAME,Const.APT_PACKAGE})
public class ARouterProcessor extends AbstractProcessor {
/**
* 结点工具类 类 函数 属性都是element
*/
private Elements elementUtils;
/**
* 类信息工具
*/
private Types typeUtils;
/**
* 打印工具类
*/
private Messager mMessager;
/**
* 文件生成器
*/
private Filer mFiler;
/**
* 组名
*/
private String moduleName;
/**
* 生成的apt文件的路径 包名
*/
private String packageNameForAPT;

/**
* 临时存放路径的类 生成的时候遍历
* key 组名 如app value 组的路由路径 如 ARouter$$Path$$app.class
*/
private Map<String, List<RouterBean>> tempPathMap = new HashMap<>();
/**
* 临时存放组的类
* 比如 key app value ARouter$$Path$$app
*/
private Map<String, String> tempGroupMap = new HashMap<>();


@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
elementUtils = processingEnv.getElementUtils();
typeUtils = processingEnv.getTypeUtils();
mMessager = processingEnv.getMessager();
mFiler = processingEnv.getFiler();

Map<String, String> options = processingEnv.getOptions();
mMessager.printMessage(Diagnostic.Kind.NOTE,options.toString());
if(!EmptyUtils.isEmpty(options)){
moduleName = options.get(Const.MODULE_NAME);
packageNameForAPT = options.get(Const.APT_PACKAGE);
mMessager.printMessage(Diagnostic.Kind.NOTE,"moduleName:"+moduleName);
mMessager.printMessage(Diagnostic.Kind.NOTE,"packageNameForAPT:"+packageNameForAPT);
}
if(EmptyUtils.isEmpty(moduleName)||EmptyUtils.isEmpty(packageNameForAPT)){
throw new IllegalArgumentException("注解处理器需要的参数 module或packageName为空,需要在gradle中配置");
}

}

@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
if(!EmptyUtils.isEmpty(set)){
//获取所有被ARouter注解的元素集合
Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(ARouter.class);
if(!EmptyUtils.isEmpty(elements)){
try {
parseElements(elements);
} catch (IOException e) {
e.printStackTrace();
}
}
return true;
}
return false;
}

/**
* 解析所有别ARouter注解的元素集合
* @param elements
*/
private void parseElements(Set<? extends Element> elements) throws IOException {
//通过element工具获取Activity的类型 activity的路径为 android.app.Activity
TypeElement typeElement = elementUtils.getTypeElement(Const.ACTIVITY);
//获取activity的TypeMirror
TypeMirror activityTypeMirror = typeElement.asType();
for (Element element : elements) {
//获取每个元素的类信息
TypeMirror elementTypeMirror = element.asType();
mMessager.printMessage(Diagnostic.Kind.NOTE,"遍历的元素信息为:"+elementTypeMirror.toString());

//获取每个类上的ARouter 注解对应的path值
ARouter aRouter = element.getAnnotation(ARouter.class);
//封装RouterBean
RouterBean routerBean = new RouterBean.Builder()
.setGroup(aRouter.group())
.setPath(aRouter.path())
.setElement(element)
.build();
//防止用户乱写 判断注解是作用在activity上面
if(typeUtils.isSubtype(elementTypeMirror,activityTypeMirror)){
routerBean.setType(RouterBean.Type.ACTIVITY);
}else {
throw new IllegalArgumentException("@ARouter 目前只能用在Activity上面");
}

//存放到临时的map中 方便后面组装
valueOfPathMap(routerBean);
}
//ARouterLoadGroup和ARouterLoadPath的类型
TypeElement groupElementType = elementUtils.getTypeElement(Const.AROUTER_GROUP);
TypeElement pathElementType = elementUtils.getTypeElement(Const.AROUTER_PATH);
//1 先生成路由的path文件 比如 ARouter$$Path$$order
createPathFile(pathElementType);
//2 在生成路由的组文件 比如 ARouter$$GROUP$$order 因为组中用到了上面的类
createGroupFile(groupElementType,pathElementType);
}

/**
* 生成group对应的path ARouter$$Path$$order
* @param pathElementType
*/
private void createPathFile(TypeElement pathElementType) throws IOException {
if(EmptyUtils.isEmpty(tempPathMap)){
return;
}
//方法的返回值Map<String, RouterBean>
TypeName typeName = ParameterizedTypeName.get(
ClassName.get(Map.class),ClassName.get(String.class),ClassName.get(RouterBean.class));
//遍历分组,每一个分组创建一个路径文件ARouter$$Path$$order
for (Map.Entry<String, List<RouterBean>> entry : tempPathMap.entrySet()) {
//方法体 public Map<String, RouterBean> loadPath() {}
MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder(Const.PATH_METHOD_NAME)
.addAnnotation(Override.class)
.addModifiers(Modifier.PUBLIC)
.returns(typeName);
//Map<String, RouterBean> pathMap = new HashMap<>();
methodBuilder.addStatement("$T<$T,$T> $N = new $T<>()",ClassName.get(Map.class)
,ClassName.get(String.class),ClassName.get(RouterBean.class),Const.PATH_PARAMA_NAME
,HashMap.class);
List<RouterBean> values = entry.getValue();
for (RouterBean bean : values) {
// pathMap.put("/order/Order_MainActivity",RouterBean.create(RouterBean.Type.ACTIVITY
// , Order_MainActivity.class,"/order/Order_MainActivity","order"));
methodBuilder.addStatement("$N.put($S,$T.create($T.$L,$T.class,$S,$S))",
Const.PATH_PARAMA_NAME,
bean.getPath(),
ClassName.get(RouterBean.class),
ClassName.get(RouterBean.Type.class),
bean.getType(),
ClassName.get((TypeElement) bean.getElement()),
bean.getPath(),
bean.getGroup());
}
methodBuilder.addStatement("return $N",Const.PATH_PARAMA_NAME);

String finalClassName = Const.PATH_FILD_NAME + entry.getKey();
mMessager.printMessage(Diagnostic.Kind.NOTE,
"APT生成的path类为"+packageNameForAPT+finalClassName);
JavaFile.builder(packageNameForAPT,
TypeSpec.classBuilder(finalClassName)
//该类实现的接口
.addSuperinterface(ClassName.get(pathElementType))
.addModifiers(Modifier.PUBLIC)
//方法体
.addMethod(methodBuilder.build())
.build())
.build()
.writeTo(mFiler);

tempGroupMap.put(entry.getKey(),finalClassName);
}
}

/**
* 生成group ARouter$$Group$$order
* @param groupElementType
* @param pathElementType
*/
private void createGroupFile(TypeElement groupElementType, TypeElement pathElementType) throws IOException {
if(EmptyUtils.isEmpty(tempPathMap)||EmptyUtils.isEmpty(tempGroupMap)){
return;
}
//public Map<String, Class<? extends ARouterLoadPath>> loadGroup() {
TypeName methodReturn = ParameterizedTypeName.get(ClassName.get(Map.class),ClassName.get(String.class)
,ParameterizedTypeName.get(ClassName.get(Class.class),
//Class<? extends ARouterLoadPath>
WildcardTypeName.subtypeOf(ClassName.get(pathElementType))));
//方法体 public Map<String, RouterBean> loadPath() {}
MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder(Const.GROUP_METHOD_NAME)
.addAnnotation(Override.class)
.addModifiers(Modifier.PUBLIC)
.returns(methodReturn);
// Map<String, Class<? extends ARouterLoadPath>> groupMap = new HashMap<>();
methodBuilder.addStatement("$T<$T,$T> $N = new $T<>()",
ClassName.get(Map.class)
,ClassName.get(String.class),
ParameterizedTypeName.get(ClassName.get(Class.class),
WildcardTypeName.subtypeOf(ClassName.get(pathElementType))),
Const.GROUP_PARAMA_NAME
,HashMap.class);
//groupMap.put("order",ARouter$$Path$$order.class);
for (Map.Entry<String, String> entry : tempGroupMap.entrySet()) {
methodBuilder.addStatement("$N.put($S,$T.class)",
Const.GROUP_PARAMA_NAME,
entry.getKey(),
//指定的包名下
ClassName.get(packageNameForAPT,entry.getValue()));
}
methodBuilder.addStatement("return $N",Const.GROUP_PARAMA_NAME);
String finalClassName = Const.GROUP_FILD_NAME + moduleName;
mMessager.printMessage(Diagnostic.Kind.NOTE,
"APT生成的group类为"+packageNameForAPT+finalClassName);
JavaFile.builder(packageNameForAPT,
TypeSpec.classBuilder(finalClassName)
//该类实现的接口
.addSuperinterface(ClassName.get(groupElementType))
.addModifiers(Modifier.PUBLIC)
//方法体
.addMethod(methodBuilder.build())
.build())
.build()
.writeTo(mFiler);
}

private static boolean isExit(String pathName,List<RouterBean> list){
for (RouterBean bean : list) {
if(pathName.equalsIgnoreCase(bean.getPath())){
return true;
}
}
return false;
}
private void valueOfPathMap(RouterBean routerBean) {
if(checkRouterPath(routerBean)){
mMessager.printMessage(Diagnostic.Kind.NOTE,"routerBean对象"+routerBean.toString());
//开始放入map
List<RouterBean> list = tempPathMap.get(routerBean.getGroup());
if(EmptyUtils.isEmpty(list)){
list = new ArrayList<>();
list.add(routerBean);
tempPathMap.put(routerBean.getGroup(),list);
}else {
if(!isExit(routerBean.getPath(),list)){
list.add(routerBean);
}
}
}else {
mMessager.printMessage(Diagnostic.Kind.ERROR,"@ARouter注解没有按照规范些 /app/MainActivity");
}

}

private boolean checkRouterPath(RouterBean routerBean) {
String path = routerBean.getPath();
String group = routerBean.getGroup();
if(EmptyUtils.isEmpty(path)||!path.startsWith("/")){
mMessager.printMessage(Diagnostic.Kind.ERROR,"@ARouter注解没有按照规范些 /app/MainActivity");
return false;
}
if(path.lastIndexOf("/")== 0){
mMessager.printMessage(Diagnostic.Kind.ERROR,"@ARouter注解没有按照规范些 /app/MainActivity");
return false;
}
String finalGroup = path.substring(1,path.indexOf("/",1));
mMessager.printMessage(Diagnostic.Kind.NOTE,"finalGroup:"+finalGroup);

if(finalGroup.contains("/")){
mMessager.printMessage(Diagnostic.Kind.ERROR,"@ARouter注解没有按照规范些 /app/MainActivity");
return false;
}
if(!EmptyUtils.isEmpty(group)&&!group.equalsIgnoreCase(moduleName)){
mMessager.printMessage(Diagnostic.Kind.ERROR,"group必须是当前模块的名字");
return false;
}else {
routerBean.setGroup(finalGroup);
}
return true;
}
}
public class EmptyUtils {
public static boolean isEmpty(CharSequence c){
return c==null||c.length()==0;
}
public static boolean isEmpty(Collection<?> c){
return c==null||c.isEmpty();
}
public static boolean isEmpty(final Map<?,?> c){
return c==null||c.isEmpty();
}
}
//Const类中都是一些常量字符串。

上面类上的@SupportedOptions 这个注解可以接收从build.grale中传过来的参数。比如我们在build.gradle中把module的名字和最后生成的类的包名传过来

1
2
3
4
5
6
//rootProject.ext.packageNameForAPT是在gradle中定义的常量com.chs.module.apt
javaCompileOptions {
annotationProcessorOptions {
arguments = [moduleName: project.getName(), packageNameForAPT: rootProject.ext.packageNameForAPT]
}
}

在每个需要生成类的module中都引入注解和注解管理器

1
2
implementation project(':annotation')
annotationProcessor project(':complier')

最后重新build工程,在对应module的build文件夹下就会生成相应的类了,比如appmodule中在路径 \app\build\generated\source\apt\debug\com.chs.module.apt下面就可以看到生成的类ARouter$$Group$$app和ARouter$$Path$$app

最后使用生成的类来实现跳转

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
ARouterLoadGroup group = new ARouter$$Group$$order();
Map<String, Class<? extends ARouterLoadPath>> map = group.loadGroup();
// 通过order组名获取对应路由路径对象
Class<? extends ARouterLoadPath> clazz = map.get("order");

try {
// 类加载动态加载路由路径对象
ARouter$$Path$$order path = (ARouter$$Path$$order) clazz.newInstance();
Map<String, RouterBean> pathMap = path.loadPath();
// 获取目标对象封装
RouterBean bean = pathMap.get("/order/Order_MainActivity");

if (bean != null) {
Intent intent = new Intent(this, bean.getClzz());
intent.putExtra("name", "lily");
startActivity(intent);
}
} catch (Exception e) {
e.printStackTrace();
}

最后集成打包的时候 所有子模块app、order、integral通过APT生成的类文件都会打包到apk里面,不用担心找不到

运行之后会看到跳转成功,不过跳转一次写这么多代码好像不是我们想要的啊,我们理想中的跳转应该是下面的样子

1
2
3
RouterManager.getInstance().build("/personal/Personal_MainActivity")
.withString("name","chs")
.navigation(this);

这就需要我们把前面的跳转的代码封装一下啦

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
public class RouterManager {

private static RouterManager instence;
private LruCache<String, ARouterLoadGroup> groupCache;
private LruCache<String, ARouterLoadPath> pathCache;
private String path;
private String group;
private static final String GROUP_PRFIX_NAME = ".ARouter$$Group$$";

public static RouterManager getInstance(){
if(instence == null){
synchronized (RouterManager.class){
if(instence == null){
instence = new RouterManager();
}
}
}
return instence;
}

public RouterManager() {
groupCache = new LruCache<>(100);
pathCache = new LruCache<>(100);
}

public BundleManager build(String path) {
if (TextUtils.isEmpty(path) || !path.startsWith("/")) {
throw new IllegalArgumentException("未按规范配置,如:/app/MainActivity");
}
group = subFromPath2Group(path);
this.path = path;
return new BundleManager();
}

/**
* 判断path 的格式是否符合规范
* @param path 需要当行的路径
* @return
*/
private String subFromPath2Group(String path) {
if(path.lastIndexOf("/") == 0){
throw new IllegalArgumentException("@ARouter注解未按规范配置,如:/app/MainActivity");
}
String finalGroup = path.substring(1, path.indexOf("/", 1));
if (TextUtils.isEmpty(finalGroup)) {
throw new IllegalArgumentException("@ARouter注解未按规范配置,如:/app/MainActivity");
}
return finalGroup;
}

public Object navigation(Context context, BundleManager bundleManager, int code) {
String groupClassName = context.getPackageName() + ".apt" + GROUP_PRFIX_NAME + group;
Log.e("chs >>> ", "groupClassName -> " + groupClassName);

try {
ARouterLoadGroup aRouterLoadGroup = groupCache.get(groupClassName);
if(aRouterLoadGroup == null){
Class<?> clazz = Class.forName(groupClassName);
aRouterLoadGroup = (ARouterLoadGroup) clazz.newInstance();
groupCache.put(groupClassName,aRouterLoadGroup);
}
// 获取路由路径类ARouter$$Path$$app 的map
if (aRouterLoadGroup.loadGroup().isEmpty()) {
throw new RuntimeException("路由加载失败");
}

ARouterLoadPath aRouterLoadPath = pathCache.get(path);
if(aRouterLoadPath == null){
Class<?> clazz = aRouterLoadGroup.loadGroup().get(group);
if (clazz != null){
aRouterLoadPath = (ARouterLoadPath) clazz.newInstance();
pathCache.put(path,aRouterLoadPath);
}
}

if(aRouterLoadPath.loadPath().isEmpty()){
throw new RuntimeException("路由路径加载失败");
}
RouterBean routerBean = aRouterLoadPath.loadPath().get(path);
if (routerBean != null) {
switch (routerBean.getType()){
case ACTIVITY:
Intent intent = new Intent(context, routerBean.getClzz());
intent.putExtras(bundleManager.getBundle());

if (bundleManager.isResult()) {
((Activity) context).setResult(code, intent);
((Activity) context).finish();
}else {
if (code > 0) { // 跳转时是否回调
((Activity) context).startActivityForResult(intent, code, bundleManager.getBundle());
} else {
context.startActivity(intent, bundleManager.getBundle());
}
}
break;
}
}

}catch (Exception e) {
e.printStackTrace();
}

return null;
}
}

上面代码中的核心类其实就是前面的跳转的代码

  • 通过传入的path路径,截取出group的名字
  • 然后根据组的名字,包名,和定义好的前缀拼接出完成的组类的名字,然后通过类加载实例化出这个对象
  • 实例完成之后调用其loadGroup方法,根据group找出存path的map,最后拿到相关的class执行跳转。
  • 为了提高运行的效率,使用两个LruCache类来缓存组类和path类,先去这里面取,取不到在创建。

我们在activity中跳转的时候,有时候会传递参数,所以前面的代码中创建了一个类BundleManager来管理参数,将参数封装到Bundle中传递,BundleManager类很简单就是封装了一下Bundle。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public class BundleManager {

private Bundle bundle = new Bundle();
private boolean isResult;

public Bundle getBundle() {
return bundle;
}

public boolean isResult() {
return isResult;
}

public BundleManager withString(@NonNull String key, @Nullable String value) {
bundle.putString(key, value);
return this;
}

public BundleManager withResultString(@NonNull String key, @Nullable String value) {
bundle.putString(key, value);
isResult = true;
return this;
}
public BundleManager withBoolean(@NonNull String key, boolean value) {
bundle.putBoolean(key, value);
return this;
}
public BundleManager withInt(@NonNull String key, int value) {
bundle.putInt(key, value);
return this;
}

public BundleManager withBundle(@NonNull Bundle bundle) {
this.bundle = bundle;
return this;
}

public Object navigation(Context context) {
return RouterManager.getInstance().navigation(context,this,-1);
}
public Object navigation(Context context,int code) {
return RouterManager.getInstance().navigation(context,this,code);
}
}

OK到这里一个简单的跳转路由就完成了。

# 架构

コメント

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×