Java基础-注解/反射(二)

Android知识总结

一、反射定义

反射则是一开始我们并不知道要初始化类对象是什么,自然也无法使用new关键字来创建对象。这时候,我们使用JDK提供的反射API进行反射调用。反射就是在运行状态中,对任意一个类,都能过知道这个类的所有属性和方法;对任意一个对象,都能够调用它的任意方法和属性,并改变它的属性。是Java被视为动态语言的关键。

二、反射各方法示意图

三、ASM字节码插桩

插桩就是将一段代码插入到另一段代码,或替换另一段代码。字节码插桩顾名思义就是在我们编写的源码编译成字节码(Class)后,在Android下生成dex之前修改Class文件,修改或者增强原有代码逻辑的操作。

四、动态代理

1)抽象接口

interface Api{
    fun test(str : String)
}

2)真实对象

class Men : Api{
    override test(str: String) {
        println(str)
    }
}

3)实现类

        val men = Men()
        val messageInterface = Proxy.newProxyInstance(
            MainTest::class.java.classLoader,
            arrayOf>(Api::class.java) ,
            object :InvocationHandler{
                override fun invoke(proxy: Any?, method: Method?, args: Array?): Any? {
                    return method!!.invoke(men, *(args.orEmpty()))
                }
            }) as Api
        messageInterface.test("小米")

kotlin的可变参数和Java的可变参数转换

  • (args.orEmpty()) kotlin的可变参数,并判断参数是否为空。
  • args:等于Java中的可变参数(Object... args)。
  • *号的作用是从可变参数中取出值,kotlin是可变参数传值时*号必须写。

kotlin的可变参数和Java的可变参数转换
invoke方法的返回值可能为空,所以要将返回的Any变成Any?类型。

4)、kotlin中的泛型

  • Kotlin抛弃了通配符?,直接实现了PECS的规则
  • 等价于 <?extends T>
  • 等价于

5)、星号投影:使用 * 代替类型参数

  • 星号投影语法可以用来表名你不知道关于泛型实参的任何信息。例如,一个包含未知类型的元素的列表用这种语法表示为 List<*>。
  • MutableList<> 和 MutableList 不一样,MutableList 这种列表包含的是任意类型的元素,而 MutableList<> 是包含某种特定类型元素的列表。
  • MutableList<> 投影成了 MutableList :当你没有任何元素类型信息的时候,读取 Any? 类型的元素仍然是安全的,但是向列表中写入元素是不安全的。谈到 Java 通配符,Kotlin 的 AnyType<> 对应于 Java 的 MyType

6)、动态代理特性
实际上, Proxy.newProxyInstance 会创建一个Class,与静态代理不同,这个Class不是由具体的.java源文件编译
而来,即没有真正的文件,只是在内存中按照Class格式生成了一个Class。

String name = Api.class.getName()+"$Proxy0";
//生成代理指定接口的Class数据
byte[] bytes = ProxyGenerator.generateProxyClass(name, new Class[]{Api.class});
FileOutputStream fos = new FileOutputStream("lib/" + name+".class");
fos.write(bytes);
fos.close();

然后可以在生成的文件中查看我们的代理类:



在初始化时,获得 method 备用。而这个代理类中所有方法的实现变为:



这里的 h 其实就是 InvocationHandler 接口,所以我们在使用动态代理时,传递的 InvocationHandler 就是一个
监听,在代理对象上执行方法,都会由这个监听回调出来。

五、实现点击事件的注入

java实现

1)、定义注解

@Target(ElementType.ANNOTATION_TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface EventType {
    Class listenerType();

    String listenerSetter();
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@EventType(listenerType = View.OnClickListener.class, listenerSetter = "setOnClickListener")
public @interface OnClick {
    int[] value();
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@EventType(listenerType = View.OnLongClickListener.class, listenerSetter = "setOnLongClickListener")
public @interface OnLongClick {
    int[] value();
}

2)、实现类

public class InjectUtils {

    public static void initUtils(@NotNull Activity activity) {
        Class aClass = activity.getClass();
        Method[] declaredMethods = aClass.getDeclaredMethods();
        for (Method method : declaredMethods) {
            //获得方法上所有注解
            Annotation[] annotations = method.getAnnotations();
            for (Annotation annotation : annotations) {
                Class annotationType = annotation.annotationType();
                if (annotationType.isAnnotationPresent(EventType.class)) {
                    //注解类型
                    EventType eventType = annotationType.getAnnotation(EventType.class);
                    // OnClickListener.class
                    Class listenerType = eventType.listenerType();
                    //setOnClickListener
                    String listenerSetter = eventType.listenerSetter();
                    try {
                        // 不需要关心到底是OnClick 还是 OnLongClick
                        Method valueMethod = annotationType.getDeclaredMethod("value");
                        int[] viewIds = (int[]) valueMethod.invoke(annotation);

                        method.setAccessible(true);
                        ListenerInvocationHandler handler =
                                new ListenerInvocationHandler<>(method, activity);
                        Object listenerProxy = Proxy.newProxyInstance(listenerType.getClassLoader(),
                                new Class[]{listenerType}, handler);
                        // 遍历注解的值
                        for (int viewId : viewIds) {
                            // 获得当前activity的view(赋值)
                            View view = activity.findViewById(viewId);
                            // 获取指定的方法(不需要判断是Click还是LongClick)
                            // 如获得:setOnClickLisnter方法,参数为OnClickListener
                            // 获得 setOnLongClickLisnter,则参数为OnLongClickLisnter
                            Method setter = view.getClass().getMethod(listenerSetter, listenerType);
                            // 执行方法
                            //执行setOnclickListener里面的回调 onclick方法
                            setter.invoke(view, listenerProxy);
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    /**
     * 还可能在自定义view注入,所以是泛型: T = Activity/View
     *
     * @param 
     */
    static class ListenerInvocationHandler implements InvocationHandler {
        private Method method;
        private T target;

        public ListenerInvocationHandler(Method method, T target) {
            this.method = method;
            this.target = target;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            return this.method.invoke(target, args);
        }
    }
}

3)、使用

     @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        InjectUtils.injectEvent(this);
    }

    @OnClick({R.id.btn1, R.id.btn2})
    public void click(View view) {
        switch (view.getId()) {
            case R.id.btn1:
                Log.i(TAG, "click: 按钮1");
                break;
            case R.id.btn2:
                Log.i(TAG, "click: 按钮2");
                break;
        }
    }

    @OnLongClick({R.id.btn1, R.id.btn2})
    public boolean longClick(View view) {
        switch (view.getId()) {
            case R.id.btn1:
                Log.i(TAG, "longClick: 按钮1");
                break;
            case R.id.btn2:
                Log.i(TAG, "longClick: 按钮2");
                break;
        }
        return false;
    }

kotlin实现

  • 1)、注解类
@Target(AnnotationTarget.ANNOTATION_CLASS)
@Retention(RetentionPolicy.RUNTIME)
annotation class EventType(
    val listenerType: KClass<*>,
    val listenerSetter: String
)
@Target(
    AnnotationTarget.FUNCTION,
    AnnotationTarget.PROPERTY_GETTER,
    AnnotationTarget.PROPERTY_SETTER
)
@Retention(RetentionPolicy.RUNTIME)
@EventType(listenerType = View.OnClickListener::class,
    listenerSetter = "setOnClickListener")
annotation class OnClick(vararg val data: Int)
@Target(
    AnnotationTarget.FUNCTION,
    AnnotationTarget.PROPERTY_GETTER,
    AnnotationTarget.PROPERTY_SETTER
)
@Retention(RetentionPolicy.RUNTIME)
@EventType(listenerType = View.OnLongClickListener::class,
    listenerSetter = "setOnLongClickListener")
annotation class OnLongClick(vararg val data : Int)

2)、实现类

object InjectUtils {
    fun initUtils(activity: Activity) {
        val clazz = activity::class.java
        val declaredMethods = clazz.declaredMethods
        for (method in declaredMethods.iterator()) {
            val annotations = method.annotations
            for (annotation in annotations.iterator()) {
                //注解类型
                val annotationType = annotation.annotationClass.javaObjectType
                if (annotationType.isAnnotationPresent(EventType::class.java)) {
                    val eventType = annotationType.getAnnotation(EventType::class.java)
                    // OnClickListener.class
                    val listenerType = eventType.listenerType
                    //setOnClickListener
                    val listenerSetter = eventType.listenerSetter
                    try {
                        // 不需要关心到底是OnClick 还是 OnLongClick
                        //getDeclaredMethod 仅能获取类本身的方法(包括私有、共有、保护) 
                        //getMethod 仅能获取类(及其父类可以自己测试) public 方法
                        val valueMethod = annotationType.getDeclaredMethod("data")
                        val viewIds = valueMethod.invoke(annotation) as IntArray

                        method.isAccessible = true
                        val handler = ListenerInvocationHandler(method, activity)
                        val newProxyInstance = Proxy.newProxyInstance(
                            listenerType.java.classLoader,
                            arrayOf>(listenerType.java), handler
                        )
                        for (id in viewIds.iterator()) {
                            val view = activity.findViewById(id)
                            val viewMethod = view::class.java.getMethod(
                                listenerSetter,
                                listenerType.javaObjectType)
                            viewMethod.invoke(view, newProxyInstance)
                        }
                    } catch (e: Exception) {
                        e.printStackTrace()
                    }

                }
            }
        }

    }

    /**
     * 还可能在自定义view注入,所以是泛型: T = Activity/View
     *
     * @param 
     */
    internal class ListenerInvocationHandler : InvocationHandler {
        private val method: Method
        private val target: T

        constructor(method: Method, target: T) {
            this.method = method
            this.target = target
        }

        override fun invoke(proxy: Any?, method: Method?, args: Array?): Any? {
            return this.method.invoke(target, *(args.orEmpty()))
        }
    }
}
  • 3)、使用
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        InjectUtils.initUtils(this@MainActivity)
    }


    @OnClick(data = [R.id.name_btn1, R.id.name_btn2])
    fun setName1(v: View) {
        when (v.id) {
            R.id.name_btn1 ->
                name_btn1.text = "点击我了" + a++
            R.id.name_btn2 ->
                name_btn2.text = "点击我了" + a++
        }
    }


    @OnLongClick(data = [R.id.name_btn1, R.id.name_btn2])
    fun setName2(v: View) : Boolean{
        when (v.id) {
            R.id.name_btn1 ->
                name_btn1.text = "点击我了" + a++
            R.id.name_btn2 ->
                name_btn2.text = "点击我了" + a++
        }
        return false
    }

利用APT技术实现的项目,源码

你可能感兴趣的:(Java基础-注解/反射(二))