Introduction to ButterKnife and its principles (6) butterknife

Source: Internet
Author: User

Introduction to ButterKnife and its principles (6) butterknife

I analyzed the source code of ButterKnife and understood its implementation principle. Then I will apply the principle to practice.

I. Custom annotations

Only BindView annotations are provided for ease of understanding.

@Target(ElementType.FIELD)@Retention(RetentionPolicy.CLASS)public @interface BindView {    int value();}
2. Add annotation Processors

Add the ViewInjectProcessor annotation processor and read the code,

public class ViewInjectProcessor extends AbstractProcessor {    // Storing all the annotation information under the same Class    Map
 
   classMap = new HashMap<>();    private Filer mFiler;    Elements elementUtils;    private Messager mMessager;    @Override    public synchronized void init(ProcessingEnvironment processingEnv) {        super.init(processingEnv);        mFiler = processingEnv.getFiler();        elementUtils = processingEnv.getElementUtils();        mMessager = processingEnv.getMessager();    }    @Override    public Set
  
    getSupportedAnnotationTypes() {        return super.getSupportedAnnotationTypes();    }    @Override    public SourceVersion getSupportedSourceVersion() {        return super.getSupportedSourceVersion();    }    @Override    public boolean process(Set
    annotations, RoundEnvironment roundEnv) {        // collect information        gatherInformation(roundEnv);        // generate java code        try {            for (BindingClass bindingClass : classMap.values()) {                info("Generating file for %s", bindingClass.getFullClassName());                bindingClass.brewJava().writeTo(mFiler);            }        } catch (IOException e) {            e.printStackTrace();            error("Generate file failed, reason: %s", e.getMessage());        }        return true;    }    ......}
  
 

The init, getSupportedAnnotationTypes, getSupportedSourceVersion, and process methods are implemented respectively.

The process method mainly implements the gatherInformation method for collecting information and the brewJava method for generating Java code.

For details, refer to the gatherInformation method;

private void gatherInformation(RoundEnvironment roundEnv) {        classMap.clear();        gatherBindView(roundEnv);    }    private void gatherBindView(RoundEnvironment roundEnv) {        Set
  elements = roundEnv.getElementsAnnotatedWith(BindView.class);        for (Element element : elements) {            BindingClass bindingClass = getBindingClass(element);            BindViewField field = new BindViewField(element);            bindingClass.addField(field);        }    }

A BindingClass class is defined here. Let's take a look at its implementation;

Public class BindingClass {public TypeElement mClassElement; // class name public List
 
  
MFields; // member variable public Elements mElementUtils; public BindingClass (TypeElement classElement, Elements elementUtils) {this. mClassElement = classElement; this. mElementUtils = elementUtils; mFields = new ArrayList <> ();} public String getFullClassName () {return mClassElement. getQualifiedName (). toString ();} public void addField (BindViewField field) {mFields. add (field);} private String getPackageName (TypeElement type) {return mElementUtils. getPackageOf (type ). getQualifiedName (). toString ();} private static String getClassName (TypeElement type, String packageName) {int packageLen = packageName. length () + 1; return type. getQualifiedName (). toString (). substring (packageLen ). replace ('. ',' $ ');} public JavaFile brewJava () {// method inject (final T host, Object source, Provider provider) ClassName FINDER = ClassName. get ("com. muse. api. finder "," Finder "); MethodSpec. builder injectMethodBuilder = MethodSpec. methodBuilder ("inject "). addModifiers (Modifier. PUBLIC ). addAnnotation (Override. class ). addParameter (TypeName. get (mClassElement. asType (), "target", Modifier. FINAL ). addParameter (TypeName. OBJECT, "source "). addParameter (FINDER, "finder"); // field for (BindViewField field: mFields) {injectMethodBuilder. addStatement ("target. $ N = ($ T) (finder. findView (source, $ L) ", field. getFieldName (), ClassName. get (field. getFieldType (), field. getResId ();} String packageName = getPackageName (mClassElement); String className = getClassName (mClassElement, packageName); ClassName bindingClassName = ClassName. get (packageName, className); ClassName INJECTOR = ClassName. get ("com. muse. api "," Injector "); // generate whole class TypeSpec finderClass = TypeSpec. classBuilder (bindingClassName. simpleName () + "$ ViewInjector "). addModifiers (Modifier. PUBLIC ). addSuperinterface (ParameterizedTypeName. get (INJECTOR, TypeName. get (mClassElement. asType ()))). addMethod (injectMethodBuilder. build ()). build (); return JavaFile. builder (packageName, finderClass ). build ();}}
 
Obviously, the design of this class refers to the BindingSet design in the source code, but the design mode is not used.
3. Provide APIs

Provide InjectHelper externally. The Code is as follows:

public class InjectHelper {    private static final String SUFFIX = "$$ViewInjector";    private static final ActivityFinder ACTIVITY_FINDER = new ActivityFinder();    private static final Map
 
   FINDER_MAP = new HashMap<>();    public static void inject(Activity host) {        inject(host, host, ACTIVITY_FINDER);    }    public static void inject(Object host, Object source, Finder finder) {        String className = host.getClass().getName();        try {            Injector injector = FINDER_MAP.get(className);            if (injector == null) {                String classFullName = host.getClass().getName() + SUFFIX;                Class
   finderClass = Class.forName(classFullName);                injector = (Injector) finderClass.newInstance();                FINDER_MAP.put(className, injector);            }            injector.inject(host, source, finder);        } catch (Exception e) {            throw new RuntimeException("Unable to inject for " + className, e);        }    }}
 

For other classes involved in the API, refer to the source code.

Iv. Framework

Dependencies are required when applications are used.

    implementation project(':ioc-api')    annotationProcessor project(':ioc-compiler')    implementation project(':ioc-annotation')

The Code is as follows:

public class MainActivity extends AppCompatActivity {    @BindView(R.id.test_btn)    Button testBtn;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        InjectHelper.inject(this);        testBtn.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View view) {                Toast.makeText(MainActivity.this, "Button is bind", Toast.LENGTH_SHORT).show();            }        });    }}

The Demo program runs as follows:

The use and source of ButterKnife is now complete.

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.