Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

6 Android Libraries That Can Clean Up Your Code

DZone's Guide to

6 Android Libraries That Can Clean Up Your Code

There are tons of interesting Android libraries out there. Setting them up is fairly easy, as you tend just to need to specify the dependencies and off you go.

· Mobile Zone
Free Resource

Download this comprehensive Mobile Testing Reference Guide to help prioritize which mobile devices and OSs to test against, brought to you in partnership with Sauce Labs.

Android development is fun — there’s no doubt about it. However, 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 that just does not feel right.

Today we’ll look at seven 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. 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.

1. 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. The more UI elements you have, the longer will a single method be turned into. Let’s take a quick example:

Image title

This view will require three views: two EditTexts and one 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 annotate 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.

2. 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, because it’s kinda silly, and secondly, because a configuration change that 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 Ice Pick 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);
}

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 no need to mess with adding constants for bundle keys or adding null checks for the savedInstanceState.

3. Dart and Henson

Similar to Ice Pick, 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.

4. Parceler

Parceler helps you with the object serialization. It is needed so you can 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.

5. Timber

Every once in awhile, when I do write code, I tend to make mistakes. More often than not, this results in unexpected behavior from an 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 two 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 that you need to initialize Timber before using it. Check out the ContributorsApplication.onCreate() code where I’ve added the call:

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.

6. Dagger and Dagger 2

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.

Analysts agree that a mix of emulators/simulators and real devices are necessary to optimize your mobile app testing - learn more in this white paper, brought to you in partnership with Sauce Labs.

Topics:
android ,app development ,mobile ,clean code

Published at DZone with permission of Sten Suitsev. See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}