Dealing with new architectural components in Android
Guest article from Google I \ O 2017 participant and one of the leaders of GDG Kazan - Arthur Vasilov ( @Arturka ).
The Google I / O conference ended on May 19th. In my opinion, despite the fact that not so many new products were presented this year, from a technical point of view the conference turned out to be interesting.
The biggest and most interesting technical update for me personally was the new Architecture Components (not Android O, in which there is little interesting, and certainly not Kotlin). Google did what it had to do a very long time ago - to develop architecture standards and provide them to developers. Well, better late than never, and let's figure out how useful Google architecture can be.

Personally, the title of the reports didn’t interest me so much that I didn’t even visit them, because I didn’t believe that Google could do something interesting in this area, and not just repeat Mosby / Moxy / <your own bike>. The same skepticism remained after a quick look at the developed libraries. However, after some time I had to change my mind, because:
After a deeper dive, I tried to write an application using this architecture (or rather, rewrite it to assess the complexity of migration), and now I can share this experience.
To get started, consider what is generally meant by good application architecture. In order not to go into philosophical statements about why architecture is needed at all, and so on, we immediately outline the requirements for a good application architecture:
All these are general requirements, but apart from them, quite specific tasks can be identified that should be solved without problems within the framework of a specific architecture:
Of course, paragraphs 2-5 should be implemented taking into account the first paragraph.
For a long time, Android developers independently solved all these problems, but gradually accumulated experience led to the appearance of Clean Architecture . Clean Architecture approaches are very good, except that it is often inappropriate to split the logic into UI and business (as one of the layers may contain almost no code), so many developers use an architecture that can be represented by the following scheme (by slightly rearranging the blocks from Clean Architecture):

The only thing that is not taken into account is the life cycle problem, which everyone solves either
independently or with the help of any libraries. Now let's see what Google has offered us.
Just look at the diagram from Google:

Perhaps it seemed to me, but in my opinion, the previous diagram was simply rotated vertically. And this I do not want to say that Google has appropriated something old, on the contrary, it shows that Google is eyeing the opinion of developers and takes into account all their achievements, which cannot but rejoice.
Let's look at an example of the implementation of a single screen with a single query in the context of the proposed architecture, and at the same time consider the key classes.
One of the big problems in Android is the need to constantly subscribe / unsubscribe from some objects when calling life cycle methods. And since lifecycle methods are only in Activity / Fragment, all such objects (for example, GoogleApiClient, LocationManager, SensorManager and others) should be located only in Activity / Fragment, which leads to a large number of lines of code in these files.
To solve this and other problems, Google suggested using the LiveData class - a class that is closely related to the life cycle and that implements the Observer pattern. By and large, this is a very simple class that encapsulates work with one object, which can be changed and which can be monitored. And again, this is a fairly standard approach.
Consider how we can use LiveData, for example, in order to monitor a user's location. Create the following class:
And now we can write the following code in Activity:
All necessary LiveData lifecycle methods will be called using the first parameter in the observe method - LifecycleOwner. This allows us to know when to subscribe and unsubscribe from various objects (for example, using the onActive and onInactive methods).
LifecycleActivity is a class from the library that implements the LifecycleOwner interface. Unfortunately, it is for some reason inherited from FragmentActivity, and not from AppCompatActivity, and if you still want to inherit from AppCompatActivity, you will have to write some code yourself:
In addition to the onActive and onInactive methods in LiveData, you can receive callbacks to any life cycle method.
Exactly the same model can be used to implement server requests. That is, we do not in any way change the method of obtaining data (everything is also through the Repository), but we delegate the data to LiveData, which is essentially a binding for View.
And now the main question arises - how to deal with the problem of the life cycle? Of course, Google thought about it and gave the developers a component that is undergoing the recreation of Activity - ViewModel .
A few words about ViewModel:
Let’s look at how we can implement a server request for any screen. Create a ViewModel that will control the logic of the screen:
Then View will be able to use this ViewModel as follows:
Since the ViewModel is undergoing a recreation of the Activity, LiveData will be created only once and the server request will be executed only once, that is, these problems are resolved. The question remains of creating a ViewModel.
To access the ViewModel, the ViewModelProviders class is used:
However, there is a slight problem. When we use not the default constructor (and in the example above we passed the Repository object to the constructor), we will have to write our own factory to create the ViewModel:
And now we can create the ViewModel as follows:
In this example, we manually transfer to the Factory all the necessary objects for creating the ViewModel, however, the only normal approach for creating the ViewModel with an increase in the number of parameters is Dagger. Here, Google actually forces you to use it. For better or worse, you decide.
And that’s all - we got the data, displayed it, and all this with life cycle processing. However, there are a few more points that we will consider later.
When I just started to deal with new architectural components, I immediately had a question about how to handle errors with LiveData or get a change in progress. After all, all that we have is an instance of one class, which we must pass to Activity / Fragment. A similar question arises with the display of progress. Such a problem is a bit like a problem with loaders, however there are two options here how to solve it.
First, we can create some class whose object will encapsulate the status of the request, the error message and the data itself, and pass this object to Activity / Fragment. This method is recommended in the documentation. Such a class may look, for example, as follows (for now, we will analyze only error handling):
Then, with each change, we pass to the View an object of this class:
And View handles it accordingly:
And secondly, there is a way that in my opinion is more concise. After all, request status and errors are also some changing data. Then why not put them in LiveData and subscribe to their changes? For these purposes, we can write the following code (and in this situation we will already consider the processing of the download status):
And now View will subscribe not to one LiveData, but to several. This can be very convenient if we write a general handler for displaying progress or errors):
As a result, we can say that error handling using LiveData is almost as good as using the usual RxJava.
As mentioned above, a good architecture should allow you to test the application. Since the architecture from Google does not carry fundamental changes, testing can only be considered at the ViewModel level (since we know how to test Repository / UI).
Testing ViewModel is always a little more complicated than testing Presenter from the MVP pattern, since in MVP the explicit connection between View and Presenter makes it easy to verify that the required methods have been called on View. In the case of MVVM, we have to check the operation of binders or LiveData.
Let's create a test class in which in the setUp method we create the ViewModel:
To test the correctness of the ViewModel, we need to make sure that it fulfills the request to the Repository and passes the received data to LiveData:
Here we omit unnecessary details, but if necessary, you can use the capabilities of Mockito to make sure that exactly the data you need is in LiveData.
However, such a test will not work, since the setValue method in the LiveData class checks that it is called in the main thread of the application (of course, using Looper):
As we well know, tests on JUnit do not like this and fall. But Google developers have provided for this as well. To do this, you need to connect an additional library for tests and add a rule to the test:
Similarly, we can test the progress display during data loading:
Thus, we can write tests for all the necessary components without any problems.
Strictly speaking, data caching does not change in any way with such a change in architecture, because we all also use a repository, which hides all the details of receiving and saving data. However, data caching is worth mentioning in connection with the new library for working with the database - Room.
Everyone knows perfectly well about the existing huge zoo of database libraries in Android. These are Realm , greenDao , ObjectBox , DBFlow , SQLBrite and many others. Therefore, many developers do not know what to choose for their project, and here Room is an ideal option, at least because it is from Google.
Room is a fairly simple library that runs on top of SQLite, uses annotations to generate boilerplate code, and has a fairly familiar and convenient API. However, I do not think that I can tell about Room something more interesting than it was in the session on Google I / O and what is in the documentation . Therefore, here we finish the consideration of architectural innovations.
In general, Google is doing the right thing, and their architecture is really quite thoughtful and convenient. So you can start using it now, at least it’s worth a try in new projects. A small example, on which the article was written and which may help you try the new architecture from Google, is available on Github . Also, of course, it's worth looking at examples from Google.
The Google I / O conference ended on May 19th. In my opinion, despite the fact that not so many new products were presented this year, from a technical point of view the conference turned out to be interesting.
The biggest and most interesting technical update for me personally was the new Architecture Components (not Android O, in which there is little interesting, and certainly not Kotlin). Google did what it had to do a very long time ago - to develop architecture standards and provide them to developers. Well, better late than never, and let's figure out how useful Google architecture can be.

First impression
Personally, the title of the reports didn’t interest me so much that I didn’t even visit them, because I didn’t believe that Google could do something interesting in this area, and not just repeat Mosby / Moxy / <your own bike>. The same skepticism remained after a quick look at the developed libraries. However, after some time I had to change my mind, because:
- Google really made a good library with a convenient API;
- Google did not close, but took into account all the latest developments in the field of architecture of Android applications, taking the best of everything;
- Beginners finally have good documentation that will help them start developing applications with a good architecture, without having to go into detail about the meaning of MVP / MVVM, etc.
After a deeper dive, I tried to write an application using this architecture (or rather, rewrite it to assess the complexity of migration), and now I can share this experience.
Good architecture
To get started, consider what is generally meant by good application architecture. In order not to go into philosophical statements about why architecture is needed at all, and so on, we immediately outline the requirements for a good application architecture:
- Allows decomposing logic (receiving data / processing / user interaction, etc.);
- Scalability. The word that should be in any article about architecture. In Android, this is quite simple, since any application can be divided into screens, between which there will be very transparent connections, and making the architecture of a separate screen scalable does not seem complicated;
- Allows you to write tests for the business logic of the application.
All these are general requirements, but apart from them, quite specific tasks can be identified that should be solved without problems within the framework of a specific architecture:
- Recreation Activity Processing. Probably the most distressing topic in building an application architecture that needs no comment;
- Execution of HTTP requests (both for receiving data and sending);
- Support for data streams, for example, when working with sockets / sensors;
- Data caching;
- Error processing.
Of course, paragraphs 2-5 should be implemented taking into account the first paragraph.
For a long time, Android developers independently solved all these problems, but gradually accumulated experience led to the appearance of Clean Architecture . Clean Architecture approaches are very good, except that it is often inappropriate to split the logic into UI and business (as one of the layers may contain almost no code), so many developers use an architecture that can be represented by the following scheme (by slightly rearranging the blocks from Clean Architecture):

The only thing that is not taken into account is the life cycle problem, which everyone solves either
independently or with the help of any libraries. Now let's see what Google has offered us.
Architecture from Google
Just look at the diagram from Google:

Perhaps it seemed to me, but in my opinion, the previous diagram was simply rotated vertically. And this I do not want to say that Google has appropriated something old, on the contrary, it shows that Google is eyeing the opinion of developers and takes into account all their achievements, which cannot but rejoice.
Let's look at an example of the implementation of a single screen with a single query in the context of the proposed architecture, and at the same time consider the key classes.
One of the big problems in Android is the need to constantly subscribe / unsubscribe from some objects when calling life cycle methods. And since lifecycle methods are only in Activity / Fragment, all such objects (for example, GoogleApiClient, LocationManager, SensorManager and others) should be located only in Activity / Fragment, which leads to a large number of lines of code in these files.
To solve this and other problems, Google suggested using the LiveData class - a class that is closely related to the life cycle and that implements the Observer pattern. By and large, this is a very simple class that encapsulates work with one object, which can be changed and which can be monitored. And again, this is a fairly standard approach.
Consider how we can use LiveData, for example, in order to monitor a user's location. Create the following class:
public class LocationLiveData extends LiveData {
private LocationManager locationManager;
private LocationListener listener = new LocationListener();
private LocationLiveData(@NonNull Context context) {
locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
}
@Override
protected void onActive() {
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, listener);
}
@Override
protected void onInactive() {
locationManager.removeUpdates(listener);
}
}
And now we can write the following code in Activity:
public class LocationActivity extends LifecycleActivity {
private LocationLiveData locationLiveData;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_movies);
//...
locationLiveData = new LocationLiveData(this);
locationLiveData.observe(this, location -> {
// update your UI
});
}
//...
}
All necessary LiveData lifecycle methods will be called using the first parameter in the observe method - LifecycleOwner. This allows us to know when to subscribe and unsubscribe from various objects (for example, using the onActive and onInactive methods).
LifecycleActivity is a class from the library that implements the LifecycleOwner interface. Unfortunately, it is for some reason inherited from FragmentActivity, and not from AppCompatActivity, and if you still want to inherit from AppCompatActivity, you will have to write some code yourself:
public class BaseLifecycleActivity extends AppCompatActivity implements LifecycleRegistryOwner {
@NonNull
private final LifecycleRegistry lifecycleRegistry = new LifecycleRegistry(this);
@MainThread
@NonNull
@Override
public LifecycleRegistry getLifecycle() {
return lifecycleRegistry;
}
}
In addition to the onActive and onInactive methods in LiveData, you can receive callbacks to any life cycle method.
Exactly the same model can be used to implement server requests. That is, we do not in any way change the method of obtaining data (everything is also through the Repository), but we delegate the data to LiveData, which is essentially a binding for View.
And now the main question arises - how to deal with the problem of the life cycle? Of course, Google thought about it and gave the developers a component that is undergoing the recreation of Activity - ViewModel .
A few words about ViewModel:
- All instances of the ViewModel are stored in retain Fragment (HolderFragment). This is a very standard solution, which is also used by many developers.
- The ViewModel class must store all instances of LiveData so that they survive the recreation of the Activity.
- The ViewModel communicates with the View through LiveData, that is, the ViewModel does not explicitly control the View, and this can be considered some variation of the MVVM pattern.
Let’s look at how we can implement a server request for any screen. Create a ViewModel that will control the logic of the screen:
public class MoviesViewModel extends ViewModel {
@NonNull
private final MoviesRepository moviesRepository;
@Nullable
private MutableLiveData> moviesLiveData;
public MoviesViewModel(@NonNull MoviesRepository moviesRepository) {
this.moviesRepository = moviesRepository;
}
@MainThread
@NonNull
LiveData> getMoviesList() {
if (moviesLiveData == null) {
moviesLiveData = new MutableLiveData<>();
moviesRepository.popularMovies()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(moviesLiveData::setValue);
}
return moviesLiveData;
}
}
Then View will be able to use this ViewModel as follows:
viewModel.getMoviesList().observe(this, movies -> {
if (movies != null) {
adapter.setNewValues(movies);
}
});
Since the ViewModel is undergoing a recreation of the Activity, LiveData will be created only once and the server request will be executed only once, that is, these problems are resolved. The question remains of creating a ViewModel.
To access the ViewModel, the ViewModelProviders class is used:
MoviesViewModel viewModel = ViewModelProviders.of(this).get(MoviesViewModel.class);However, there is a slight problem. When we use not the default constructor (and in the example above we passed the Repository object to the constructor), we will have to write our own factory to create the ViewModel:
class MoviesViewModelProviderFactory extends BaseViewModelFactory {
@NonNull
private final MoviesRepository repository;
MoviesViewModelProviderFactory(@NonNull MoviesService moviesService) {
this.repository = new MoviesRepository(moviesService);
}
@NonNull
@Override
public T create(@NonNull Class modelClass) {
if (modelClass.equals(MoviesViewModel.class)) {
//noinspection unchecked
return (T) new MoviesViewModel(repository);
}
return super.create(modelClass);
}
}
And now we can create the ViewModel as follows:
ViewModelProvider.Factory factory = new MoviesViewModelProviderFactory(service);
return ViewModelProviders.of(this, factory).get(MoviesViewModel.class);In this example, we manually transfer to the Factory all the necessary objects for creating the ViewModel, however, the only normal approach for creating the ViewModel with an increase in the number of parameters is Dagger. Here, Google actually forces you to use it. For better or worse, you decide.
And that’s all - we got the data, displayed it, and all this with life cycle processing. However, there are a few more points that we will consider later.
Showing progress and error handling
When I just started to deal with new architectural components, I immediately had a question about how to handle errors with LiveData or get a change in progress. After all, all that we have is an instance of one class, which we must pass to Activity / Fragment. A similar question arises with the display of progress. Such a problem is a bit like a problem with loaders, however there are two options here how to solve it.
First, we can create some class whose object will encapsulate the status of the request, the error message and the data itself, and pass this object to Activity / Fragment. This method is recommended in the documentation. Such a class may look, for example, as follows (for now, we will analyze only error handling):
public class Response {
@Nullable
private final T data;
@Nullable
private final Throwable error;
private Response(@Nullable T data, @Nullable Throwable error) {
this.data = data;
this.error = error;
}
@NonNull
public static Response success(@NonNull T data) {
return new Response(data, null);
}
@NonNull
public static Response error(@NonNull Throwable error) {
return new Response(null, error);
}
@Nullable
public T getData() {
return data;
}
@Nullable
public Throwable getError() {
return error;
}
}
Then, with each change, we pass to the View an object of this class:
public class MoviesViewModel extends ViewModel {
@Nullable
private MutableLiveData>> moviesLiveData;
//...
@MainThread
@NonNull
LiveData>> getMoviesList() {
if (moviesLiveData == null) {
moviesLiveData = new MutableLiveData<>();
moviesRepository.popularMovies()
//...
.subscribe(
movies -> moviesLiveData.setValue(Response.success(movies)),
throwable -> moviesLiveData.setValue(Response.error(throwable))
);
}
return moviesLiveData;
}
}
And View handles it accordingly:
viewModel.getMoviesList().observe(this, moviesResponse -> {
if (moviesResponse != null && moviesResponse.getData() != null) {
adapter.setNewValues(moviesResponse.getData());
} else if (moviesResponse != null && moviesResponse.getError() != null) {
// show error
}
});
And secondly, there is a way that in my opinion is more concise. After all, request status and errors are also some changing data. Then why not put them in LiveData and subscribe to their changes? For these purposes, we can write the following code (and in this situation we will already consider the processing of the download status):
@NonNull
private final MutableLiveData loadingLiveData = new MutableLiveData<>();
@NonNull
LiveData isLoading() {
return loadingLiveData;
}
@MainThread
@NonNull
LiveData>> getMoviesList() {
if (moviesLiveData == null) {
moviesLiveData = new MutableLiveData<>();
moviesRepository.popularMovies()
//...
.doOnSubscribe(disposable -> loadingLiveData.setValue(true))
.doAfterTerminate(() -> loadingLiveData.setValue(false))
.subscribe();
}
return moviesLiveData;
}
And now View will subscribe not to one LiveData, but to several. This can be very convenient if we write a general handler for displaying progress or errors):
viewModel.isLoading().observe(this, isLoading -> {
if (isLoading != null && isLoading) {
progressDialog.show(getSupportFragmentManager());
} else {
progressDialog.cancel();
}
});
As a result, we can say that error handling using LiveData is almost as good as using the usual RxJava.
Testing
As mentioned above, a good architecture should allow you to test the application. Since the architecture from Google does not carry fundamental changes, testing can only be considered at the ViewModel level (since we know how to test Repository / UI).
Testing ViewModel is always a little more complicated than testing Presenter from the MVP pattern, since in MVP the explicit connection between View and Presenter makes it easy to verify that the required methods have been called on View. In the case of MVVM, we have to check the operation of binders or LiveData.
Let's create a test class in which in the setUp method we create the ViewModel:
@RunWith(JUnit4.class)
public class MoviesViewModelTest {
private MoviesViewModel viewModel;
private MoviesRepository repository;
@Before
public void setUp() throws Exception {
repository = Mockito.mock(MoviesRepository.class);
Mockito.when(repository.popularMovies()).thenReturn(Observable.just(new ArrayList<>()));
viewModel = new MoviesViewModel(repository);
}
}
To test the correctness of the ViewModel, we need to make sure that it fulfills the request to the Repository and passes the received data to LiveData:
@Test
public void testLoadingMovies() throws Exception {
Observer observer = Mockito.mock(Observer.class);
viewModel.getMoviesList().observeForever(observer);
Mockito.verify(repository).popularMovies();
Mockito.verify(observer).onChanged(any(Response.class));
}
Here we omit unnecessary details, but if necessary, you can use the capabilities of Mockito to make sure that exactly the data you need is in LiveData.
However, such a test will not work, since the setValue method in the LiveData class checks that it is called in the main thread of the application (of course, using Looper):
@MainThread
protected void setValue(T value) {
assertMainThread("setValue");
mVersion++;
mData = value;
dispatchingValue(null);
}
As we well know, tests on JUnit do not like this and fall. But Google developers have provided for this as well. To do this, you need to connect an additional library for tests and add a rule to the test:
testCompile ("android.arch.core:core-testing:$architectureVersion", {
exclude group: 'com.android.support', module: 'support-compat'
exclude group: 'com.android.support', module: 'support-annotations'
exclude group: 'com.android.support', module: 'support-core-utils'
})
@Rule
public InstantTaskExecutorRule instantExecutorRule = new InstantTaskExecutorRule();
Similarly, we can test the progress display during data loading:
@Test
public void testProgressUpdated() throws Exception {
Observer observer = Mockito.mock(Observer.class);
viewModel.isLoading().observeForever(observer);
viewModel.getMoviesList();
Mockito.verify(observer).onChanged(true);
Mockito.verify(observer).onChanged(false);
}
Thus, we can write tests for all the necessary components without any problems.
Data caching
Strictly speaking, data caching does not change in any way with such a change in architecture, because we all also use a repository, which hides all the details of receiving and saving data. However, data caching is worth mentioning in connection with the new library for working with the database - Room.
Everyone knows perfectly well about the existing huge zoo of database libraries in Android. These are Realm , greenDao , ObjectBox , DBFlow , SQLBrite and many others. Therefore, many developers do not know what to choose for their project, and here Room is an ideal option, at least because it is from Google.
Room is a fairly simple library that runs on top of SQLite, uses annotations to generate boilerplate code, and has a fairly familiar and convenient API. However, I do not think that I can tell about Room something more interesting than it was in the session on Google I / O and what is in the documentation . Therefore, here we finish the consideration of architectural innovations.
Conclusion
In general, Google is doing the right thing, and their architecture is really quite thoughtful and convenient. So you can start using it now, at least it’s worth a try in new projects. A small example, on which the article was written and which may help you try the new architecture from Google, is available on Github . Also, of course, it's worth looking at examples from Google.