通过注解和反射获取Activity Intent里的数据

实际开发中,我们经常需要在不同页面之间传递数据。我们一般使用在页面之间传递数据。使用Intent put各种类型的数据,然后在另一个页面接收。如果接收的数据量不多还好办,可是多了,页面就会出现很多intent.getIntExtra()、intent.getStringExtra()这样的代码,不美观,而且容易赋值出错,导致一直在排查。或者赋错了值,导致业务出错,又不能及时察觉。如果使用注解和反射解决赋值的问题,就会避免这些问题了。

首先,我们需要定义一个注解。由于是作用于成员变量的,所以@Target值应该为ElementType.FIELD。要实现在运行时获取到Intent传来的值并且赋值的目的,则@Retention必须为RetentionPolicy.RUNTIME。而且Intent传值时,使用的key为String类型,所以注解里的方法返回值应该是String类型。自定义注解如下:

@Target(ElementType.FIELD) 
@Retention(RetentionPolicy.RUNTIME) 
public @interface Autowired { 
    String value() default ""; 
}
然后我们需要定义获取到传递的值,并且赋值的工具类。这里我们仅仅以Activity为例子:
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.Parcelable;
import android.text.TextUtils;

import java.lang.reflect.Field;
import java.util.Arrays;

public class IntentUtil {

    public static void initIntent(Activity activity) {
        Intent intent = activity.getIntent();
        Bundle extras = intent.getExtras();
        if (extras == null) {
            return;
        }
        Class<? extends Activity> cls = activity.getClass();
        //获取此类的所有成员
        Field[] fields = cls.getDeclaredFields();
        for (Field field : fields) {
            if (field.isAnnotationPresent(Autowired.class)) {
                Autowired autowired = field.getAnnotation(Autowired.class);
                //是否使用注解给定了别名
                boolean hasValue = TextUtils.isEmpty(autowired.value());
                //如果别名不为空,使用别名获取key
                String key = hasValue ? field.getName() : autowired.value();
                //判断extras是否有当前key
                if (extras.containsKey(key)) {
                    Object object = extras.get(key);
                    //Parcelable数组类型不能直接设置,需要转化,其他的数组类型都可以直接设置
                    Class<?> componentType = field.getType().getComponentType();
                    if (field.getType().isArray() && Parcelable.class.isAssignableFrom(componentType)) {
                        Object[] objs = (Object[]) object;
                        Object[] objects = Arrays.copyOf(objs, objs.length, (Class<? extends Object[]>) field.getType());
                        object = objects;
                    }
                    field.setAccessible(true);
                    try {
                        /**
                         * 当前的属性字段都是成员变量,所以字段重新设置给activity对象,
                         * 如果不是对象的字段,而是对象对应的类的属性,即static的属性,
                         * 则需要传入null,即field.set(null, object);
                         */
                        field.set(activity, object);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}
现在注解和工具类都定义好了,我们还需要定义几个常用的实体类。如下:
/**
 * 实现序列化接口Parcelable
 */
public class UserParcelable implements Parcelable {
    String name;

    public UserParcelable(String name) {
        this.name = name;
    }

    protected UserParcelable(Parcel in) {
        name = in.readString();
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(name);
    }

    @Override
    public int describeContents() {
        return 0;
    }

    public static final Creator<UserParcelable> CREATOR = new Creator<UserParcelable>() {
        @Override
        public UserParcelable createFromParcel(Parcel in) {
            return new UserParcelable(in);
        }

        @Override
        public UserParcelable[] newArray(int size) {
            return new UserParcelable[size];
        }
    };

    @Override
    public String toString() {
        return "UserParcelable{" +
                "name='" + name + '\'' +
                '}';
    }
}

 

public class UserSerializable implements Serializable {
    String name;

    public UserSerializable() {
    }

    public UserSerializable(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "UserSerializable{" +
                "name='" + name + '\'' +
                '}';
    }
}

 

实体类也定义好了,我们来试试效果。先定义两个Activity和两个xml文件,其中OneActivity负责传值,TwoActivity负责展示:

OneActivity:

public class OneActivity extends AppCompatActivity implements View.OnClickListener {
    private Button btn_start;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_one);

        btn_start = findViewById(R.id.btn_start);
        btn_start.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        ArrayList<UserParcelable> userParcelableArrayList = new ArrayList<>();
        userParcelableArrayList.add(new UserParcelable("博客-第一代码"));

        Intent intent = new Intent(this, SecondActivity.class);
        intent.putExtra("name", "第一代码");
        intent.putExtra("intArray", new int[]{10, 20, 30, 40, 50});
        intent.putExtra("strArray", new String[]{"diyidaima.com", "博客网站"});
        intent.putExtra("userParcelable", new UserParcelable("序列化数据"));
        intent.putExtra("userParcelableArray", new UserParcelable[]{new UserParcelable("序列化数组数据1")});
        intent.putExtra("users", new UserSerializable[]{new UserSerializable("序列化数组数据2")});
        intent.putParcelableArrayListExtra("userParcelableArrayList",userParcelableArrayList);
        
        startActivity(intent);
    }
}

 

OneActivity:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".OneActivity">

    <Button
        android:id="@+id/btn_start"
        android:layout_width="0dp"
        android:layout_height="45dp"
        android:gravity="center"
        android:text="携带数据跳转到SecondActivity"
        android:layout_marginHorizontal="16dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>

 

SecondActivity:

public class SecondActivity extends AppCompatActivity {
    @Autowired
    String name;

    @Autowired("intArray")
    int[] intArr;

    @Autowired("strArray")
    String[] strArr;

    @Autowired
    UserParcelable userParcelable;

    @Autowired
    UserParcelable[] userParcelableArray;

    @Autowired("users")
    UserSerializable[] userSerializables;

    @Autowired
    List<UserParcelable> userParcelableArrayList;

    TextView tv_result;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);

        IntentUtil.initIntent(this);

        tv_result = findViewById(R.id.tv_result);
        tv_result.setText("接收到的数据:\n\n" + toString());
    }

    @Override
    public String toString() {
        return "SecondActivity { " + "\n\n" +
                "name = '" + name + '\'' + ", \n\n" +
                "intArr = " + Arrays.toString(intArr) + ", \n\n" +
                "strArr = " + Arrays.toString(strArr) + ", \n\n" +
                "userParcelable = " + userParcelable + ", \n\n" +
                "userParcelableArray = " + Arrays.toString(userParcelableArray) + ", \n\n" +
                "userSerializables = " + Arrays.toString(userSerializables) + ", \n\n" +
                "userParcelableArrayList = " + userParcelableArrayList + "\n\n" +
                '}';
    }
}

 

activity_second:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".SecondActivity">

    <TextView
        android:id="@+id/tv_result"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_margin="16dp"
        android:textColor="@android:color/black"
        android:textSize="14sp"
        android:text="接收到的数据:"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>

 

测试结果如下图:
intent.png

至此完成!!!

发表回复

后才能评论