ButterKnife source code analysis, butterknife source code

Source: Internet
Author: User

ButterKnife source code analysis, butterknife source code

Speaking of Butterknife, we will think of the compile-time annotation. Here I emphasize several important concepts:

What is IOC?

Inversion of Control, abbreviated as IOC. What does it mean? It is a class that requires many member variables. In traditional writing, if you want to use these member variables, you can use new variables! The IOC principle is: NO, we don't need new, so the coupling degree is too high. You configure an xml file to indicate which class and which member variables are used in it, when you wait to load this class, I will help you inject (new) into it; of course, you will think that writing a configuration file, slot, this is a lot of trouble. As a result, another solution has emerged, so you may be in trouble with the configuration file. Use annotations to add annotations to the member variables you need to Inject. For example: @ Inject, that's all. You can't say this word. Of course, with configuration files and annotations, how can we inject them? Is it actually to convert the string class path into a class? reflection is required in this case.

What is reflection?

JAVA is not a dynamic programming language. To make the language more flexible, JAVA introduces a reflection mechanism. The JAVA reflection mechanism is in the running state. For any class, all attributes and methods of this class can be known; for any object, any method of this class can be called; this kind of dynamically obtained information and the function of dynamically calling object methods is called the reflection mechanism of java language. The Java reflection mechanism mainly provides the following functions: to judge the class to which any object belongs at runtime; to construct the object of any class at runtime; judge the member variables and methods of any class at runtime; call the methods of any object at runtime; generate a dynamic proxy.

What is annotation?

Annotations and reflection introduced after JAVA1.5 are implemented based on reflection. Annotations in JAVA are special interfaces inherited from java. lang. annotation. Annotation. How can I set attributes for interfaces? In short, JAVA generates an instance implementing the Annotation interface through dynamic proxy, and then assigns a value to the attributes of the proxy instance, in this way, the configuration information of the annotation can be obtained through reflection when the program is running (if the annotation is visible at runtime. To put it bluntly, annotation is equivalent to a mark. Adding an annotation in the program is equivalent to marking the program. The program can use the reflection mechanism of JAVA to understand whether there are any tags on your class and various elements, and perform corresponding events for different tags. Tags can be added to parameters of packages, classes, methods, methods, and member variables.

Now ButterKnife is very popular. I believe many people have used it and said nothing has no reflection, and built-in plug-ins do not need to write code. My first reaction was blind. I'm curious, don't think it's okay? So powerful. Today, I will show you the source code of ButterKnife and write it by myself. I believe this will give you a deeper understanding. Writing it by yourself does not mean we will repeat the wheel. It's not just nothing to do, but knowledge is used to awaken wisdom. We can use this knowledge to other places, such as bypassing the limitations of payment and automatically reading and generating configuration code. It is very important to extract nutrition from the source code.

1. ButterKnife Principle Analysis

Before analyzing the source code, we should first use it. We will not introduce it in detail here. If you are not familiar with the source code, you can read other people's articles. There are also many source code analysis articles on the Internet. You can check them out. I have read many articles in this regard. Find the github address and learn how to use it. When we click run, we can find such code carefully:

// Generated code from Butter Knife. Do not modify!package com.darren.architect_day05;public class MainActivity_ViewBinding implements Unbinder {  private MainActivity target;  @UiThread  public MainActivity_ViewBinding(MainActivity target) {    this(target, target.getWindow().getDecorView());  }  @UiThread  public MainActivity_ViewBinding(MainActivity target, View source) {    this.target = target;    target.textView1 = Utils.findRequiredViewAsType(source, R.id.tv1, "field 'textView1'", TextView.class);    target.textView2 = Utils.findRequiredViewAsType(source, R.id.tv2, "field 'textView2'", TextView.class);  }  @Override  @CallSuper  public void unbind() {    MainActivity target = this.target;    if (target == null) throw new IllegalStateException("Bindings already cleared.");    this.target = null;    target.textView1 = null;    target.textView2 = null;  }}

I think a lot of people should understand this. In fact, every time we click run, we will scan the runtime annotation and then automatically generate such a class, it is automatically generated, not written by ourselves. The rule is like the above. xxx_ViewBinding is used as the class name to implement Unbinder to go to findViewById or setOnclickListener In the constructor of xxx_ViewBinding. As long as we instantiate a MainActivity_ViewBinding object in MainActivity, all attributes in MainActivity will be assigned a value. At present, there is no reflection involved, so we only need to understand how to generate code. In this way, we can write a ButterKnife by ourselves.

2. apt Generation Code

How to automatically generate code is actually mentioned in the thinking in java book. This is the knowledge of the mirror section. We can refer to ButterKnife. We create a new Java project. Note that it must be a Java project. Otherwise, classes may not be found.

New ButterKnifeProcessor inherits AbstractProcessor

@ AutoService (Processor. class) public class ButterKnifeProcessor extends actprocessor {/*** is used to specify the supported AnnotationTypes ** @ return */@ Override public Set
 
  
GetSupportedAnnotationTypes () {Set
  
   
Types = new LinkedHashSet <> (); for (Class
   Annotation: getSupportedAnnotations () {types. add (annotation. getCanonicalName ();} return types;}/*** refer to the ButterKnife syntax ** @ return */private Set
   
    
> GetSupportedAnnotations () {Set
    
     
> Annotations = new LinkedHashSet <> (); annotations. add (BindView. class); return annotations;}/*** specifies the supported SourceVersion ** @ return */@ Override public SourceVersion getSupportedSourceVersion () {return SourceVersion. latestSupported () ;}@ Override public boolean process (Set
     Set, RoundEnvironment roundEnvironment) {// debug and print System. out. println ("dependency>"); System. out. println ("------------------------------------>"); return false ;}}
    
   
  
 

The above is so simple. Click RUN apk. If you can see the print below, it indicates that the configuration is correct. If you do not see any printing, it indicates that it does not work, first look for the problem:

If we have annotations in any part of the code, we will come to the process method. We only need to generate code in this method, the basic knowledge of annotation reflection generics is not described in detail here.

@ Override public boolean process (Set
 Set, RoundEnvironment roundEnvironment) {// debug printing // System. out. println ("dependency>"); // System. out. println ("---------------------------------->"); Set
 BindViewElements = roundEnvironment. getElementsAnnotatedWith (BindView. class); // parse Element Map
 
  
> AnalysisElementMap = new LinkedHashMap <> (); for (Element bindViewElement: bindViewElements) {Element enclosingElement = bindViewElement. getEnclosingElement (); List
  
   
Elements = analysisElementMap. get (enclosingElement); if (elements = null) {elements = new ArrayList <> (); analysisElementMap. put (enclosingElement, elements);} elements. add (bindViewElement);} // generates a java class for (Map. entry
   
    
> Entry: analysisElementMap. entrySet () {Element enclosingElement = entry. getKey (); List
    
     
Elements = entry. getValue (); String classNameStr = enclosingElement. getSimpleName (). toString (); ClassName unbinderClassName = ClassName. get ("com. butterknife "," Unbinder "); // Assembly class: xxx_ViewBinding implements Unbinder TypeSpec. builder typeSpecBuilder = TypeSpec. classBuilder (classNameStr + "_ ViewBinding "). addModifiers (Modifier. PUBLIC, Modifier. FINAL ). addSuperinterface (unbinderClassName); ClassName callSuperClassName = ClassName. get ("android. support. annotation "," CallSuper "); // assemble the unbind method MethodSpec. builder unbindMethodBuilder = MethodSpec. methodBuilder ("unbind "). addAnnotation (Override. class ). addAnnotation (callSuperClassName ). addModifiers (Modifier. PUBLIC ). returns (TypeName. VOID); ClassName uiThreadClassName = ClassName. get ("android. support. annotation "," UiThread "); ClassName parameterClassName = ClassName. bestGuess (classNameStr); // assemble the constructor: public xxx_ViewBinding (xxx target) MethodSpec. builder constructorBuilder = MethodSpec. constructorBuilder (). addAnnotation (uiThreadClassName ). addModifiers (Modifier. PUBLIC ). addParameter (parameterClassName, "target"); // Add target. textView1 = Utils. findViewById (target, R. id. tv1); for (Element bindViewElement: elements) {String filedName = bindViewElement. getSimpleName (). toString (); ClassName utilClassName = ClassName. get ("com. butterknife "," Utils "); int resId = bindViewElement. getAnnotation (BindView. class ). value (); constructorBuilder. addStatement ("target. $ L = $ T. findViewById (target, $ L) ", filedName, utilClassName, resId);} typeSpecBuilder. addMethod (constructorBuilder. build (); typeSpecBuilder. addMethod (unbindMethodBuilder. build (); try {// write to generate java class String packageName = mElementUtils. getPackageOf (enclosingElement ). getQualifiedName (). toString (); JavaFile. builder (packageName, typeSpecBuilder. build ()). addFileComment ("automatically generated by ButterKnife "). build (). writeTo (mFiler);} catch (IOException e) {e. printStackTrace (); System. out. println ("overturned") ;}return false ;}
    
   
  
 

Finally, let's take a look at the generated class file. In the future, we can use the annotation points during compilation to generate code to solve some difficult problems in our development process.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.