This story was first published at chaitanyapramod.com

Butterknife is majorly composed of three pieces:

Annotations

Defines all the BindView and the like annotations that you use on fields and methods in view code. You can see them all here.

Annotation processor

This is the most interesting part of Butterknife. The implementation is simple(ish). The processor says it’s interested in all the Butterknife annotations. Then the compiler calls the processor with all the elements annotated with these annotations. It creates the XActivity_ViewBinding class. Here is a sample _ViewBinding class generated by the processor.

Let’s focus on the BindView annotation, others behave similarly (listeners are slightly different). The crux of parseBindView is in lines 475-499.

Listeners are implemented with little more complexity, mainly in parseListenerAnnotation , specifically lines 1139-1164 and 1204-1213. If we skip all the error-checking, the method parseListenerAnnotation matches the parameters from the Android listener which are used in the bound method and passes along the required params to BindingSet.Builder#addMethod .

Both these methods collect all the bindings into a BindingSet per type. Once a Type is processed, all these bindings are flushed to a JavaFile named X_ViewBinding.java

See Jake Wharton’s talk on Annotation Processor to get more details of how one is built.

Runtime library

This is a utility library and some sugar. The utilities are used from the generated code and the Butterknife class. When you call Butterknife.bind() , it reflectively loads the processor-generated class, which is named your-view-class-name + "_ViewBinding" and constructs an instance of the class.

The Butterknife class is not really required to make use of the binder. In fact, it adds runtime reflection cost as seen above. You can instead use the generated class directly. This is the approach Dagger 2 has taken as well.

Bonus: The Gradle plugin

The gradle plugin has a very simple function. It lets you use Butterknife in library modules. But why can’t you use Butterknife in library modules like regular application modules? Because the element value in an annotation can only be a constant value for primitive types¹². But the R class generated by Android Gradle plugin doesn’t make the ID values constant in library modules. If libraries had their resource IDs final, then the IDs could collide when building the final APK. So your compile step will complain loudly if you write

@BindView(R.id.viewid) View idView;