Android 组件化】路由组件 ( 组件间共享的服务 )

网友投稿 663 2022-05-29

文章目录

一、组件间共享的服务

二、注解处理器添加对上述 " 组件间共享的服务 " 的支持

三、注解处理器 生成代码规则

四、完整注解处理器代码 及 生成的 Java 代码

1、注解处理器代码

2、app 模块中的注解类生成的 Java 源码

3、library2 模块中的注解类生成的 Java 源码

五、博客资源

组件化系列博客 :

【Android 组件化】从模块化到组件化

【Android 组件化】使用 Gradle 实现组件化 ( Gradle 变量定义与使用 )

【Android 组件化】使用 Gradle 实现组件化 ( 组件模式与集成模式切换 )

【Android 组件化】使用 Gradle 实现组件化 ( 组件 / 集成模式下的 Library Module 开发 )

【Android 组件化】路由组件 ( 路由组件结构 )

【Android 组件化】路由组件 ( 注解处理器获取被注解的节点 )

【Android 组件化】路由组件 ( 注解处理器中使用 JavaPoet 生成代码 )

【Android 组件化】路由组件 ( 注解处理器参数选项设置 )

【Android 组件化】路由组件 ( 构造路由表中的路由信息 )

【Android 组件化】路由组件 ( 使用 JavaPoet 生成路由表类 )

一、组件间共享的服务

路由除了支持 Activity 之外 , 还要支持 组件间共享的服务 如 工具类 , 逻辑功能 等 ;

注意 : 这里的 " 组件间共享的服务 "

不是 4 4 4 大组件中的 Service 组件

, 是

任意的 , 实现了 IService 接口的 Java 类

, 可以是工具类 , 业务逻辑 , 等等 ;

定义空的接口 IService , 令 需要共享的服务类 实现接口 , 该接口没有实际的意义 , 仅用于标记该接口需要纳入路由组件管理 , 起标记作用 ;

package kim.hsl.route_core.template; /** * 需要跨组件通信的服务需要实现该接口 * 用于标记服务 */ public interface IService { }

1

2

3

4

5

6

7

8

接口定义位置 :

跨组件调用时 , 需要暴露出一个接口 , 接口必须实现上述 IService 接口 , 用于作为标识 , 注解处理器中 , 通过判断该注解节点的类型是不是该接口的子类 , 如果是则生成 路由信息 , 加入到 路由表 中 ;

IService 接口仅用与 标识 服务是否在 组件间共享 ;

针对每个具体的服务 , 还要在 底层依赖库 中定义一系列的接口 , 这里的底层依赖库是所有的 Module 组件都要依赖的 Android Library Module 依赖库 ;

在其中定义一个接口 ComponentService , 继承 IService 接口 , 在该接口中定义一系列需要暴露的方法 ;

package kim.hsl.base; import kim.hsl.route_core.template.IService; /** * 暴露一个接口 */ public interface ComponentService extends IService { void doSomething(); }

1

2

3

4

5

6

7

8

9

10

该接口定义位置 : 所有的组件都依赖 base 依赖库 ;

在具体的组件中 , 实现上述 ComponentService 接口 , 并添加 @Route 注解 ;

package kim.hsl.library2; import android.util.Log; import kim.hsl.base.ComponentService; import kim.hsl.router_annotation.Route; @Route(path = "/library2/StringService") public class StringService implements ComponentService { @Override public void doSomething() { Log.i("StringService", "library2 组件中的 StringService 服务 "); } }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

该类定义位置 : 在任意模块都可以调用该类 ;

二、注解处理器添加对上述 " 组件间共享的服务 " 的支持

之前在注解处理器中 , 只支持 android.app.Activity 的路由节点添加 ;

// 获取 android.app.Activity 类型的注解节点 TypeElement activityElement = mElementUtils.getTypeElement("android.app.Activity"); // 判断 typeMirror 注解节点是否是 Activity 类型 if (mTypeUtils.isSubtype(element.asType(), activityElement.asType())) { // 该节点是 android.app.Activity 类型的 routeBean = new RouteBean( RouteBean.Type.ACTIVITY, // 路由对象类型 element, // 路由节点 null, // 类对象 route.path(), // 路由地址 route.group()); // 路由组 }

1

2

3

4

5

6

7

8

9

10

11

12

13

当前注解处理器中 , 支持 kim.hsl.route_core.template.IService 类型的 路由节点 添加 ;

// 获取 android.app.Activity 类型的注解节点 TypeElement activityElement = mElementUtils.getTypeElement("android.app.Activity"); // 获取 组件间共享服务 的接口, 该接口仅用于表示组件类型 TypeElement iServiceElement = mElementUtils.getTypeElement("kim.hsl.route_core.template.IService"); // 判断 typeMirror 注解节点是否是 Activity 类型 if (mTypeUtils.isSubtype(element.asType(), activityElement.asType())) { // 该节点是 android.app.Activity 类型的 routeBean = new RouteBean( RouteBean.Type.ACTIVITY, // 路由对象类型 element, // 路由节点 null, // 类对象 route.path(), // 路由地址 route.group()); // 路由组 }else if (mTypeUtils.isSubtype(element.asType(), iServiceElement.asType())) { // 该节点是 kim.hsl.route_core.template.IService 类型的 routeBean = new RouteBean( RouteBean.Type.ISERVICE, // 路由对象类型 element, // 路由节点 null, // 类对象 route.path(), // 路由地址 route.group()); // 路由组 }else{ // 该节点不是 android.app.Activity 类型的 throw new RuntimeException("@Route 注解节点类型错误"); }

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

三、注解处理器 生成代码规则

注解处理器的 process 方法调用 , 是按照 Module 模块进行的 ;

如果 Module 模块中有相关注解 , 传入的 Set set 参数不为空 , 就会进行相关处理 ;

如果 Module 模块中没有相关注解 , 传入的 Set set 参数为空 , 此时就不进行后续操作 ;

下图红色的 library1 模块中没有注解 ;

蓝色的 library2 模块中添加了 @Route(path = “/library2/StringService”) 注解 ;

绿色的 app 模块中添加了 @Route(path = “/app/MainActivity”) 注解 ;

Module 模块中 , 使用注解生成的源码 , 都在对应模块的 " build\generated\ap_generated_sources\debug\out\ " 目录中 ;

四、完整注解处理器代码 及 生成的 Java 代码

1、注解处理器代码

package kim.hsl.router_compiler; 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.ParameterSpec; import com.squareup.javapoet.ParameterizedTypeName; import com.squareup.javapoet.TypeSpec; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.TreeMap; 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.type.TypeMirror; import javax.lang.model.util.Elements; import javax.lang.model.util.Types; import javax.tools.Diagnostic; import kim.hsl.router_annotation.Route; import kim.hsl.router_annotation.model.RouteBean; import static javax.lang.model.element.Modifier.PUBLIC; // 注解处理器接收的参数 @SupportedOptions("moduleName") // 自动注册注解处理器 @AutoService(Processor.class) // 支持的注解类型 @SupportedAnnotationTypes({"kim.hsl.router_annotation.Route"}) // 支持的 Java 版本 @SupportedSourceVersion(SourceVersion.RELEASE_8) public class RouterProcessor extends AbstractProcessor { /** * 注解处理器中使用 Messager 对象打印日志 */ private Messager mMessager; /** * 用于写出生成的 Java 代码 */ private Filer mFiler; /** * 注解节点工具 */ private Elements mElementUtils; /** * 类工具 */ private Types mTypeUtils; /** * 获取的 moduleName 参数 */ private String mModuleName; /** * 管理路由信息 * 键 ( Key ) : 路由分组名称 * 值 ( Value ) : 路由信息集合 */ private HashMap> mGroupMap = new HashMap<>(); /** * 管理 路由表信息 * 键 ( Key ) : 组名 * 值 ( Value ) : 类名 */ private Map mRootMap = new TreeMap<>(); /** * 该函数在初始化时调用 , 相当于构造函数 * @param processingEnvironment */ @Override public synchronized void init(ProcessingEnvironment processingEnvironment) { super.init(processingEnvironment); // 获取打印日志接口 this.mMessager = processingEnvironment.getMessager(); // 测试日志打印 mMessager.printMessage(Diagnostic.Kind.NOTE, "Messager Print Log"); this.mFiler = processingEnvironment.getFiler(); this.mElementUtils = processingEnvironment.getElementUtils(); this.mTypeUtils = processingEnvironment.getTypeUtils(); // 获取 moduleName 参数 // 先获取 注解处理器 选项 Map options = processingEnvironment.getOptions(); if (options != null){ mModuleName = options.get("moduleName"); mMessager.printMessage(Diagnostic.Kind.NOTE, "打印 moduleName 参数 : " + mModuleName); } } /** * 该函数在注解处理器注册时自动执行, 是处理注解的核心函数 * * Set set 参数 : 该集合表示使用了相关注解的节点的集合 * * @param set * @param roundEnvironment * @return */ @Override public boolean process(Set set, RoundEnvironment roundEnvironment) { if (set == null || set.isEmpty()){ // 如果没有检测到注解 , 直接退出 return false; } // 获取被 @Route 注解的节点 // 这些 注解节点 都是类节点 , TypeElement 类型的 Set routeElements = roundEnvironment.getElementsAnnotatedWith(Route.class); generateRouteClass(routeElements); // 生成 路由组件 分组表 对应的 Java 类 generateGroupTable(); // 生成 路由组件 路由表 对应的 Java 类 return true; } /** * 生成 路由组件 分组表 对应的 Java 类 */ private void generateGroupTable() { // 获取要生成的类 需要实现的接口节点 TypeElement iRouteGroup = mElementUtils.getTypeElement( "kim.hsl.route_core.template.IRouteGroup"); // 打印类节点全类名 mMessager.printMessage(Diagnostic.Kind.NOTE, "打印 路由表 需要实现的接口节点 iRouteGroup : " + iRouteGroup.getQualifiedName()); // 生成参数类型 Map atlas ParameterizedTypeName atlasType = ParameterizedTypeName.get( ClassName.get(Map.class), ClassName.get(String.class), ClassName.get(RouteBean.class) ); // 生成参数 Map atlas ParameterSpec atlasValue = ParameterSpec.builder(atlasType, "atlas").build(); // 遍历 HashMap> mGroupMap = new HashMap<>() 路由分组 // 为每个 路由分组 创建一个类 for (Map.Entry> entry : mGroupMap.entrySet()){ // 创建函数 loadInto MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder("loadInto") .addModifiers(Modifier.PUBLIC) .addAnnotation(Override.class) .addParameter(atlasValue); // 函数体中的代码生成 // 获取 ArrayList 数据 ArrayList groupRoutes = entry.getValue(); // 组名 String groupName = ""; // 生成函数体代码 for (RouteBean routeBean : groupRoutes){ // 获取组名 groupName = routeBean.getRouteGroup(); // $S 表示字符串 // $T 表示类 // $L 表示字面量 , 原封不动的字符串替换 methodBuilder.addStatement("atlas.put($S, new $T($T.$L, $T.class, $S, $S))", // $S 字符串 : "main" routeBean.getRouteGroup(), // $T 类名 : RouteBean ClassName.get(RouteBean.class), // $T 类名 : Type ClassName.get(RouteBean.Type.class), // $L 字面量 : ACTIVITY routeBean.getType(), // $T 类名 : kim.hsl.component.MainActivity 类 ClassName.get((TypeElement) routeBean.getElement()), // $S 字符串 : "/app/MainActivity" routeBean.getRouteAddress(), // $S 字符串 : "app" routeBean.getRouteGroup()); } // 创建类 // 构造类名 Router_Group_main String groupClassName = "Router_Group_" + groupName; // 创建类 TypeSpec typeSpec = TypeSpec.classBuilder(groupClassName) .addSuperinterface(ClassName.get(iRouteGroup)) .addModifiers(PUBLIC) .addMethod(methodBuilder.build()) .build(); // 生成 Java 源码文件 JavaFile javaFile = JavaFile.builder("kim.hsl.router", typeSpec).build(); // 将 Java 源文件写出到相应目录中 try { mMessager.printMessage(Diagnostic.Kind.NOTE, "输出文件 : " + groupClassName); javaFile.writeTo(mFiler); } catch (IOException e) { e.printStackTrace(); mMessager.printMessage(Diagnostic.Kind.NOTE, "输出文件出现异常"); }finally { mMessager.printMessage(Diagnostic.Kind.NOTE, "输出文件完毕"); } // 统计路由表信息 mRootMap.put(groupName, groupClassName); } } private void generateRouteClass(Set routeElements) { // 获取 android.app.Activity 类型的注解节点 TypeElement activityElement = mElementUtils.getTypeElement("android.app.Activity"); // 获取 组件间共享服务 的接口, 该接口仅用于表示组件类型 TypeElement iServiceElement = mElementUtils.getTypeElement("kim.hsl.route_core.template.IService"); // 处理 @Route(path = "app/MainActivity") 节点 for (Element element : routeElements) { // 获取 Route 注解 Route route = element.getAnnotation(Route.class); // 路由表中的单个路由对象 RouteBean routeBean = null; // 判断 typeMirror 注解节点是否是 Activity 类型 if (mTypeUtils.isSubtype(element.asType(), activityElement.asType())) { // 该节点是 android.app.Activity 类型的 routeBean = new RouteBean( RouteBean.Type.ACTIVITY, // 路由对象类型 element, // 路由节点 null, // 类对象 route.path(), // 路由地址 route.group()); // 路由组 }else if (mTypeUtils.isSubtype(element.asType(), iServiceElement.asType())) { // 该节点是 kim.hsl.route_core.template.IService 类型的 routeBean = new RouteBean( RouteBean.Type.ISERVICE, // 路由对象类型 element, // 路由节点 null, // 类对象 route.path(), // 路由地址 route.group()); // 路由组 }else{ // 该节点不是 android.app.Activity 类型的 throw new RuntimeException("@Route 注解节点类型错误"); } // 检查路由地址 checkRouteAddress(routeBean); // 打印路由信息 mMessager.printMessage(Diagnostic.Kind.NOTE, "打印路由信息 : " + routeBean.toString()); // 处理路由信息分组 routeGroup(routeBean); } } /** * 处理路由信息分组 * @param routeBean */ private void routeGroup(RouteBean routeBean) { // 首先从 groupMap 集合中获取该分组的所有 路由信息 ArrayList routeBeans = mGroupMap.get(routeBean.getRouteGroup()); if (routeBeans == null){ // 如果从 mGroupMap 获取的该分组的路由信息集合为空 // 则创建新集合, 放置路由信息, 并加入到 mGroupMap 中 routeBeans = new ArrayList<>(); routeBeans.add(routeBean); mGroupMap.put(routeBean.getRouteGroup(), routeBeans); }else{ // 从 mGroupMap 获取的路由分组对应的路由信息集合不为空 // 直接添加 路由信息 即可 routeBeans.add(routeBean); } } /** * 验证路由地址 * @Route(path = "/app/MainActivity") * @param routeBean */ private void checkRouteAddress(RouteBean routeBean){ // 获取路由地址 String routeAddress = routeBean.getRouteAddress(); // 获取路由分组 String routeGroup = routeBean.getRouteGroup(); // 验证路由地址是否以 "/" 开头 if (!routeAddress.startsWith("/")) { throw new RuntimeException("路由地址 " + routeAddress + " 格式错误"); } // 如果路由地址的分组为空 , // 则截取第 0 和 第 1 个 "/" 之间的字符串作为分组名称 if (routeGroup == null || "".equals(routeGroup)){ String group = routeAddress.substring( routeAddress.indexOf("/", 0) + 1, routeAddress.indexOf("/", 1) ); if (group == null || "".equals(group)){ throw new RuntimeException("路由地址 " + routeAddress + " 获取分组错误"); } // 打印组名 mMessager.printMessage(Diagnostic.Kind.NOTE, "打印路由地址 " + routeAddress + " 的组名为 " + group); // 正式设置路由地址分组 routeBean.setRouteGroup(group); } } }

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

【Android 组件化】路由组件 ( 组件间共享的服务 )

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

298

299

300

301

302

303

304

305

306

307

308

309

310

311

312

313

314

315

316

317

318

319

320

321

322

323

324

325

326

327

328

329

330

331

332

333

334

335

336

337

338

339

340

341

342

343

344

345

2、app 模块中的注解类生成的 Java 源码

Module 模块中 , 使用注解生成的源码 , 都在对应模块的 " build\generated\ap_generated_sources\debug\out\ " 目录中 ;

app 中的注解类 :

@Route(path = "/app/MainActivity") public class MainActivity extends Activity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } }

1

2

3

4

5

6

7

8

生成的源码 : 生成源码路径 D:\002_Project\002_Android_Learn\Component\app\build\generated\ap_generated_sources\debug\out\kim\hsl\router\Router_Group_app.java ;

package kim.hsl.router; import java.lang.Override; import java.lang.String; import java.util.Map; import kim.hsl.component.MainActivity; import kim.hsl.route_core.template.IRouteGroup; import kim.hsl.router_annotation.model.RouteBean; public class Router_Group_app implements IRouteGroup { @Override public void loadInto(Map atlas) { atlas.put("app", new RouteBean(RouteBean.Type.ACTIVITY, MainActivity.class, "/app/MainActivity", "app")); } }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

3、library2 模块中的注解类生成的 Java 源码

Module 模块中 , 使用注解生成的源码 , 都在对应模块的 " build\generated\ap_generated_sources\debug\out\ " 目录中 ;

library2 中的注解类 :

package kim.hsl.library2; import android.util.Log; import kim.hsl.base.ComponentService; import kim.hsl.router_annotation.Route; @Route(path = "/library2/StringService") public class StringService implements ComponentService { @Override public void doSomething() { Log.i("StringService", "library2 组件中的 StringService 服务 "); } }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

生成的源码 : 生成源码路径 D:\002_Project\002_Android_Learn\Component\library2\build\generated\ap_generated_sources\debug\out\kim\hsl\router\Router_Group_library2.java ;

package kim.hsl.router; import java.lang.Override; import java.lang.String; import java.util.Map; import kim.hsl.library2.StringService; import kim.hsl.route_core.template.IRouteGroup; import kim.hsl.router_annotation.model.RouteBean; public class Router_Group_library2 implements IRouteGroup { @Override public void loadInto(Map atlas) { atlas.put("library2", new RouteBean(RouteBean.Type.ISERVICE, StringService.class, "/library2/StringService", "library2")); } }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

五、博客资源

博客源码 :

GitHub : https://github.com/han1202012/Component

CSDN 下载 :

Android NAT

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Unity 之 Profiler概述
下一篇:[跟着官方文档学TestNG][一][学习笔记]
相关文章