This is the sixth day of my participation in the More text Challenge. For details, see more text Challenge

preface

Nice to meet you

This is another new series, inspired by a recent layout optimization, as we know: Android in a small amount of system control is created by means of the new, and most of the controls such as androidx. Appcompat. Under the widget controls, custom controls, the third party controls, etc., are all created by reflection. A lot of reflection creation can cause performance problems, so we need to solve the problem of reflection creation. My solution is:

1. Get all the controls in the Xml layout by writing an Android plug-in

2. After getting the control, create a View class using new method through APT generation

Finally, get the current class by reflection and complete the replacement in the base class

A small layout optimization, involves a lot of things, Android plug-in we follow in the talk, the words Gradle series at present only more 😂, don’t worry, the next will have. We’re going to focus on APT in this series, and there are two key things we need to understand about APT: annotations and reflection

I’m going to focus on reflection today

Github Demo address, you can see the Demo to follow my ideas together analysis

What is reflection?

In simple terms, reflection is: given a class, you can get all the information about that class

In general, according to the principle of object-oriented encapsulation, the properties of Java entity classes are private, we can not get the properties of the class. But we can use reflection to get private variables, methods, constructors, annotations, generics, and so on. It’s very powerful

Note: Google has restricted reflection in Android 9.0 and later, so attributes and methods that are tagged with @hide are not available through reflection

Second, the use of reflection

Here is a known code that we can use to practice reflection:

/ / package path
package com.dream.aptdemo;

// Customize annotation 1
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface CustomAnnotation1{
  
}

// Customize annotation 2
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface CustomAnnotation2{
  
}

// Customize annotation 3
@Target(ElementType.TYPE)
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@interface CustomAnnotation3{

}

/ / interface
interface ICar {
    void combine(a);
}

/ / car
@CustomAnnotation3
class Car<K.V> {
    private String carDesign = "Design draft";
    public String engine = "Engine";

    public void run(long kilometer) {
        System.out.println("Car run " + kilometer + " km"); }}/ / = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = the above all of these are for the following mercedes-benz service 😂 = = = = = = = = = = = = = = = = = = = = = = = = = = =
/ / Benz
@CustomAnnotation1
@CustomAnnotation2
class Benz extends Car<String.Integer> implements ICar {
  
    private String carName = "Mercedes";
    public String carColor = "White";
  
    public Benz(a) {}private Benz(String carName) {
        this.carName = carName;
    }

    public Benz(String carName, String carColor) {
        this.carName = carName;
        this.carColor = carColor;
    }

    @Override
    public void combine(a) {
        System.out.println("Build a Mercedes.");
    }

    private void privateMethod(String params){
        System.out.println("I am a private method:"+ params); }}Copy the code

What follows is a list of common apis for reflection

Three, class,

We can obtain class objects in three ways:

1),Benz.class: class for

2),benz.getClass: Object acquisition

3),Class.forName: Static fetch

 Benz benz = new Benz();
 Class benzClass = Benz.class;
 Class benzClass1 = benz.getClass();
 Class benzClass2 = Class.forName("com.dream.aptdemo.Benz");
Copy the code

Note:

1, in a JVM, a class, only one class object exists. So these three ways of getting the class object out, they’re all the same.

2. The static properties of a class object will be initialized, and will only be executed once, regardless of how the class object is obtained. (This method does not cause static properties to be initialized, except when obtained directly by Benz. Class)

The following process makes frequent use of Benz instances and benzClass objects

4) get the class name

String className = benzClass.getSimpleName();
System.out.println(className);

// Print the result
Benz
Copy the code

5) Get the classpath

String classPath1 = benzClass.getName();
String classPath2 = benzClass.getCanonicalName();
System.out.println(classPath1);
System.out.println(classPath2);
// Print the result
com.dream.aptdemo.Benz
com.dream.aptdemo.Benz
Copy the code

May you have a question here: benzClass getName () and benzClass getCanonicalName () what’s the difference?

From the print above, there is no difference, but if we add an inner class to Benz and get the path of the inner class, you will see the difference:

/ /...
class Benz extends Car implements ICar {
    / /...
    class InnerClass{
        
    }
}

Class<Benz.InnerClass> innerClass = Benz.InnerClass.class;
System.out.println(innerClass.getName());
System.out.println(innerClass.getCanonicalName());
// Print the result
com.dream.aptdemo.Benz$InnerClass
com.dream.aptdemo.Benz.InnerClass
Copy the code

See the difference, so we can conclude that under normal circumstances getCanonicalName and getName get the name of the class that contains the path. But inner classes are a little special. GetName gets the path. Class name $inner class

6) obtain the name of the parent class

String fatherClassName = benzClass.getSuperclass().getSimpleName();
System.out.println(fatherClassName);
// Print the result
Car
Copy the code

7) Obtain the interface

Class[] interfaces = benzClass.getInterfaces();
for (Class anInterface : interfaces) {
    System.out.println(anInterface.getName());
}
// Print the result
com.dream.aptdemo.ICar
Copy the code

8) Create instance objects

// Get the constructor
Constructor constructor = benzClass.getDeclaredConstructor();
// Create an instance
Benz myBenz = (Benz) constructor.newInstance();
// Modify the properties
myBenz.carColor = "Black";
myBenz.combine();
System.out.println(myBenz.carColor);
// Print the resultAssemble a Mercedes BlackCopy the code

Note: Here’s the difference between properties and methods with and without Declare:

1,DeclareProperties and methods of the class are all properties and methods of the class, not inherited

2, don’t takeDeclareProperties and methods of thepublicModified properties and methods, including inherited ones

3, access,privateAttributes and methods that need to be calledsetAccessibleSet to true to allow us to access private variables

Four, attributes,

1) Get a single attribute

Field carName = benzClass.getDeclaredField("carName");
Copy the code

2) Get multiple attributes

// Get all attributes of this class
Field[] declaredFields = benzClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
    System.out.println("Attributes:" + declaredField.getName());
}
// Print the resultProperty: carName Property: carColor// Get all public properties of this class and its parent class
Field[] fields = benzClass.getFields();
for (Field field : fields) {
    System.out.println("Attributes:" + field.getName());
}
// Print the resultProperty: carColor property: EngineCopy the code

3) Set allowed access to private variables

carName.setAccessible(true);
Copy the code

4) Obtain the attribute name

System.out.println(carName.getName());
// Print the result
carName
Copy the code

5) Obtain the variable type

System.out.println(carName.getType().getName());
// Print the result
java.lang.String
Copy the code

6) Get the value of this property in the object

System.out.println(carName.get(benz));
// Print the resultMercedesCopy the code

7) Set a value for the property

carName.set(benz,"sweetying");
System.out.println(carName.get(benz));
// Print the result
sweetying
Copy the code

Five, methods,

1) Get a single method

// Get the public method
Method publicMethod = benzClass.getMethod("combine");

// Get the private method
Method privateMethod = benzClass.getDeclaredMethod("privateMethod",String.class);
Copy the code

2) Get multiple methods

// Get all methods of this class
Method[] declaredMethods = benzClass.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
    System.out.println("Method name:" + declaredMethod.getName());
}
// Print the resultMethod name: privateMethod Method name: combine// Get all public modifiers for this class and its parent class
Method[] methods = benzClass.getMethods();
for (Method method : methods) {
    System.out.println("Method name:" + method.getName());
}
Since all classes inherit Object by default, some methods of Object are printedMethod name: combine Method name: run Method name: wait Method name: WAIT Method name: Wait Method name: equals method name: toString Method name: hashCode Method name: getClass Method name: Notify Method name: notifyAllCopy the code

3) method invocation

Method privateMethod = benzClass.getDeclaredMethod("privateMethod",String.class);
privateMethod.setAccessible(true);
privateMethod.invoke(benz,"Receive incoming parameters");
// Print the resultI am a private method: accepts the arguments passed inCopy the code

Vi. Construction method

1) Get a single constructor

// Get a single constructor for this class
Constructor declaredConstructor = benzClass.getDeclaredConstructor(String.class);

// Get the constructor for the single public decoration of this class
Constructor singleConstructor = benzClass.getConstructor(String.class,String.class);
Copy the code

2) Get multiple constructors

// Get all constructors of this class
Constructor[] declaredConstructors = benzClass.getDeclaredConstructors();
for (Constructor declaredConstructor1 : declaredConstructors) {
    System.out.println("Construction method:" + declaredConstructor1);
}
// Print the resultConstruction method:publicCom.dream.aptdemo.benz ()publicCom. Dream. Aptdemo. Benz (Java. Lang. String, Java. Lang. String) constructor:private com.dream.aptdemo.Benz(java.lang.String)


// Get all public constructors, excluding the superclass constructor
Constructor[] constructors = benzClass.getConstructors();
for (Constructor constructor1 : constructors) {
    System.out.println("Construction method:" + constructor1);
}
// Print the resultConstruction method:publicCom.dream.aptdemo.benz ()public com.dream.aptdemo.Benz(java.lang.String,java.lang.String)
Copy the code

3) The constructor instantiates the object

// Take declaredConstructor as an example
declaredConstructor.setAccessible(true);
Benz declareBenz = (Benz) declaredConstructor.newInstance("");
System.out.println(declareBenz.carColor);
// Print the resultwhite// Take singleConstructor as an example
Benz singleBenz = (Benz) singleConstructor.newInstance("Benz S"."Champagne gold");
System.out.println(singleBenz.carColor);
// Print the resultChampagne goldCopy the code

Seven, generics

1) Obtain the generics of the superclass

Type genericType = benzClass.getGenericSuperclass();
if (genericType instanceof ParameterizedType) {
   Type[] actualType = ((ParameterizedType) genericType).getActualTypeArguments();
   for(Type type : actualType) { System.out.println(type.getTypeName()); }}// Print the result
java.lang.String
java.lang.Integer
Copy the code

Eight, annotations,

1) Get a single annotation

// Get a single annotation for this class or its parent class
Annotation annotation1 = benzClass.getAnnotation(CustomAnnotation1.class);
System.out.println(annotation1.annotationType().getSimpleName());
Annotation annotation3 = benzClass.getAnnotation(CustomAnnotation3.class);
System.out.println(annotation3.annotationType().getSimpleName());
// Print the result
CustomAnnotation1
CustomAnnotation3

// Get a single annotation for this class
Annotation declaredAnnotation1 = benzClass.getDeclaredAnnotation(CustomAnnotation2.class);
System.out.println(declaredAnnotation1.annotationType().getSimpleName());
// Print the result
CustomAnnotation2
Copy the code

2) Get all annotations

// Get Inherited annotations for this class and its parent class (@inherited)
Annotation[] annotations = benzClass.getAnnotations();
for (Annotation annotation : annotations) {
    System.out.println("Annotation name:" + annotation.annotationType().getSimpleName());
}
// Print the resultAnnotation name: CustomAnnotation3 Annotation name: CustomAnnotation1 Annotation name: CustomAnnotation2// Get the annotation for this class
Annotation[] declaredAnnotations = benzClass.getDeclaredAnnotations();
for (Annotation declaredAnnotation : declaredAnnotations) {
    System.out.println("Annotation name:" + declaredAnnotation.annotationType().getSimpleName());
}
// Print the resultAnnotation name: CustomAnnotation1 Annotation name: CustomAnnotation2Copy the code

Reflection is very powerful, but after learning it, you may not know how to use it. Instead, you may feel that it is more direct and convenient to call the method directly. Let’s experience it in practice.

Practice of reflection

The requirements are as follows: Through the background configuration delivery, the switchover of App service functions is completed. Because it is just a simulation, we will complete the switchover of App service functions by reading the local configuration file:

1) First prepare two business classes, assuming they are both functionally complex

/ / package name
package com.dream.aptdemo;

/ / business 1
class Business1 {
 
    public void doBusiness1Function(a){
        System.out.println("Complex Business Function 1"); }}2 / / business
class Business2 {
 
    public void doBusiness2Function(a){
        System.out.println("Complex Business Function 2"); }}Copy the code

2), non reflection mode

public class Client {
  
    @Test
    public void test(a) {
      	// Service function 1
        newBusiness1().doBusiness1Function(); }}Copy the code

Suppose you need to switch from the first business function to the second business function, using non-reflective mode, you must modify the code, and recompile and run it to achieve the effect. Then we can use reflection to switch functions by reading the configuration, so that we don’t need to change the code and the code becomes more generic

3), reflection mode

1. First, prepare a configuration file, as shown below:

2, read the configuration file, reflection creates the instance and calls the method

public class Client {
  
    @Test
    public void test(a) throws Exception {
      	try {
            // Get the file
            File springConfigFile = new File("/Users/zhouying/AndroidStudioProjects/AptDemo/config.txt");
            // Read the configuration
            Properties config= new Properties();
            config.load(new FileInputStream(springConfigFile));
            // Get the classpath
            String classPath = (String) config.get("class");
            // Get the method name
            String methodName = (String) config.get("method");
						
            Reflection creates the instance and calls the method
            Class aClass = Class.forName(classPath);
            Constructor declaredConstructor = aClass.getDeclaredConstructor();
            Object o = declaredConstructor.newInstance();
            Method declaredMethod = aClass.getDeclaredMethod(methodName);
            declaredMethod.invoke(o);
        } catch(Exception e) { e.printStackTrace(); }}}Copy the code

3. After completing the above two steps, we only need to modify the configuration file to complete the switch of App service functions

Ten,

Some of the highlights of this article:

1. Reflect the use of common apis. Note that when accessing private properties and methods, the setAccessible call is set to true to allow us to access private variables

2. Practice completes the switchover of App service functions through reflection

Well, that’s the end of this article, hope to help you 🤝

Thanks for reading this article

The next trailer

I’ll talk about annotations in the next post, so stay tuned

References and Recommendations

How strong is Java reflection? He has these five magical features!

HOW2J reflection section

Here is the full text, the original is not easy, welcome to like, collect, comment and forward, your recognition is the motivation of my creation

Follow my public account, search for sweereferees on wechat and updates will be received as soon as possible