Android页面跳转的参数注入(自定义注解+反射实现)
Java注解分类(注解就是一个标记而已 单独使用没有任何意义 一般结合反射使用)
1.标准注解
@Override、@Deprecated、@SuppressWarnings等 ,使用这些注解后编译器就会进行检查
2.元注解 (注解上面的注解)
@Retention(重点)、@Target(重点)、@Inherited(继承注解用的极少)、@Documented(javac可以生成文件记录)、@Repeatable 等 ,元注解也是Java自带的标准注解,只不过用于修饰注解,比较特殊
3. 自定义注解 (本章重点)
在我们自定义注解之前,我们需要把元注解彻底搞懂,否则自定义注解的时候我们完全不知道代码为什么这么写
这里的@Retention 和 @Target非常重要
3.1 @Retention(用来定义该注解在哪一个级别可用 )
//注解信息会保留在源文件、类文件中,在执行的时也加载到Java的JVM中,因此可以反射性的读取
@Retention(RetentionPolicy.RUNTIME)
//此注解类型的信息只会记录在源文件中,编译时将被编译器丢弃,也就是说不会保存在编译好的类信息中
@Retention(RetentionPolicy.SOURCE)//
//编译器将注解记录在类文件中(class文件中),但不会加载到JVM中。如果一个注解声明没指定范围,则系统默认值就是Class
@Retention(RetentionPolicy.CLASS)//
package java.lang.annotation;
public enum RetentionPolicy {
SOURCE,
CLASS,
RUNTIME
}
总结一下
3.2 @Target 用于描述注解的使用范围(即:被描述的注解可以用在什么地方)
@Target(ElementType.FIELD)//用于字段或者属性
@Target(ElementType.TYPE)//用于类、接口、枚举类型
@Target(ElementType.CONSTRUCTOR)//用于构造函数
@Target(ElementType.PARAMETER)//用于方法的参数
@Target(ElementType.METHOD)//用于方法
@Target(ElementType.LOCAL_VARIABLE)//用于局部变量
以上是重点 其他的参数自行去ElementType类看一下 我们一般自定义注解的使用范围基于以上就够用了
接下来就是重点了!!!接下来就是重点了!!!接下来就是重点了!!!
4. 自定义注解步骤(结合本章需求实现)
4.1 自定义注解类(@interface 这里的interface和接口的定义没有任何关系,不要搞混)
package inject;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//注解信息会保留在源文件、类文件中,在执行的时也加载到Java的JVM中,因此可以反射性的读取(使用级别)
@Retention(RetentionPolicy.RUNTIME)
//用于字段或者属性(使用范围)
@Target(ElementType.FIELD)
public @interface InjectView2 {
String paramsKey() default "" ;//此函数 就是我们平时getIntent().getXXX(key) key
}
4.2 定义一个注解工具类
package inject;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.Parcelable;
import android.text.TextUtils;
import android.widget.TextView;
import java.lang.reflect.Field;
import java.util.Arrays;
public class InjectUtils2 {
public static void injectParams(Activity activity) {
Class<? extends Activity> aClass = activity.getClass();
Intent intent = activity.getIntent();
Bundle bundle = intent.getExtras();
if (bundle == null) {
return;
}
Field[] fields = aClass.getDeclaredFields();//获取属性数组
for (Field f : fields) {
//判断是否被InjectView2修饰通过注解获取值
if (f.isAnnotationPresent(InjectView2.class)) {
//得到注解
InjectView2 annotation = f.getAnnotation(InjectView2.class);
String paramsKey = TextUtils.isEmpty(annotation.paramsKey())?f.getName():annotation.paramsKey();
//判断当前的bundle是否包含key
if (!bundle.containsKey(paramsKey)) {
return;
}
//这里一定要用bundle去获取值 intent本质也是通过bundle传值 不要用getIntent().getXXX()去获取值
Object oValue = bundle.get(paramsKey);
//这里特殊处理一下实现了 Parcelable的传值
//获取属性的type
Class<?> fieldType = f.getType();
//判断当前属性如果是实现了Parcelable接口的数组 一般用fieldType.isAssignableFrom(String.class) 数组特殊
if (fieldType.isArray()) {
//再去判断数组中单个元素的属性 如果单个属性是Parcelable(子类)数组 此时需要吧获取到的value强转为数组
Class<?> componentType = fieldType.getComponentType();
if (Parcelable.class.isAssignableFrom(componentType)) {
Object[] newValue = (Object[]) oValue;
//创建一个数组并且赋值给oValue
Object[] parcelables = Arrays.copyOf(newValue, newValue.length, Parcelable[].class);
oValue = parcelables;
}
}
f.setAccessible(true);//设置可访问 防止是private属性
try {
f.set(activity, oValue);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
我们看一下使用
package com.lvyb.javaapplication;
import androidx.appcompat.app.AppCompatActivity;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.os.Bundle;
import android.widget.TextView;
import inject.InjectUtils;
import inject.InjectView;
import inject.Person;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(this, MainActivity2.class);
intent.putExtra(Constans.NAME, "张三");
intent.putExtra(Constans.AGE, 69);
intent.putExtra(Constans.PERSON, new Person("123", "https://www.biadu.com"));
intent.putExtra(Constans.PHONE, "15xxxxxxx793");
intent.putExtra(Constans.SEX, Integer.valueOf(36));
startActivity(intent);
}
}
package com.lvyb.javaapplication;
public class Constans {
public static final String NAME = "name";
public static final String AGE = "age";
public static final String PHONE = "phone";
public static final String PERSON = "person";
public static final String SEX = "sex";
}
package com.lvyb.javaapplication;
import android.os.Bundle;
import android.util.Log;
import androidx.appcompat.app.AppCompatActivity;
import inject.InjectUtils2;
import inject.InjectView2;
import inject.Person;
public class MainActivity2 extends AppCompatActivity {
@InjectView2(paramsKey = Constans.NAME)
String name;
@InjectView2(paramsKey = Constans.AGE)
int age;
@InjectView2//这里不给key 默认用phone当做key
String phone;//这里的参数我没有注解 此时我用key = phone来获取值 正常的getIntent.getString去获取
@InjectView2(paramsKey = Constans.PERSON)
Person person;
@InjectView2(paramsKey = Constans.SEX)
Integer sex;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
InjectUtils2.injectParams(this);
Log.d("ceshi", "onCreate: ====" + name + "======" + age + "======" + phone
+ "=======" + person.getTag() + "=======" + person.getUrl() + "=======" + sex);
}
}
总结
其实自定义注解+反射去实现这种在很多框架中都有实现 也可以自定义注解+反射实现findviewbyid 类似早期的Butterknife ,
感兴趣的同学完全可以去实现一下 非常简单 但是多少会有性能问题 留给大家一个想象的空间 ,通常网上说的早期的Butterknife 影响性能说的是什么地方