文章目錄
- 一、參數自動注入
- 二、自定義注解
- 三、使用 @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 "";
}
三、使用 @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);
}
}
四、注解處理器解析 @Extra 自定義注解 并生成相應 Activity 對應代碼
注解處理器中解析上述注解 , 生成如下代碼 , 生成代碼位置 " D:\002_Project\002_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);
}
}
生成上述代碼的注解處理器 :
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<TypeElement, List<Element>> 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<String, String> 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<Element> childs = new ArrayList<>();
childs.add(element);
mActivity2Field.put(activityElement, childs);
}
mMessager.printMessage(Diagnostic.Kind.NOTE, "ExtraProcessor : " + mModuleName + " 添加注解類型 : " + element.getSimpleName());
}
// 至此 , 已經将所有 Activity 以及其下使用 @Extra 标注的屬性都存放在了
// Map<TypeElement, List<Element>> 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<TypeElement, List<Element>> 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<TypeName> 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);
}
}
五、部落格資源
部落格源碼 :
- GitHub : https://github.com/han1202012/Component