APT和运行时IOC

1:APT(Annotation Processing Tool)是属于编译时IOC的一种工具。它是Java编译器提供的一个工具,用于处理在编译时使用的注解。APT通过注解处理器(Annotation Processor)在编译阶段扫描和处理源代码中的注解,并生成额外的代码或进行其他的处理操作。

APT的工作原理如下:

  1. Java编译器在编译源代码时,会扫描并加载使用了特定注解的类。
  2. 当编译器遇到使用了特定注解的类时,会调用对应的注解处理器进行处理。
  3. 注解处理器通过API访问被注解的元素,如类、方法、字段等,并根据注解的定义执行相应的处理逻辑。
  4. 注解处理器可以生成额外的源代码文件或进行其他的操作,这些生成的代码将在后续的编译过程中被编译器处理。

 

2:APT注解实例,自定义ButterKnife功能:

1)定义3个模块,app、annotations、annotationsCompiler,app依赖其他两个,annotationsCompiler和annotations是两个java库。

 

2)Annotations里面定义一个注解:

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.SOURCE)
public @interface BindView {
    int value();
}

3)annotationsCompiler注册一个APT功能:

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

implementation project(path: ':iocAnnotations')

4)AnnotationsCompiler定义一个注解处理器:

 

package com.example.iocannotationcompiler;

import com.example.iocannotations.BindView;
import com.google.auto.service.AutoService;

import java.io.FileOutputStream;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeMirror;
import javax.tools.JavaFileObject;

import java.util.logging.Logger;


/**
 * 注解处理器,用来生成代码
 * 使用前需要先注册
 */
@AutoService(Processor.class)
public class AnnotationsCompiler extends AbstractProcessor {

    //使用前初始化3个动作
    //1:支持的Java版本

    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }

    //2:支持的注解类型

    Filer filer;

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

    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> types = new HashSet<>();
        types.add(BindView.class.getCanonicalName());
        return types;
    }


    //3:用来生成文件的对象


    /**
     * 对注解进行处理
     */
    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {

        //类:TypeElement
        //方法:ExecutableElement
        //属性:VariableElement
        Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(BindView.class);
        System.out.println("elements--->" + elements.toString());
        //elements--->[button]

        //开始分类存放
        Map<String, List<VariableElement>> map = new HashMap<>();
        List<VariableElement> list1 = new ArrayList<>();
        for (Element element : elements) {
            VariableElement variableElement = (VariableElement) element;
            //得到包裹的Activity的name
            String activityName = variableElement.getEnclosingElement().getSimpleName().toString();
            list1 = map.get(activityName);
            if (list1 == null) {
                list1 = new ArrayList<>();
                map.put(activityName, list1);
            }
            list1.add(variableElement);
        }

        System.out.println("list1--->" + list1.toString());
        //list1--->[button]

        System.out.println("map--->" + map.toString());
        //map--->{FirstFragment=[button]}


        if (map.size() > 0) {
            Writer writer = null;
            Iterator<String> iterator = map.keySet().iterator();
            while (iterator.hasNext()) {
                String activityName = iterator.next();
                System.out.println("activityName--->" + activityName);
                //activityName--->FirstFragment

                List<VariableElement> list = map.get(activityName);
                //得到包名
                TypeElement enclosingElement = (TypeElement) list.get(0).getEnclosingElement();
                System.out.println("enclosingElement--->" + enclosingElement.toString());
                //enclosingElement--->com.example.ioc.FirstFragment

                String packageName = processingEnv.getElementUtils().getPackageOf(enclosingElement).getQualifiedName().toString();
                System.out.println("packageName--->" + packageName);
                //packageName--->com.example.ioc

                //写入文件
                try {
                    JavaFileObject sourceFile = filer.createSourceFile(packageName + "." + activityName + "_ViewBinding");
                    writer = sourceFile.openWriter();
                    writer.write("package " + packageName + ";\n\n");
                    writer.write("import  " + packageName + ".IBinder;\n");
                    writer.write("public class " + activityName + "_ViewBinding implements IBinder<" + packageName + "." + activityName + "> {\n");
                    writer.write("@Override\n");
                    writer.write("public void bind(" + packageName + "." + activityName + " target) {\n");
                    for (VariableElement variableElement : list) {
                        BindView bindView = variableElement.getAnnotation(BindView.class);
                        int id = bindView.value();
                        String name = variableElement.getSimpleName().toString();

                        //得到类型
                        TypeMirror typeMirror = variableElement.asType();
                        writer.write("  target." + name + "= (" + typeMirror + ")target.getView().findViewById(" + id + ");\n");
                    }
                    writer.write("}\n");
                    writer.write("}\n");
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    if (writer != null) {
                        try {
                            writer.close();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }

        }
        return false;
    }
}

 

 

5)APP里面改成:

annotationProcessor project(path: ':iocAnnotationCompiler')

implementation project(path: ':iocAnnotations')

 

6)用法:在App的FirstFragment里面使用:

 @BindView(R.id.button_first)
    public Button button;

7)运行代码,在app的generated里面自动生成代码:FirstFragment_ViewBinding

package com.example.ioc;

import  com.example.ioc.IBinder;
public class FirstFragment_ViewBinding implements IBinder<com.example.ioc.FirstFragment> {
@Override
public void bind(com.example.ioc.FirstFragment target) {
  target.button= (android.widget.Button)target.getView().findViewById(2131230825);
}
}

IBinder:

/**
 * 给用户绑定Activity使用
 */
public interface IBinder<T> {
    void bind(T target);
}

MyBUtterKnife:

package com.example.ioc;

import android.app.Activity;

import androidx.fragment.app.Fragment;

/**
 * @author yanjim
 * @Date 2023/5/8
 */
public class MyButterKnife {
    public static void bind(Fragment fragment){
        System.out.println("ccc" + fragment.getClass().getName());
        String className = fragment.getClass().getName() + "_ViewBinding";
        try {
            Class<?> aClass = Class.forName(className);
            IBinder iBinder = (IBinder) aClass.newInstance();
            iBinder.bind(fragment);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

FirstFragment:

MyButterKnife.bind(this);

 

 

 

 

运行时IOC:反射+注解:学习视频:https://www.ixigua.com/6832986741805154824?logTag=76046354488d956fe32b

MainActivity:通过反射+注解,替代setContentView、findViewById和setOnclickListener

@InjectLayout(R.layout.activity_main)
public class MainActivity extends AppCompatActivity {

    @InjectView(R.id.button)
    private Button button;

    @InjectView(R.id.button2)
    private Button button2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
       /* setContentView(R.layout.activity_main);
        button = findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                button.setText(" bbbbbbbbbbbbbbbbbbbb");
            }
        });*/

        InjectManager.inject(this);
    }


    /**
     * 有可能多个按钮对应同一个点击时间
     */
    @OnClick({R.id.button})
    public void onClick(View view) {
        button.setText(" cccccccccccccccc");
    }

    @OnClick({ R.id.button2})
    public void onClick2(View view) {
        button2.setText(" dddddddddddddddd");
    }

    public boolean longClick(View view){
        button.setText(" eeeeeeeeeeeeeeee");
        return false;
    }
}

 

1:设置3种注解

1)InjectLayout:@InjectLayout(R.layout.activity_main)

用于解析资源文件

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface InjectLayout {
    int value();
}

2)InjectView:@InjectView(R.id.button) private Button button;

用于解析属性button

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface InjectView {
    int value();
}

3)OnClick:用于解析方法

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@InjectEvent(listenerSetter = "setOnClickListener",
        listenerType = View.OnClickListener.class,
        callbackMethod = "onClick")
public @interface OnClick {
    int[] value();
}

onLongClick:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@InjectEvent(listenerSetter = "setOnLongClickListener",
        listenerType = View.OnLongClickListener.class,
        callbackMethod = "onLongClick")
public @interface onLongClick {
    int[] value();
}

4)注解的注解:

@Target(ElementType.ANNOTATION_TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface InjectEvent {
     /*button.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            button.setText(" bbbbbbbbbbbbbbbbbbbb");
        }
    });*/
    // 事件三要素
    // 1. 事件源:setOnclikListener
    String listenerSetter();
    // 2. 事件源的类型:View.OnClickListener
    Class listenerType();
    // 3. 事件被触发后,执行的回调方法:onClick
    String callbackMethod();
}

 

2:InjectManager:解析所有的注解:

package com.example.dagger;

import java.lang.annotation.Annotation;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @author yanjim
 * @Date 2023/5/26
 */
public class InjectManager {
    public static void inject(Object object) {
        injectLayout(object);
        injectView(object);
        injectEvent(object);
    }

    private static void injectEvent(Object object) {
        Class<?> clazz = object.getClass();
        Method[] methods = clazz.getDeclaredMethods();
        for (Method method : methods) {
            //获取方法上的所有注解
            Annotation[] annotations = method.getAnnotations();
            for (Annotation annotation : annotations) {
                //拿到了注解的Class对象
                Class<? extends Annotation> annotationType = annotation.annotationType();
                //获取注解上的注解
                InjectEvent injectEvent = annotationType.getAnnotation(InjectEvent.class);
                if (injectEvent != null) {
                    //获取事件三要素
                    String listenerSetter = injectEvent.listenerSetter();
                    Class listenerType = injectEvent.listenerType();
                    String callbackMethod = injectEvent.callbackMethod();
                    //前面拿到了注解的Class对象,这里就可以通过反射获取注解的值
                    try {

                        //这里的method是onClick方法
                        ListenerInvocationHandler listenerInvocationHandler = new ListenerInvocationHandler(object, method);

                        //传入的linstenerType是谁,就返回谁的代理对象
                        Object proxyInstance = Proxy.newProxyInstance(listenerType.getClassLoader(),
                                new Class[]{listenerType}, listenerInvocationHandler);

                        //注解里面的value实际上是一个方法,所以需要通过反射获取
                        int[] ids = (int[]) (annotationType.getMethod("value")).invoke(annotation);

                        for (int id : ids) {
                             /*button.setOnClickListener(new View.OnClickListener() {
                            @Override
                                          public void onClick(View view) {
                                                         button.setText(" bbbbbbbbbbbbbbbbbbbb");
                                             }
                                                });*/

                            Method findViewById = clazz.getMethod("findViewById", int.class);
                            Object view = findViewById.invoke(object, id);

                            Method setterMethod = view.getClass().getMethod(listenerSetter, listenerType);
                            setterMethod.invoke(view, proxyInstance);

                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }


    /**
     * 布局注入:执行MainActivity.setContentView(R.layout.activity_main);
     */
    private static void injectLayout(Object object) {
        //setContentView(R.layout.activity_main);
        //object:MainActivity
        Class<?> clazz = object.getClass();
        //clazz:com.example.dagger.MainActivity
        //获取注解,需要注意的是,使用反射获取注解时,需要确保注解的保留策略为 RetentionPolicy.RUNTIME,否则注解信息在运行时可能无法获取到。
        InjectLayout annotation = clazz.getAnnotation(InjectLayout.class);

        if (annotation != null) {
            //layout:R.id.activity_main
            //获取注解的值
            int layoutId = annotation.value();
            try {
                //setContentView(R.layout.activity_main);
                //执行具体的方法
                clazz.getMethod("setContentView", int.class).invoke(object, layoutId);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 控件注入:执行MainActivity.button = findViewById(R.id.button);
     */
    private static void injectView(Object object) {
        Class<?> clazz = object.getClass();
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            InjectView annotation = field.getAnnotation(InjectView.class);
            //不等于null,说明该属性被注解了
            if (annotation != null) {
                //R.id.button
                int viewId = annotation.value();
                try {
                    Method findViewById = clazz.getMethod("findViewById", int.class);

                    Object view = findViewById.invoke(object, viewId);

                    field.setAccessible(true);
                    field.set(object, view);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

ListenerInvocationHandler

public class ListenerInvocationHandler implements InvocationHandler {

    private Object obj;
    private Method MyMethod;

    public ListenerInvocationHandler(Object obj, Method MyMethod) {
        this.obj = obj;
        this.MyMethod = MyMethod;
    }

    @Override
    public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
        return MyMethod.invoke(obj, objects);
    }
}

 

posted @ 2023-03-31 08:55  蜗牛攀爬  阅读(147)  评论(0编辑  收藏  举报