美文网首页
使用编译期注解对Activity和Fragment动态注入参数

使用编译期注解对Activity和Fragment动态注入参数

作者: 有没有口罩给我一个 | 来源:发表于2020-02-08 18:09 被阅读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;

    相关文章

      网友评论

          本文标题:使用编译期注解对Activity和Fragment动态注入参数

          本文链接:https://www.haomeiwen.com/subject/dcgixhtx.html