The world of annotations in android

Glitch
3 min readJun 20, 2021

I am sure you all have, consciously or unconsciously, used annotations.
Annotations are a class of metadata that can be associated with classes, methods, fields, parameters, and even other annotations. They can be accessed at runtime — using reflection — and/or at compile time using annotation processors.

Annotation processors allow you to generate code based on the annotations in your source at compile time.

To get a feeling of what you can do with annotation processing, here are some popular libraries using it:

Many libraries use annotation processors to get rid of repetitive boilerplate code. Sometimes it is also used as an alternative to reflection because reflection often has more runtime overhead compared to generated code.

If we explain all annotation processing steps one by one;

1 — Build starts in java compiler. (java compiler knows all processors, So If we want to create a new one, we need to tell the compiler about that.)

2 — Starts all Annotation Processors which is not executed. (Every processor has its own implementation)

3 — Loop over annotated elements inside the processor

3 — Finds annotated classes, methods, fields.

4 — Generate a new class with metadata of founded classes, methods, fields. (This is the place where you generate code.)

5 — Create a new file and write your generated string as a class.

6 — Compiler checks if all annotation processors are executed. If not, start to next round.

I know one picture worth a thousand words. Here is the following image.

How does ButteKnife Work?

1 — Define a view as a global variable(Should not be private, it will be explained later.)

2 — Add annotation to view with its ID

3 — Bind your class to Butterknife.

When you click build. ButteKnife does all steps given above. And create an instance of the generated class by reflection. And load your views.

A simple Example

You could go from having something like this:

public class MainActivity extends AppCompatActivity {    @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final TextView descriptionTextView = (TextView) findViewById(R.id.tv_description);
final Button hideButton = (Button) findViewById(R.id.btn_hide);
hideButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
descriptionTextView.setVisibility(View.INVISIBLE);
}
});
}
}

To something like this:

@EActivity(R.layout.activity_main)
public class MainActivity extends AppCompatActivity {
@ViewById(R.id.tv_description)
TextView mDescriptionTextView;
@Click(R.id.btn_hide)
protected void onHideButtonClick() {
mDescriptionTextView.setVisibility(View.INVISIBLE);
}
}

Should you use it?

I would say that if you have a small-medium project it's fine. It will make your code easier to read. But if your application is bigger and contains a lot of navigation flows with complex activity/component life cycles, it can get a little bit hard to implement or difficult to debug and understand errors if something is not appropriately annotated.

Because of how Android Annotations operate, they embed themselves in the life cycle and doing so, you are now dependent on their lifecycle (e.g. if you annotate your views with @ViewById, then you cannot reference them in onCreate(), you need to make a method and annotate it with @AfterViews and when this method gets executed then your views are ready to be used). This is not necessarily a problem, you just need to have a good understanding of Android's behaviors and well, Android Annotations behaviors as well.

In summary, as in any library, if you depend on it, well you depend on it, so you might as well understand very thoroughly how it works. Your project now depends on someone else’s.

Ta-da!! we learned a simple new concept . Keep coding!!

--

--