使用编译期注解对Activity和Fragment动态注入参数
2020-02-08 本文已影响0人
有没有口罩给我一个
在我们从上一个页面跳转到下一个页面需要携带数据给下一个页面,我们是这样获取生个页面的数据:
intent.putExtra("id",1);
getIntent().getStringExtra("id")
上面代码我相信大家也是会犯的错误,参数类型安全,因为这些参数的类型需要人工维护,容易造成参数类型安全频频出现。所以就有了这篇文章,自动注入参数现在有很多框架也有,像ARouter和ButterKife也有使用编译器生成代码自动注入参数,只有用法不一样。
按照我们以往的写法:
//必须和Activity同包
public class XXXActivity$$Parameter implements ParameterInject {
@Override
public void inject(Object target) {
MainActivity mainActivity = (MainActivity) target;
mainActivity.getIntent().getSerializableExtra("");
ArrayList<Parcelable> parcelableArrayListExtra = mainActivity.getIntent().getParcelableArrayListExtra("");
mainActivity.age = mainActivity.getIntent().getBundleExtra("").getInt("");
}
}
所以我们需要按照上面的代码,通过注解处理器生成我们想要的代码,然后通过inject方法注入参数。
public interface ParameterInject {
void inject(Object target);
}
ParameterInject 类是我们通过注解生成类必须实现的接口,因为我们后面要通过反射创建生成类的对象,而且生成的类名是有一定的规则,方便我们快速的找到该类,需要注意的是,生成的类必须和需要注入参数的类是同一个包下的,避免方法属性出现问题。
@AutoService(Processor.class)
@SupportedAnnotationTypes({Constants.PARAMETER_ANNOTATION_TYPES})
@SupportedSourceVersion(SourceVersion.RELEASE_7)
public class ParameterProcessor extends AbstractProcessor {
//临时map存储,用来存放@Parameter注解的属性集合,生成类文件时遍历
//key : 类节点 value:被@Parameter注解的属性集合
private Map<TypeElement, List<Element>> tempPrameterMap = new ConcurrentHashMap<>();
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
elementUtils = processingEnvironment.getElementUtils();
typeUtils = processingEnvironment.getTypeUtils();
messager = processingEnvironment.getMessager();
filer = processingEnvironment.getFiler();
activityTypeMirror = elementUtils.getTypeElement(Constants.ACTIVITY).asType();
appFragmentTypeMirror = elementUtils.getTypeElement(Constants.APP_FRAGMENT).asType();
androidXFragmentTypeMirror = elementUtils.getTypeElement(Constants.ANDROIDX_FRAGMENT).asType();
parcelableTypeMirror = elementUtils.getTypeElement(Constants.PARCELABLE).asType();
serializableTypeMirror = elementUtils.getTypeElement(Constants.SERIALIZABLE).asType();
}
/**
* 相当于main函数,开始处理注解
* 注解处理器的核心方法,处理具体的注解,生成Java文件
*
* @param set 使用了支持处理注解的节点集合(类 上面写了注解)
* @param roundEnvironment 当前或是之前的运行环境,可以通过该对象查找找到的注解。
* @return true 表示后续处理器不会再处理(已经处理完成)
*/
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
if (set.isEmpty()) return true;
Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(Parameter.class);
if (!EmptyUtils.isEmpty(elements)) {
valueOfParameter(elements);
createParameterFile();
}
return true;
}
private void createParameterFile() {
if (tempPrameterMap.isEmpty()) return;
TypeElement typeElement = elementUtils.getTypeElement(Constants.PARAMETER_LOAD);
ParameterSpec parameterSpec = ParameterSpec.builder(TypeName.OBJECT, Constants.PARAMETER_NAMR).build();
for (Map.Entry<TypeElement, List<Element>> entry : tempPrameterMap.entrySet()) {
//activity,Fragment or other
TypeElement otherTypeElement = entry.getKey();
ClassName otherClassName = ClassName.get(otherTypeElement);
TypeMirror otherType = otherTypeElement.asType();
MethodSpec.Builder builder = MethodSpec.methodBuilder(Constants.PARAMETER_METHOD_NAME)
.addParameter(parameterSpec)
.addModifiers(Modifier.PUBLIC)
.returns(TypeName.VOID)
//MainActivity t = (MainActivity) target;
.addStatement("$T t = ($T)$N", otherClassName,
otherClassName, Constants.PARAMETER_NAMR);
//该类下的所有被@Parameter注解的属性
List<Element> elements = entry.getValue();
for (Element element : elements) {
//被@Parameter注解属性信息
TypeMirror typeMirror = element.asType();
int type = typeMirror.getKind().ordinal();
// 被@Parameter注解的属性名
String filedName = element.getSimpleName().toString();
// @Parameter注解获取属性名
String annotationValue = element.getAnnotation(Parameter.class).name();
annotationValue = EmptyUtils.isEmpty(annotationValue) ? filedName : annotationValue;
//如:t.age = target.getIntent().getStringExtra("age", 1);
String finalValue = "t" + "." + annotationValue;
StringBuilder buffer = new StringBuilder();
if (typeUtils.isSubtype(otherType, activityTypeMirror)) {//activity
if (type == TypeKind.INT.ordinal()) {
buffer.append(finalValue).append(" = t.getIntent().");
buffer.append("getIntExtra($S,").append(finalValue).append(")");
} else if (type == TypeKind.BOOLEAN.ordinal()) {
buffer.append(finalValue).append(" = t.getIntent().");
buffer.append("getBooleanExtra($S,").append(finalValue).append(")");
} else if (typeMirror.toString().equalsIgnoreCase(Constants.STRING)) {
buffer.append(finalValue).append(" = t.getIntent().");
buffer.append("getStringExtra($S)");
} else if (type == TypeKind.DOUBLE.ordinal()) {
buffer.append(finalValue).append(" = t.getIntent().");
buffer.append("getDoubleExtra($S,").append(finalValue).append(")");
} else if (type == TypeKind.FLOAT.ordinal()) {
buffer.append(finalValue).append(" = t.getIntent().");
buffer.append("getFloatExtra($S,").append(finalValue).append(")");
} else if (type == TypeKind.LONG.ordinal()) {
buffer.append(finalValue).append(" = t.getIntent().");
buffer.append("getLongExtra($S,").append(finalValue).append(")");
} else if (typeUtils.isSubtype(element.asType(), parcelableTypeMirror)) {//Parcelable的子类
buffer.append(finalValue).append(" = t.getIntent().");
buffer.append("getParcelableExtra($S)");
} else if (typeUtils.isSubtype(element.asType(), serializableTypeMirror)) {//Serializable的实现类
messager.printMessage(Diagnostic.Kind.NOTE, element.asType().toString());
buffer.append(finalValue).append(" = ");//t.finalValue
//int[]、 String[]、Bundle、ArrayList和Map实现了Serializable,我们只需要强转即可
buffer.append("(").append(element.asType().toString()).append(")");
buffer.append("t.getIntent().");
buffer.append("getSerializableExtra($S)");
}
builder.addStatement(buffer.toString(), annotationValue);
} else if (typeUtils.isSubtype(otherType, appFragmentTypeMirror) ||
typeUtils.isSubtype(otherType, androidXFragmentTypeMirror)) {//fragment
buffer.append(finalValue).append(" = t.getArguments().");
if (type == TypeKind.INT.ordinal()) {
buffer.append("getInt($S,").append(finalValue).append(")");
} else if (type == TypeKind.BOOLEAN.ordinal()) {
buffer.append("getBoolean($S,").append(finalValue).append(")");
} else if (typeMirror.toString().equalsIgnoreCase(Constants.STRING)) {
buffer.append("getString($S,").append(finalValue).append(")");
}
builder.addStatement(buffer.toString(), annotationValue);
}
}
String finalClassName = otherClassName.simpleName() + Constants.PARAMETER_FILE_NAME;
messager.printMessage(Diagnostic.Kind.NOTE, otherClassName.packageName() + "生成parameter注解的类名 >>> " + finalClassName);
try {
JavaFile.builder(
otherClassName.packageName(),
TypeSpec.classBuilder(finalClassName)
.addModifiers(Modifier.PUBLIC)
.addSuperinterface(ClassName.get(typeElement))
.addMethod(builder.build()).build()
).build().writeTo(filer);
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void valueOfParameter(Set<? extends Element> elements) {
for (Element element : elements) {
TypeElement typeElement = (TypeElement) element.getEnclosingElement();
if (tempPrameterMap.containsKey(typeElement)) {
tempPrameterMap.get(typeElement).add(element);
} else {
CopyOnWriteArrayList<Element> files = new CopyOnWriteArrayList<>();
files.add(element);
tempPrameterMap.put(typeElement, files);
}
}
}
}
注解处理代码也不多,需要注意的是在生成类型时int[]、 String[]、Bundle、ArrayList和Map实现了Serializable,我们只需要强转即可。
如何使用?
public class Order_MainActivity extends AppCompatActivity {
@Parameter
int age = 1;
@Parameter
String name = "";
@Parameter
User user;
@Parameter(name = "users")
ArrayList<User> users;
@Parameter
Order order;
@Parameter
ArrayList<Order> orders;
@Parameter
Bundle bundle;
@Parameter
String[] args;
@Parameter
int[] ints;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_order__main);
ParameterManager.getInstance().inject(this);
Log.e("tag", "age >>> " + age + " users>>>" +
users + " >>> " + user + " >>> " + order + " >>> " + orders + " >>> " + bundle.getInt("age"));
}
}
最后生成的类:
public class Order_MainActivity$$Parameter implements ParameterInject {
public void inject(Object target) {
Order_MainActivity t = (Order_MainActivity) target;
t.age = t.getIntent().getIntExtra("age", t.age);
t.name = t.getIntent().getStringExtra("name");
t.user = t.getIntent().getParcelableExtra("user");
t.users = (java.util.ArrayList<com.wfy.common.User>) t.getIntent().getSerializableExtra("users");
t.order = (com.wfy.common.Order) t.getIntent().getSerializableExtra("order");
t.orders = (java.util.ArrayList<com.wfy.common.Order>) t.getIntent().getSerializableExtra("orders");
t.bundle = t.getIntent().getParcelableExtra("bundle");
t.args = (java.lang.String[]) t.getIntent().getSerializableExtra("args");
t.ints = (int[]) t.getIntent().getSerializableExtra("ints");
}
}
总结
- int[]、 String[]、Bundle、ArrayList和Map实现了Serializable,在接收的时候也要用具体实现类;
- Dagger2、 ButterKinfe和ARouter这些框架们也是使用注解完成动态参数的注入;
- 注解处理不仅仅只是这一种方式去生成代码,还有ElementVisitor、TypeVisitor和AnnotationValueVisitor都可以操作,所以想要代码更好看就使用这几个方式去访问Java的元素,如:Element -> Type;