Foreword

Reflection of the effect are not much said, I am in this time reflection is one tuo throw code, always let a person feel very not elegant, now have I this reflection tools, so you can work a word with grace to complete reflection, the tool on the class is standing on the shoulders of jOOR transformation, repaired it didn’t finish the work, as for the repair, The source code analysis will detail where this utility class is now in AndroidUtilCode 1.12.0. Let’s take a look at its functionality.

Functions

Its APIs are shown below:

Reflectutils. Java -> Test

Reflect: Sets the class to be reflected newInstance: instantiates a reflection object Field: sets the reflected field method: sets the reflected method get: obtains the desired reflectionCopy the code

Use

Instantiate the reflection object

For example, we can instantiate a String like this:

String str1 = ReflectUtils.reflect(String.class).newInstance().get();
// equals: String str1 = new String();

String str2 = ReflectUtils.reflect("java.lang.String").newInstance("abc").get();
// equals: String str2 = new String("abc");

String str3 = ReflectUtils.reflect(String.class).newInstance("abc".getBytes()).get();
// equals: String str3 = new String("abc".getBytes());
Copy the code

Set the reflection method

For example, if we want to call String’s substring function, we can do this:

String str1 = ReflectUtils.reflect((Object) "1234").method("substring".2).get();
// equals: String str1 = "1234".substring(2);

String str2 = ReflectUtils.reflect((Object) "1234").method("substring".0.2).get();
// equals: String str1 = "1234".substring(0, 2);
Copy the code

Sets the reflected fields

For example, TestPrivateStaticFinal. Java is shown below:

public class TestPrivateStaticFinal {
    private static final int     I1 = new Integer(1);
    private static final Integer I2 = new Integer(1);
}
Copy the code

We want to set the values of I1 and I2 to 2 as follows:

ReflectUtils.reflect(TestPrivateStaticFinal.class).field("I1".2);
ReflectUtils.reflect(TestPrivateStaticFinal.class).field("I2".2);
Copy the code

To obtain the values of I1 and I2, perform the following operations:

ReflectUtils.reflect(TestPrivateStaticFinal.class).field("I1").get()
ReflectUtils.reflect(TestPrivateStaticFinal.class).field("I2").get()
Copy the code

Of course, there are more advanced operations for field operations, such as the test1.java test class shown below:

public class Test1 {
    public static int     S_INT1;
    public static Integer S_INT2;
    public        int     I_INT1;
    public        Integer I_INT2;

    public static Test1 S_DATA;
    public        Test1 I_DATA;
}
Copy the code

The unit tests I ran on it look like this:

@Test
public void fieldAdvanced(a) throws Exception {
    ReflectUtils.reflect(Test1.class)
            .field("S_DATA", ReflectUtils.reflect(Test1.class).newInstance())// set test1.class S_DATA to new Test1()
            .field("S_DATA")// Get the S_DATA from test1. class
            .field("I_DATA", ReflectUtils.reflect(Test1.class).newInstance())Test1.class I_DATA = new Test1();
            .field("I_DATA")// Get I_DATA from S_DATA in test1. class
            .field("I_INT1".1)// Set the I_INT1 value of the I_DATA field of the S_DATA field in test1. class to 1
            .field("S_INT1".2);Test1.class set I_INT1 of S_DATA to 2
    assertEquals(2, Test1.S_INT1);// The static variable is the last set of 2
    assertEquals(null, Test1.S_INT2);// No operation is null
    assertEquals(0, Test1.S_DATA.I_INT1);// the value is 0
    assertEquals(null, Test1.S_DATA.I_INT2);// the value is 0
    assertEquals(1, Test1.S_DATA.I_DATA.I_INT1);// The penultimate operation is set to 1
    assertEquals(null, Test1.S_DATA.I_DATA.I_INT2);// No operation is null
}
Copy the code

If you want to learn more about how to use it, you can check out my unit test class ReflectUtilsTest. Here’s how to use it and how to implement it.

Achieve

The realization is to stand on the shoulder of jOOR for transformation, which encapsulates a private final Object Object. Get () gets its value. For example, take a look at newInstance, which involves the following code:

/** * instantiates the reflection object **@paramParameter * required for args instantiation@return {@link ReflectUtils}
 */
public ReflectUtils newInstance(Object... args) { Class<? >[] types = getArgsType(args);try{ Constructor<? > constructor = type().getDeclaredConstructor(types);return newInstance(constructor, args);
    } catch(NoSuchMethodException e) { List<Constructor<? >> list =new ArrayList<>();
        for(Constructor<? > constructor : type().getDeclaredConstructors()) {if(match(constructor.getParameterTypes(), types)) { list.add(constructor); }}if (list.isEmpty()) {
            throw new ReflectException(e);
        } else {
            sortConstructors(list);
            return newInstance(list.get(0), args); }}}privateClass<? >[] getArgsType(final Object... args) {
    if (args == null) return new Class[0]; Class<? >[] result =new Class[args.length];
    for (int i = 0; i < args.length; i++) {
        Object value = args[i];
        result[i] = value == null ? NULL.class : value.getClass();
    }
    return result;
}

private void sortConstructors(List
       
        > list)
       > {
    Collections.sort(list, newComparator<Constructor<? > > () {@Override
        public int compare(Constructor
        o1, Constructor
        o2) { Class<? >[] types1 = o1.getParameterTypes(); Class<? >[] types2 = o2.getParameterTypes();int len = types1.length;
            for (int i = 0; i < len; i++) {
                if(! types1[i].equals(types2[i])) {if (wrapper(types1[i]).isAssignableFrom(wrapper(types2[i]))) {
                        return 1;
                    } else {
                        return -1; }}}return 0; }}); }private ReflectUtils newInstance(finalConstructor<? > constructor,final Object... args) {
    try {
        return new ReflectUtils(
                constructor.getDeclaringClass(),
                accessible(constructor).newInstance(args)
        );
    } catch (Exception e) {
        throw newReflectException(e); }}private finalClass<? > type;private final Object object;

private ReflectUtils(finalClass<? > type, Object object) {
    this.type = type;
    this.object = object;
}
Copy the code

What jOOR does not do is not sort multiple matching constructors, but return the first matching one. This is a bit abstract, but I should make it clear by using an example. For example, there are two constructors as follows:

public class Test {

    public Test(Number n) {}public Test(Object n) {}}Copy the code

The jOOR reflection call Constructor argument passing in a Long will probably pass the Test(Object n) Constructor, which I have modified by sorting the matching Constructor to match the nearest parent class. The Test(Number n) constructor is used, and the jOOR in method is also used. I have fixed it.

And jOOR of private static final fields first get again set to abnormal Java lang. IllegalAccessException is unusual, Because of private static final fields get didn’t remove the final properties, if you can remove the final when the get can solve, that wouldn’t be an error in the set. However, there is no Field in the Android SDK. Class. GetDeclaredField (” modifiers “) of this Field, so will quote NoSuchFieldException anomaly, this aspect I did fault-tolerant processing, The relevant code is as follows:

/** * Sets the reflected field **@paramName field name *@return {@link ReflectUtils}
 */
public ReflectUtils field(final String name) {
    try {
        Field field = getField(name);
        return new ReflectUtils(field.getType(), field.get(object));
    } catch (IllegalAccessException e) {
        throw newReflectException(e); }}/** * Sets the reflected field **@paramName field name *@paramValue Indicates the field value *@return {@link ReflectUtils}
 */
public ReflectUtils field(String name, Object value) {
    try {
        Field field = getField(name);
        field.set(object, unwrap(value));
        return this;
    } catch (Exception e) {
        throw newReflectException(e); }}private Field getField(String name) throws IllegalAccessException {
    Field field = getAccessibleField(name);
    if ((field.getModifiers() & Modifier.FINAL) == Modifier.FINAL) {
        try {
            Field modifiersField = Field.class.getDeclaredField("modifiers");
            modifiersField.setAccessible(true);
            modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
        } catch (NoSuchFieldException ignore) {
            // runs in android will happen}}return field;
}

private Field getAccessibleField(String name) { Class<? > type = type();try {
        return accessible(type.getField(name));
    } catch (NoSuchFieldException e) {
        do {
            try {
                return accessible(type.getDeclaredField(name));
            } catch (NoSuchFieldException ignore) {
            }
            type = type.getSuperclass();
        } while(type ! =null);
        throw newReflectException(e); }}private Object unwrap(Object object) {
    if (object instanceof ReflectUtils) {
        return ((ReflectUtils) object).get();
    }
    return object;
}
Copy the code

So the utility class supports both Java and Android perfectly.

Conclusion

Well, this reflection tool class is introduced to this, do not feel so elegant, if you think good words later encounter reflection problems, then quickly use my tool class, such a good thing hidden not really is a pity oh.

I’ve wrapped most of the common android core utility classes, and I’m not going to add new ones to the core, unless I really need them. The rest I’ll put in Subutil. Thank you for accompanying AndroidUtilCode as it grows.