Imagine a bacon-wrapped Ferrari. Still not better than our free technical reports.
See all our reports

Android Libraries that can clean up your code (with examples)!

Android development is fun there’s no doubt about it! But there is also a lot of repetitive boilerplate code that the platform forces us to write. Quite a lot of it is related to the UI components that you need to process. Some of it is required when you want your application architecture to be clean. There are a lot of operations executing asynchronously in the background – in fact, it’s quite easy to end up with a bunch of spaghetti code that is unreadable or just does not feel right.

Today we’ll look at 7 Android libraries that help you keep your code clean and readable, using an example project so that you can see the libraries in action.

The Project

We’ll be using the Retrofit 2 Sample application that we’ve used before in our getting started with Retrofit guide. It’s a simple open source project that is available on Github. It takes the name of a company and a git repository and lists all the contributors which it displays as a list with their avatars. While it’s not a revolutionary app, it shows how to perform the networking, work with images, create list components, and handle the user input. So it’s a fully featured toy project that you can tinker with.

Let’s apply the annotation libraries to the code and see how they help us maintain our Android app code clean.

Butter Knife

Every time you need to access a view in your code, you need to obtain an instance of the object for that view. You can achieve this by writing the rootView.findViewById() method and then casting the returned object to the correct view type. However, your code will soon build up, especially in the onCreate and onCreateView methods with long annoyingly similar statements. Think about it, in those onCreate methods you initialize everything, bind the listeners and tie the whole UI together. And the more UI elements you have, the longer will a single method be turned into. Let’s take a quick example:

This view will require three views – 2 EditTexts and 1 Button that we need to reference in our fragment. Traditionally we would do something like this:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
  // Inflate the layout for this fragment
  View rootView = inflater.inflate(R.layout.fragment_search, container, false);
  companyEditText = (EditText) rootView.findViewById(R.id.company_edittext);
  repositoryEditText = (EditText) rootView.findViewById(R.id.repository_edittext);
  searchButton = (Button) rootView.findViewById(R.id.search_button);

  searchButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
      searchContributors();
    }
  });
  return rootView;
}

Not much is happening in the code besides finding the views from the layout, storing them in the fields of our activity and adding an anonymous inner class as a listener to handle the search commands. With Butter Knife we can make our lives and the code a lot easier. The view objects are stored in the fields, so we can simply add the Butter Knife @BindView annotation to each field, as follows:

@BindView(R.id.company_edittext) EditText companyEditText;
@BindView(R.id.repository_edittext) EditText repositoryEditText;
@BindView(R.id.search_button) Button searchButton;

We also need to make the onCreateView method aware of the Butter Knife presence. Now the initialization code will only consist of the following short statement:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
  // Inflate the layout for this fragment
  View rootView = inflater.inflate(R.layout.fragment_search, container, false);
  ButterKnife.bind(this, rootView);
  return rootView;
}

We can also take this a step further and skip binding a listener to the searchButton method and instead annotating a onSearchButtonClicked method with an @OnClick annotation that will tie it to the button click magically:

@OnClick(R.id.search_button)
public void onSearchButtonClicked(View searchButton) {
  searchContributors();
}

There are loads of other samples available at the official Butter Knife homepage. Do check them out! In general, if you need to access the view elements programmatically, Butter Knife will make your code more succinct and readable.

Ice Pick

One common problem for many Android applications is the incorrect handling of the activity and fragment lifecycle. Yeah, we know, it’s not the most elegant part of the Android framework. However, disabling the landscape mode in the AndroidManifest file just so your app won’t crumble when the user turns their device sideways is not a proper solution. First of all, it’s kinda silly, and secondly, because a configuration change which won’t be handled correctly by your code can still occur and break everything! So you have to handle the state and the lifecycle of your application components correctly.

The intended way to achieve that is to store the contents of all fields in the activity into a bundle, which is then correctly managed by the Android framework through the lifecycle. It can be quite boring to do.

Luckily, Ice Pick makes our lives so much easier by not having to add all the variable one by one to the bundle that gets saved. Also reading the data from the bundle again, if it even exists, can be challenging, but IcePick simplifies it so much. So, as an example let’s say we need to remember the combination of the last company and repository searched.

First, we annotate the field we want to save to the bundle.

@State String lastSearchCombination;

Now we need to call Ice Pick in the onSaveInstanceState() method:

@Override
public void onSaveInstanceState(Bundle outState) {
  super.onSaveInstanceState(outState);
  Icepick.saveInstanceState(this, outState);
}

And also call upon Ice Pick in the onCreateView() method to restore the state:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                        Bundle savedInstanceState) {
  // Inflate the layout for this fragment
  View rootView = inflater.inflate(R.layout.fragment_search, container, false);
  ButterKnife.bind(this, rootView);
  Icepick.restoreInstanceState(this, savedInstanceState);
  return rootView;
}

Remember, the limitations of what you can save to a bundle still exist. However, there’s is no need to mess with adding constants for bundle keys or adding null checks for the savedInstanceState.

Dart and Henson

Similar to Icepick, Dart helps us to avoid writing all the keys and checks for Intent Extras that are passed from one activity to another. It also works with the Fragments. Here is a small example of how I used the @InjectExtra annotation to pass the search keywords from the search screen to the contributors list where the search will actually be executed.

So I’ve defined two class variables with @InjectExtra annotations:

@InjectExtra String repositoryQuery;
@InjectExtra String companyQuery;

These will be automatically initialized once Dart.inject(this, getActivity()); is called. Now how will the extras end up in the Bundle added to Intent. You can do this manually, but it’s reasonable to use Henson for this. To make it work, I added the following to my SearchFragment:

Intent intentContributorsFragment = 
  Henson.with(getActivity())
        .gotoContributorsFragment()
        .companyQuery(companySearchKeyword)
        .repositoryQuery(repositorySearchKeyword).build();
Intent intentContributorsActivity = 
  Henson.with(getActivity())
        .gotoContributorsActivity().build();
intentContributorsActivity.putExtras(intentContributorsFragment);
startActivity(intentContributorsActivity);

This simplifies the communication between the activities in your code without specifying every extra by hand every time.

Parceler

Parceler helps you with the object serialization. It is needed so you could pass any object as an Intent extra without troubling yourself with the serialization of the objects.

The best thing is that Icepick, Henson, and Dart also play with Parceler nicely. In our application example, I’ve annotated my Contributor class with @Parcel. This allows me to pass Contributor with Dart as an Intent Extra, keeping my code concise and readable.

Timber

Every once in awhile when I do write code, I tend to make mistakes. More often than not it results in the unexpected behaviour of the application. I need to reproduce it before I can fix the problem. A debugger is handy when you know the steps to reproduce, but often logs contain the truth as well!

The out of the box Log class in Android is good enough, providing different logging levels etc. However each Log.d() statement takes 2 parameters. First the tag and second the message. The tag 99% of the time will be the this.class.getName(), and it’s annoying to write it again and again. Thankfully with the Timber library, you can just do

Timber.d("Informative output that needs to be logged.");

And it will provide the correct default tag for you! Also keep in mind you need to initialise Timber before using it. Check out the ContributorsApplication.onCreate() code where I’ve added the call to the

Timber.plant(new Timber.DebugTree());

This is all that is needed to correctly initialize Timber, and there’s no reason not to do it in your app.

Dagger and Dagger2

Last but by no means least the Dagger and Dagger2 libraries are amazing at managing the dependency injection in your app. Having the dependency injection handled for you is a wonderful practice of writing code. You specify the components of the application and how they should interact with each other. You define which parts of the code require other components to work, and voila, the library will initialize the subcomponents for you and inject them where needed. You can check the sample project code to see an example of using it.

However, both Dagger and Dagger 2 are too extensive to explain in full detail in this post. So I won’t! If you want to get started with Dagger there is a great example of the code in for the coffee maker, supported with excellent explanations as well.

Conclusion

There are tons of interesting Android libraries out there and I’ve listed just a few here. Setting them up is fairly easy, as you tend just to need to specify the dependencies and off you go. These are actively maintained projects, so they have great documentation too.

Just be careful with your build process. When you start combining multiple libraries with annotation processors make sure you use provided() or annotationprocessor() rather than combine them in your build.gradle.

If there is some other library you’d recommend, please let me know in the comments, I’d love to expand this post. Or ping me on Twitter: @stensuitsev and let’s have a chat!