Custom annotations
1) Define layout file injection first
//The scope of the annotation is on the class@Target() //Let the retention policy be run-time, encode the annotation into the class file, and let the virtual machine read it@Retention() public @interface ContentView { int value();// When using it, the layout file is specified directly by @ContentView(), which will automatically inject the layout file.}
2) Inject files into the control in the layout
@Target() @Retention() public @interface ViewInject { int value(); }
3) The click response of the control is injected into the file
@Target() @Retention() @interface OnClick { int[] value(); }
Use custom annotations
@ContentView(.activity_test) public class TestActivity extends AppCompatActivity implements { @ViewInject(.edit_text) EditText mEditText; @Override protected void onCreate(Bundle savedInstanceState) { (savedInstanceState); injectContentView(this); injectView(this); injectEvent(this); } @OnClick() @Override public void onClick(View v) { (,"Click Successfully"+().toString(),Toast.LENGTH_SHORT).show(); } }
Obtain annotation parameters through reflection mechanism
Introduction to Method-related methods:
(Object obj,Object args[]) is to call the method represented by the method class, where obj is the object name and args is the parameter passed to the method method
- getMethods(): Methods to obtain public type of class
- getMethod(String name, Class[] params): Get the specific method of the class, name parameter specifies the name of the method, params parameter specifies the parameter type of the method
- getDeclaredMethods(): Get all methods in the class (public, protected, default, private)
- getDeclaredMethod(String name, Class[] params): Get the specific method of the class, name parameter specifies the name of the method, params parameter specifies the parameter type of the method
1. Layout file acquisition
public static void injectContentView(Activity activity){ //Get the class instance of activity Class<? extends Activity> clazz = (); //Get the ContentView annotation of the activity ContentView contentView = (); if(contentView!=null){ //If this annotation exists on the activity, take out the value corresponding to the annotation, which is the layout set before int layoutId=(); try { //Use reflection to call the setContentView method to complete the injection Method setViewMethod = ("setContentView", ); (activity,layoutId); } catch (Exception e) { (); } } }
2. Control acquisition implementation
private static void injectView(Activity activity){ //Get the class instance of activity Class<? extends Activity> clazz = (); //Get all member variables of activity Field[] fields = (); //Transfuse the member variables and get the ViewInject annotation on the member variables for(Field field:fields){ //Get the annotation object on the field, and if there is one, continue to the next field ViewInject viewInject = (); if(viewInject!=null){ //Get the id of the View of ViewInject annotation int viewId = (); //Get the control View view = (viewId); try { //Set field to be accessible, even private ones can be accessed, which can improve efficiency (true); //Set this control to the field object (activity,view);//Set the view control of the activity object to the property corresponding to the above example is mEditText=findViewById(viewId) }catch (IllegalAccessException e){ (); } } } }
3. Control click response
private static void injectEvent(final Activity activity) { Class<? extends Activity> clazz = (); //Get all methods (private methods can also be obtained) Method[] methods = (); for (Method method : methods) { //Get the OnClick annotation above the method OnClick click = (); //If you have, continue with the following code if (click != null) { //Get data in the annotation. Since multiple buttons can be bound to click events, the defined annotation class uses the int[] data type int[] viewId = (); // When launching is required, the method's setAccessible can be set to true to increase the reflection speed. The reason is that after setting to true, the access check is skipped, and it can be accessed even if private modified. (true); //Set a proxy object. When the setOnClickListener is called, the proxy object is passed in. When the click occurs, the method will be invoke. You can call the method with the onClick annotation. //At this time, the listener is the object that implements the interface of the actvivty and implements the onClick method in its interface, which is actually the returned invoke object. Object listener = ((), new Class[]{}, new InvocationHandler() { //This invoke actually means calling. The first parameter is the object being proxied, the second parameter is the method being called, and the third parameter is the parameter array of the method being called. @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { (TAG, "method"+method); (TAG, "args "+args); //Execute the activity object, parameter args method, the activity object is the current activity, and has been implemented //In other words, if the control is clicked, the onClick method of the implemented Activity will be executed Object invoke = (activity, args); return invoke; } }); //listener is an object of the OnClickListener class. Because the method passes the activity, it is the OnClickListener object of the current class. // method is onClick method try { for (int id : viewId) { //Get the corresponding control View v = (id); //"setOnClickListener" is the method name and the type of method parameter Method setClickListener = ().getMethod("setOnClickListener", ); (TAG, "injectEvent: "+listener+"()"+v); //The setOnClickListener method in the View class requires a parameter of the OnClickListener type, which is our proxy object //We need to set the corresponding control //At this time, when we click v (that is, the control we found), the onClick method of the proxy object will be called. The onClick method of the proxy object is the onClick method that implements the interface in the Activity. It is equivalent to executing the method setOnClickListener(listener). And this listener is the object we have above proxy (the current activity implements the OnClicker interface). (v, listener); } } catch (Exception e) { (); } } } }
The above implementations are all injections through reflection at runtime, but it will have some performance losses, while Butterknife uses APT (Annotation Processing Tool) compile-time parsing technology. It already processes the annotations @Bind, @OnClick when the Java code is compiled into Java bytecode. Its principle is to read the Java source code, parse the annotations, generate new Java code, and the newly generated Java code is finally compiled into Java bytecode.
This is the article about Android using annotations and reflection to implement Butterknife functions. For more related Android Butterknife content, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!