Created
November 20, 2015 19:12
-
-
Save deveshmittal/6fe3a881816d2ad294e3 to your computer and use it in GitHub Desktop.
How butterknife works?
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| How ButterKnife actually works? | |
| Java Annotation Processing | |
| Annotation processing is a tool build in javac for scanning and processing annotations at compile time. | |
| You can define your own annotations and a custom processor to handle them. | |
| Annotations are scanned and processed at compile time. | |
| An Annotation Processor reads java code, process its annotations and generate java code in response. | |
| Generated java code is then compiled again as a regular java class | |
| An Annotation Processor can not change an existing input java class. Neither adding or modifiying methods. | |
| Java Compiler | |
| At OpenJDK you can read an excellent overview of how Java compiler works. | |
| This chart summarizes very well the part that interests us: | |
| java compilerJava compiling process overview | |
| ButterKnife workflow | |
| When you compile your Android project ButterKnife Annotations Processor process method is executed, doing the following: | |
| First, it scans all java classes looking for ButterKnife annotations: @InjectView, @OnClick, etc. | |
| When it find a class with any of these annotations it creates a file called: <className>$$ViewInjector.java | |
| This new ViewInjector class contains all the neccessary methods to handle annotation logic: findViewById, view.setOnClickListener, etc. | |
| Finally, during execution, when we call ButterKnife.inject(this) each ViewInjector inject method is called. | |
| Sample | |
| For the sample code you can find at https://github.com/JakeWharton/butterknife this is what happens underneath: | |
| butterknife sample | |
| ExampleActivity.java | |
| class ExampleActivity extends Activity { | |
| @FindView(R.id.user) EditText username; | |
| @FindView(R.id.pass) EditText password; | |
| @OnClick(R.id.submit) void submit() { | |
| // TODO call server... | |
| } | |
| @Override public void onCreate(Bundle savedInstanceState) { | |
| super.onCreate(savedInstanceState); | |
| setContentView(R.layout.simple_activity); | |
| ButterKnife.bind(this); | |
| // TODO Use fields... | |
| } | |
| } | |
| During compile time this java class will be generated: | |
| ExampleActivity$$ViewBinder.java | |
| public class ExampleActivity$$ViewBinder<T extends com.lgvalle.samples.ui.ExampleActivity> implements ViewBinder<T> { | |
| @Override public void bind(final Finder finder, final T target, Object source) { | |
| View view; | |
| view = finder.findRequiredView(source, 2131361865, "field 'user'"); | |
| target.username = finder.castView(view, 2131361865, "field 'user'"); | |
| view = finder.findRequiredView(source, 2131361868, "field 'pass'"); | |
| target.password = finder.castView(view, 2131361868, "field 'pass'"); | |
| view = finder.findRequiredView(source, 2131361874, "field 'submit' and method 'submit'"); | |
| view.setOnClickListener( | |
| new butterknife.internal.DebouncingOnClickListener() { | |
| @Override public void doClick(android.view.View p0) { | |
| target.submit(); | |
| } | |
| }); | |
| } | |
| @Override public void reset(T target) { | |
| target.username = null; | |
| target.password = null; | |
| } | |
| } | |
| Then, during execution time, when we call ButterKnife.bind(this); what happens is: | |
| ButterKnife calls findViewBinderForClass(ExampleActivity.class) finding ExampleActivity$$ViewBinder.java | |
| ExampleActivity$$ViewBinder.bind() is executed, finding and casting views and setting them into ExampleActivity.class attributes, which are public | |
| onClickListeners for views are setted up as a wrapper to execute target defined method to handle clicks (annotated with @OnClick) | |
| This is why annotated attributes and methods must be public: ButterKnife needs to be able to access them from a separate class. | |
| Article : | |
| http://lgvalle.xyz/2015/05/04/butterknife |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment