文章目錄
一、參數自動注入
二、自定義注解
三、使用 @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
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 extends TypeElement> set 參數 : 該集合表示使用了相關注解的節點的集合 * * @param set * @param roundEnvironment * @return */ @Override public boolean process(Set extends TypeElement> 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 extends Element> 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小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。