Starting a new native Android app in 2015
Last year I wrote MeasureMe a native Android application. I have just started a new project I find it interesting the difference in tools and techniques I am using only 18 months after MeasureMe.
At the time I identified four main areas that I addressed first.
- Logging
- Inversion of Control / Dependency Injection
- Unit Testing
- Automated Building
In 2014 I used eclipse and ant to control the build process, logging was handled by slf4j and logback, for dependency injection i used roboguice and UnitTesting was done on pure java classes using JUnit.
In 2015 I have made some changes. I now use AndroidStudio and Gradle to build the application. I still use JUnit to write unit tests however Google has made life much easier and there is now a mock android layer available. I also use RoboElectric to extend the reach of the tests. Logging has remains the same and I am very happy with slf4j and logback.
I have moved to Dagger2 for dependency injection. Last year I used RoboGuice and at the time it was a close call between it and Dagger1. In the end I chose RoboGuice largely because the syntax was more familiar and there appeared to be more support and examples for it.
One of the issues I had with Dagger was that injecting each class was a bit clunky, but as it turned out given that I needed to work with ActionBar activity and at the time RoboGuice did not support using the compatibility libraries so I needed to write the clunky code to inject the activities anyway.
I noticed that Google had forked the original Square implementation of dagger and appeared to be doing a lot of work on it. I liked the purely generated approach both for speed and debugging so I decided to switch. For comparison with the other two dependency injection mechanisms the fragment or activity class looks like this
public class RecommendationListFragment extends BaseFragment { @Inject ILoggerFactory logger; protected ApplicationComponent getApplicationComponent(Activity activity) { return ((AndroidApplication) activity.getApplication()) .getApplicationComponent(); } protected boolean isInjected() { return logger != null; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getApplicationComponent(getActivity()).inject(this); } @Override public void onResume() { super.onResume(); if (!isInjected()) { getApplicationComponent(getActivity()).inject(this); } }
I dont remember having to write the onResume code for RoboGuice but it seems like a small detail. The IoC container looked like this
@Module public class ApplicationModule { private final AndroidApplication application; public ApplicationModule(AndroidApplication application) { this.application = application; } @Provides @Singleton Context provideApplicationContext() { return this.application; } @Provides @Singleton ILoggerFactory provideLogger(SlfLoggerFactory loggerFactory) { return loggerFactory; } }
The application sets up the container like this
public class AndroidApplication extends Application { private ApplicationComponent applicationComponent; final private Logger logger = LoggerFactory.getLogger(AndroidApplication.class); @Override public void onCreate() { logger.warn("Application started"); super.onCreate(); this.applicationComponent = initialiseInjector(); logger.debug("Application IoC bound"); } protected ApplicationComponent initialiseInjector() { return DaggerApplicationComponent.builder() .applicationModule(new ApplicationModule(this)) .build(); } public ApplicationComponent getApplicationComponent() { return this.applicationComponent; }
Where the DaggerApplicationComponent is generated code.
So far I have been pleased with Dagger2 in the sense that it just works and I’ve not had any weird behaviour. I have also started to extend what I do with dependency injection, I have started using scoped containers with presenters and unit tests using a mocked container. I will cover this in another post.