美文网首页
IOC(二)-注解注入

IOC(二)-注解注入

作者: 烧伤的火柴 | 来源:发表于2020-04-09 11:32 被阅读0次

介绍

上一节中介绍了IOC的概念,以及使用反射注入对象,但是反射的效率比较低效,所以butterKnife换成了注解处理器的方式,在编译期间生成一些需要的代码来完成注入任务。
这一节也使用注入的方式实现一下对象的注入。

实现-分析

1.为了通用性 我们需要编写两个module, 一个是annotation的module中定义所有的注解,一个是annotation-compiler处理所有注解。他们的依赖关系是app依赖annotation和annotation-compiler, annotation-compile依赖annotation。
2.定义一个接口IBinder,接口里边的方法是接收Activity
3.annotation-compile处理器中实现IBinder的方法,调用Activity的findViewById方法给注解的属性赋值。

详细步骤

1.创建module和添加依赖关系
annotation-compile和annotation都是java的api,所以都定义为Java Library,
三者的依赖关系是
annotation-compile依赖annotation,annotation-compile下的build.gradle添加:

implementation project(path: ':annotation')

app依赖annotation,注解处理器引用annotation-compile
app的build.gradle添加:

implementation project(':annotation')
annotationProcessor project(':annotation-compiler'

2.annotation中创建注解类BindView:

@Retention(RetentionPolicy.SOURCE)//源码阶段
@Target(ElementType.FIELD)//目标是属性字段
public @interface BindView {
    int value();
}

3.在app module中创建IBinder接口

public interface IBinder<T> {

    void bind(T t);
}

4.在annotation-compile中定义注解处理器类AnnotationProcessor并且处理注解

@AutoService(Processor.class)
public class AnnotationProcessor extends AbstractProcessor {

    private Filer filer;
    private Messager messager;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        filer = processingEnv.getFiler();
        messager = processingEnv.getMessager();
    }

    /**
     * 支持的注解类型
     * @return 能够处理的注解类型列表
     */
    @Override
    public Set<String> getSupportedAnnotationTypes() {
        return new TreeSet<String>(){{
            add(BindView.class.getCanonicalName());
        }};
    }

    /**
     * 支持的jdk版本
     * @return 最新版本
     */
    @Override
    public SourceVersion getSupportedSourceVersion() {
        return super.getSupportedSourceVersion();
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        //Element元素集合;包含程序中所有元素 比如 包 类 方法 成员变量等;这里只有变量。
        Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(BindView.class);//app中使用该注解修饰所有元素
        //这里得到的是app中所有的元素,需要进行分类整理,然后创建类。
        Map<String, List<Element>> groupElements = groupElement(elements);

        for (String className : groupElements.keySet()) {
            writeFile(className, groupElements.get(className));
        }
        return true;
    }

    /**
     * 将所有元素根据类进行分类整理
     * @param elements  元素集合
     * @return 整理后的元素集合
     */
    private Map<String, List<Element>> groupElement(Set<? extends Element> elements){
        Map<String, List<Element>> map = new HashMap<>();

        for (Element element : elements) {
            String className = element.getEnclosingElement().getSimpleName().toString();
            List<Element> elementList = map.get(className);
            if (elementList == null) {
                elementList = new ArrayList<>();
                map.put(className, elementList);
            }

            elementList.add(element);
        }

        return map;
    }

    private void writeFile(String className, List<Element> elements){
        //生成文件
        Writer writer = null;
        try {
            PackageElement packageElement = processingEnv.getElementUtils().getPackageOf(elements.get(0).getEnclosingElement());
            String packageNameString = packageElement.getQualifiedName().toString();
            messager.printMessage(Diagnostic.Kind.NOTE, "getPackageOf得到的包是:"+packageNameString);
            JavaFileObject classFile = filer.createSourceFile(className + "_ViewBinding", packageElement);
            writer = classFile.openWriter();
            //        package com.example.dn_butterknife;
            writer.write("package "+packageNameString+";\n");
            //        import com.example.dn_butterknife.IBinder;
            writer.write("import "+packageNameString+".IBinder;\n");
            //        public class MainActivity_ViewBinding implements IBinder<com.example.dn_butterknife.MainActivity>{
            writer.write("public class "+className+"_ViewBinding implements IBinder<"+packageNameString+"."+className+"> {\n");
            //            @Override
            writer.write("@Override\n");
            //            public void bind(com.example.dn_butterknife.MainActivity target) {
            writer.write("public void bind("+packageNameString+"."+className+" target) {\n");
            // target.tvText=(android.widget.TextView)target.findViewById(2131165325);
            for (Element fieldElement : elements) {
                //属性名字
                String fieldName = fieldElement.getSimpleName().toString();
                writer.write("target."+fieldName+"=");
                //控件的类型
                String typeName = fieldElement.getKind().name();//这里得到的是这个元素的类型 比如:FIELD METHOD TYPE等等
                messager.printMessage(Diagnostic.Kind.NOTE, typeName);
                TypeMirror typeMirror = fieldElement.asType();
                writer.write("("+typeMirror+")");
                //资源id
                int viewId = fieldElement.getAnnotation(BindView.class).value();
                writer.write("target.findViewById("+viewId+");\n");
            }
            writer.write("}\n}");
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (writer != null) {
                try {
                    writer.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

使用@AutoService 注册该类是注解处理器,要使用@AutoService 需要在build.gradle中添加依赖:

annotationProcessor 'com.google.auto.service:auto-service:1.0-rc6'
implementation'com.google.auto.service:auto-service:1.0-rc6'

具体实现代码中都有注释。

5.生命一个方法利用多态性调用编译器生成的实现类

public class MelonButterknife {
    public static <T> void bind(T appCompatActivity) {
        String name = appCompatActivity.getClass().getName()+"_ViewBinding";
        try {
            Class<?> clazz = Class.forName(name);
            IBinder binder = (IBinder) clazz.newInstance();
            binder.bind(appCompatActivity);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

6.在Activity中使用注解和调用绑定

public class MainActivity extends AppCompatActivity {
    @BindView(R.id.text1)
    TextView text1;
    @BindView(R.id.text2)
    TextView text2;
    @BindView(R.id.text3)
    TextView text3;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        MelonButterknife.bind(this);

        text2.setText("早上好");
        text3.setText("晚上好");

        text1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startActivity(new Intent(MainActivity.this, SecondActivity.class));
            }
        });
    }
}

SecondActivity代码是:

public class SecondActivity extends AppCompatActivity {
    @BindView(R.id.text3)
    TextView textView3;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        MelonButterknife.bind(this);
        textView3.setText("today");
    }
}

下一步编译运行,会在如下目录看到生成的文件:


目录结构.jpg

总结

这种方式是通过注解处理器生成一个实现绑定方法的类,这种硬编码生成辅助类是在编译期完成的,所以比运行期反射要高效。

相关文章

网友评论

      本文标题:IOC(二)-注解注入

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