APT和运行时IOC
1:APT(Annotation Processing Tool)是属于编译时IOC的一种工具。它是Java编译器提供的一个工具,用于处理在编译时使用的注解。APT通过注解处理器(Annotation Processor)在编译阶段扫描和处理源代码中的注解,并生成额外的代码或进行其他的处理操作。
APT的工作原理如下:
- Java编译器在编译源代码时,会扫描并加载使用了特定注解的类。
- 当编译器遇到使用了特定注解的类时,会调用对应的注解处理器进行处理。
- 注解处理器通过API访问被注解的元素,如类、方法、字段等,并根据注解的定义执行相应的处理逻辑。
- 注解处理器可以生成额外的源代码文件或进行其他的操作,这些生成的代码将在后续的编译过程中被编译器处理。
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); } }