Android 组件化】路由组件 ( 页面跳转参数依赖注入 )

网友投稿 669 2022-05-30

文章目录

一、参数自动注入

二、自定义注解

三、使用 @Extra 自定义注解

四、注解处理器解析 @Extra 自定义注解 并生成相应 Activity 对应代码

五、博客资源

组件化系列博客 :

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

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

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

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

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

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

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

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

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

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

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

【Android 组件化】路由组件 ( 生成 Root 类记录模块中的路由表 )

【Android 组件化】路由组件 ( 运行时获取 注解处理器 生成的路由表 )

【Android 组件化】路由组件 ( 路由框架概述 )

【Android 组件化】路由组件 ( 页面跳转参数传递注解 )

一、参数自动注入

在 组件化 中 , 使用 路由组件 进行界面跳转时 , 涉及到参数的传递 , 传递过去的参数需要在目的地 Activity 的 onCreate 方法中 , 调用 getIntent().getXxxExtra() 获取到传递的值 ;

如果一次性传递 十几个 , 乃至几十个参数 , 这样就需要写很多次 getIntent().getXxxExtra() 样式的代码 , 这里引入注入框架 , 类似于 ButterKnife , 只要在目的 Activity 中的成员属性上标注注解 , 可以自动生成 getIntent().getXxxExtra() 相关逻辑 , 开发者不必手动编写此类逻辑 ;

ButterKnife 的作用是在 Activity 的成员属性上标注 @BindView 注解 , 自动生成 findViewById 代码 ;

二、自定义注解

自定义 Extra 注解 ,

@Target({ElementType.FIELD}) 元注解表示该注解用于标注成员字段 ,

@Retention(RetentionPolicy.CLASS) 元注解表示该注解保留到字节码编译时 ,

注解中定义了一个注解属性 name , 默认值为 “” ;

自定义注解代码示例 :

package kim.hsl.router_annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 参数自动注入注解 * 该注解使用在 成员字段 上面 */ @Target({ElementType.FIELD}) @Retention(RetentionPolicy.CLASS) public @interface Extra { /** * 参数名称 * @return */ String name() default ""; }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

三、使用 @Extra 自定义注解

在 Activity 中 , 使用 @Route 和 @Extra 自定义注解 ;

package kim.hsl.library3; import android.os.Bundle; import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.google.android.material.snackbar.Snackbar; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; import android.view.View; import kim.hsl.router_annotation.Extra; import kim.hsl.router_annotation.Route; @Route(path = "/library3/MainActivity") public class MainActivity extends AppCompatActivity { /** * 姓名 */ @Extra private String name; /** * 年龄 */ @Extra private int age; /** * 身高 */ @Extra private int height; /** * 体重 */ @Extra private int weight; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = findViewById(R.id.toolbar); } }

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

四、注解处理器解析 @Extra 自定义注解 并生成相应 Activity 对应代码

注解处理器中解析上述注解 , 生成如下代码 , 生成代码位置 " D:

注解处理器中解析上述注解 , 生成如下代码 , 生成代码位置 " D:\002_Project\002_Android_Learn\Component\library3\build\generated\ap_generated_sources\debug\out\kim\hsl\library3\MainActivity_Extra.java "

2_Project

注解处理器中解析上述注解 , 生成如下代码 , 生成代码位置 " D:\002_Project\002_Android_Learn\Component\library3\build\generated\ap_generated_sources\debug\out\kim\hsl\library3\MainActivity_Extra.java "

2_Android_Learn\Component\library3\build\generated\ap_generated_sources\debug\out\kim\hsl\library3\MainActivity_Extra.java "

package kim.hsl.library3; import java.lang.Object; import java.lang.Override; import kim.hsl.route_core.template.IExtra; public class MainActivity_Extra implements IExtra { @Override public void loadExtra(Object target) { MainActivity t = (MainActivity)target; t.name = t.getIntent().getStringExtra("name"); t.age = t.getIntent().getIntExtra("age", t.age); t.height = t.getIntent().getIntExtra("height", t.height); t.weight = t.getIntent().getIntExtra("weight", t.weight); } }

1

2

3

4

5

【Android 组件化】路由组件 ( 页面跳转参数依赖注入 )

6

7

8

9

10

11

12

13

14

15

16

生成上述代码的注解处理器 :

package kim.hsl.router_compiler; import com.google.auto.service.AutoService; import com.squareup.javapoet.ArrayTypeName; 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.TypeName; import com.squareup.javapoet.TypeSpec; import com.squareup.javapoet.WildcardTypeName; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; 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.TypeKind; 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.Extra; 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.Extra"}) // 支持的 Java 版本 @SupportedSourceVersion(SourceVersion.RELEASE_8) public class ExtraProcessor extends AbstractProcessor { /** * 注解处理器中使用 Messager 对象打印日志 */ private Messager mMessager; /** * 用于写出生成的 Java 代码 */ private Filer mFiler; /** * 注解节点工具 */ private Elements mElementUtils; /** * 类工具 */ private Types mTypeUtils; /** * 获取的 moduleName 参数 */ private String mModuleName; /** * 获取所有需要注入的节点集合 , 并按照其父节点 Activity 进行分组 * 键 ( Key ) : Activity 节点 * 值 ( Value ) : Activity 中被 @Extra 注解的属性节点 */ private Map> mActivity2Field = new HashMap<>(); /** * 该函数在初始化时调用 , 相当于构造函数 * @param processingEnvironment */ @Override public synchronized void init(ProcessingEnvironment processingEnvironment) { super.init(processingEnvironment); // 获取打印日志接口 this.mMessager = processingEnvironment.getMessager(); // 测试日志打印 mMessager.printMessage(Diagnostic.Kind.NOTE, "ExtraProcessor : 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, "ExtraProcessor : 打印 moduleName 参数 : " + mModuleName); } } /** * 该函数在注解处理器注册时自动执行, 是处理注解的核心函数 * * Set set 参数 : 该集合表示使用了相关注解的节点的集合 * * @param set * @param roundEnvironment * @return */ @Override public boolean process(Set set, RoundEnvironment roundEnvironment) { mMessager.printMessage(Diagnostic.Kind.NOTE, "ExtraProcessor : " + mModuleName + " process "); if (set == null || set.isEmpty()){ // 如果没有检测到注解 , 直接退出 mMessager.printMessage(Diagnostic.Kind.NOTE, "ExtraProcessor : 检测到注解为空 , 直接退出 mModuleName : " + mModuleName); return false; } // 获取被 @Extra 注解的属性节点集合 Set elements = roundEnvironment.getElementsAnnotatedWith(Extra.class); // 采集这些属性节点集合的 类型 和 变量名称 for (Element element : elements) { // 获取这些被 @Extra 标注的字段的父节点 Activity 节点 TypeElement activityElement = (TypeElement) element.getEnclosingElement(); if (mActivity2Field.containsKey(activityElement)) { // 如果该 Activity 父节点存在 , 直接添加到子节点集合中 mActivity2Field.get(activityElement).add(element); } else { // 如果该 Activity 父节点不存在 , 先创建子节点集合 , 再添加到集合中 List childs = new ArrayList<>(); childs.add(element); mActivity2Field.put(activityElement, childs); } mMessager.printMessage(Diagnostic.Kind.NOTE, "ExtraProcessor : " + mModuleName + " 添加注解类型 : " + element.getSimpleName()); } // 至此 , 已经将所有 Activity 以及其下使用 @Extra 标注的属性都存放在了 // Map> mActivity2Field 集合中 /* 生成 Java 代码 */ // 获取 Activity 类型 TypeMirror activityTypeMirror = mElementUtils.getTypeElement("android.app.Activity").asType(); // 获取 IExtra 接口类型节点 TypeElement IExtra = mElementUtils.getTypeElement("kim.hsl.route_core.template.IExtra"); // 生成 IExtra 接口中 void loadExtra(Object target); 方法的 Object target 参数 ParameterSpec objectParamSpec = ParameterSpec.builder(TypeName.OBJECT, "target").build(); // 遍历所有需要注入的 类:属性 for (Map.Entry> entry : mActivity2Field.entrySet()) { // 每个 Map 键值对元素都要生成一个对应的 Java 类 // 获取 Activity 类 TypeElement rawClassElement = entry.getKey(); // 如果该类不是 Activity 子类 , 直接抛出异常 if (!mTypeUtils.isSubtype(rawClassElement.asType(), activityTypeMirror)) { throw new RuntimeException("ExtraProcessor Activity 类型错误"); } // 创建 void loadExtra(Object target) 方法 MethodSpec.Builder builder = MethodSpec.methodBuilder("loadExtra") .addAnnotation(Override.class) .addModifiers(Modifier.PUBLIC) .addParameter(objectParamSpec); // 生成类型转换代码 : MainActivity t = (MainActivity)target; // 获取 Activity 类名称 ClassName className = ClassName.get(rawClassElement); // 类型转换, 将 Activity 类转为指定的 Activity 子类类型 builder.addStatement("$T t = ($T)target", className, className); mMessager.printMessage(Diagnostic.Kind.NOTE, "ExtraProcessor : 开始循环 Map 元素个数" + entry.getValue().size()); // 遍历被 @Extra 标注的属性字段 for (int i = 0; i < entry.getValue().size(); i++) { Element element = entry.getValue().get(i); buildStatement(element, builder); } mMessager.printMessage(Diagnostic.Kind.NOTE, "ExtraProcessor : 结束循环 Map 元素个数" + entry.getValue().size()); // 生成 java 类名, 原来的 Activity 类名基础上添加 "_Extra" 后缀 String extraClassName = rawClassElement.getSimpleName() + "_Extra"; // 创建 Java 类 TypeSpec typeSpec = TypeSpec.classBuilder(extraClassName) .addSuperinterface(ClassName.get(IExtra)) // 实现 IExtra 接口 .addModifiers(PUBLIC) // .addMethod(builder.build()) // 设置函数 .build(); // 正式创建 // Java 文件 JavaFile javaFile = JavaFile.builder(className.packageName(), typeSpec).build(); // 写出生成的 java 代码 try { javaFile.writeTo(mFiler); } catch (IOException e) { e.printStackTrace(); } mMessager.printMessage(Diagnostic.Kind.NOTE, "ExtraProcessor : 生成文件结束 : " + mModuleName + " " +javaFile.toString()); } return true; } /** * 拼装如下代码 * t.a = t.getIntent().getStringExtra("a"); * @param element */ public void buildStatement(Element element, MethodSpec.Builder builder) { TypeMirror typeMirror = element.asType(); int type = typeMirror.getKind().ordinal(); //属性名 String text 获得text String fieldName = element.getSimpleName().toString(); //获得注解 name值 , 默认是传入的 name 注解属性值 String extraName = element.getAnnotation(Extra.class).name(); mMessager.printMessage(Diagnostic.Kind.NOTE, "ExtraProcessor : " + mModuleName + " 处理注解类型 : " + typeMirror.toString() + " , 字段名称 : " + fieldName + " , 注解属性值 : " + extraName); if (extraName == null || extraName.length() == 0) { // 如果 name 注解属性值为空 , 则取值 字段名称 extraName = fieldName; } mMessager.printMessage(Diagnostic.Kind.NOTE, "ExtraProcessor : extraName : " + extraName); String defaultValue = "t." + fieldName; String statement = defaultValue + " = t.getIntent()."; if (type == TypeKind.BOOLEAN.ordinal()) { statement += "getBooleanExtra($S, " + defaultValue + ")"; } else if (type == TypeKind.BYTE.ordinal()) { statement += "getByteExtra($S, " + defaultValue + ")"; } else if (type == TypeKind.SHORT.ordinal()) { statement += "getShortExtra($S, " + defaultValue + ")"; } else if (type == TypeKind.INT.ordinal()) { statement += "getIntExtra($S, " + defaultValue + ")"; } else if (type == TypeKind.LONG.ordinal()) { statement += "getLongExtra($S, " + defaultValue + ")"; } else if (type == TypeKind.CHAR.ordinal()) { statement += "getCharExtra($S, " + defaultValue + ")"; } else if (type == TypeKind.FLOAT.ordinal()) { statement += "getFloatExtra($S, " + defaultValue + ")"; } else if (type == TypeKind.DOUBLE.ordinal()) { statement += "getDoubleExtra($S, " + defaultValue + ")"; } else{ //数组类型 if (type == TypeKind.ARRAY.ordinal()) { addArrayStatement(statement, fieldName, extraName, typeMirror, element, builder); } else { // 对象类型 addObjectStatement(statement, fieldName, extraName, typeMirror, element, builder); } return; } builder.addStatement(statement, extraName); mMessager.printMessage(Diagnostic.Kind.NOTE, "ExtraProcessor : extraName : " + extraName + " 生成完毕"); } private void addArrayStatement(String statement, String fieldName, String extraName, TypeMirror typeMirror, Element elementm , MethodSpec.Builder builder) { // 获取 Parcelable 类型 TypeMirror parcelableType = mElementUtils.getTypeElement("android.os.Parcelable").asType(); // 处理数组 switch (typeMirror.toString()) { case "boolean[]": statement += "getBooleanArrayExtra($S)"; break; case "int[]": statement += "getIntArrayExtra($S)"; break; case "short[]": statement += "getShortArrayExtra($S)"; break; case "float[]": statement += "getFloatArrayExtra($S)"; break; case "double[]": statement += "getDoubleArrayExtra($S)"; break; case "byte[]": statement += "getByteArrayExtra($S)"; break; case "char[]": statement += "getCharArrayExtra($S)"; break; case "long[]": statement += "getLongArrayExtra($S)"; break; case "java.lang.String[]": statement += "getStringArrayExtra($S)"; break; default: // 处理 Parcelable 数组 String defaultValue = "t." + fieldName; // object数组 componentType 获得 object类型 ArrayTypeName arrayTypeName = (ArrayTypeName) ClassName.get(typeMirror); TypeElement typeElement = mElementUtils.getTypeElement(arrayTypeName .componentType.toString()); // 如果不是 Parcelable 抛异常退出 if (!mTypeUtils.isSubtype(typeElement.asType(), parcelableType)) { throw new RuntimeException("不支持的 Extra 类型 : " + typeMirror); } // 字符串格式 statement = "$T[] " + fieldName + " = t.getIntent()" + ".getParcelableArrayExtra" + "($S)"; builder.addStatement(statement, parcelableType, extraName); builder.beginControlFlow("if( null != $L)", fieldName); statement = defaultValue + " = new $T[" + fieldName + ".length]"; builder.addStatement(statement, arrayTypeName.componentType) .beginControlFlow("for (int i = 0; i < " + fieldName + "" + ".length; " + "i++)") .addStatement(defaultValue + "[i] = ($T)" + fieldName + "[i]", arrayTypeName.componentType) .endControlFlow(); builder.endControlFlow(); return; } builder.addStatement(statement, extraName); } private void addObjectStatement(String statement, String fieldName, String extraName, TypeMirror typeMirror, Element element, MethodSpec.Builder builder) { // 获取 Parcelable 类型 TypeMirror parcelableType = mElementUtils.getTypeElement("android.os.Parcelable").asType(); // 获取 IService 类型 TypeMirror iServiceType = mElementUtils.getTypeElement("kim.hsl.route_core.template.IService").asType(); if (mTypeUtils.isSubtype(typeMirror, parcelableType)) { statement += "getParcelableExtra($S)"; } else if (typeMirror.toString().equals("java.lang.String")) { statement += "getStringExtra($S)"; } else if (mTypeUtils.isSubtype(typeMirror, iServiceType)) { ClassName routerClassName = ClassName.get("kim.hsl.route_core", "Router"); statement = "t." + fieldName + " = ($T) $T.getInstance().build($S).navigation()"; builder.addStatement(statement, TypeName.get(element.asType()), routerClassName, extraName); return; } else { // List TypeName typeName = ClassName.get(typeMirror); //泛型 if (typeName instanceof ParameterizedTypeName) { //list 或 arraylist ClassName rawType = ((ParameterizedTypeName) typeName).rawType; //泛型类型 List typeArguments = ((ParameterizedTypeName) typeName) .typeArguments; if (!rawType.toString().equals("java.util.ArrayList") && !rawType.toString() .equals("java.util.List")) { throw new RuntimeException("Not Support Inject Type:" + typeMirror + " " + element); } if (typeArguments.isEmpty() || typeArguments.size() != 1) { throw new RuntimeException("List Must Specify Generic Type:" + typeArguments); } TypeName typeArgumentName = typeArguments.get(0); TypeElement typeElement = mElementUtils.getTypeElement(typeArgumentName .toString()); // Parcelable 类型 if (mTypeUtils.isSubtype(typeElement.asType(), parcelableType)) { statement += "getParcelableArrayListExtra($S)"; } else if (typeElement.asType().toString().equals("java.lang.String")) { statement += "getStringArrayListExtra($S)"; } else if (typeElement.asType().toString().equals("java.lang.Integer")) { statement += "getIntegerArrayListExtra($S)"; } else { throw new RuntimeException("Not Support Generic Type : " + typeMirror + " " + element); } } else { throw new RuntimeException("Not Support Extra Type : " + typeMirror + " " + element); } } builder.addStatement(statement, extraName); } }

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

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

346

347

348

349

350

351

352

353

354

355

356

357

358

359

360

361

362

363

364

365

366

367

368

369

370

371

372

373

374

375

376

377

378

379

380

381

382

383

384

385

386

387

388

389

390

391

392

393

394

395

396

397

398

399

400

401

402

403

404

405

406

407

408

409

410

411

412

413

414

415

五、博客资源

博客源码 :

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

CSDN 下载 :

Android NAT

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

上一篇:关于 Kubernetes中kube-controller-managerr的一些笔记
下一篇:多线程与同步代码块详解
相关文章