Android Jetpack is a collection of additional solutions which allows Android developers to optimize their work. It was released by Google to fulfil three main objectives: acceleration of programming, removal of redundant code, and improvements to product quality. Despite the unquestionable advantages, Android Jetpack brought us as many new questions as solutions. For half a year Google has been trying to convince us to use their components. But there are problems, which have prevented developers from using Jetpack. One of these is the new way of passing data between the fragments.
The passing data issue
Let’s suppose we have two simple fragments and we want to send the data between them. We can easily pass the data with Bundle from one fragment to the following one. But it’s a little more complicated when it comes to passing the data to other fragments in our app. Then we can create custom interface connections or, for example, use the
With Android Jetpack, Google provided us with a solution called Android Navigation Component to ease the navigation process. However, with transferring data between fragments, it only gives us an easier way to pass the data from one fragment to the following one. It doesn’t provide us with any new method of transferring the data back, so we have to stay with the basic solutions.
Use ViewModels and LiveData
Another way of data transmission is using ViewModel and LiveData objects and the concept of SharedViewModel class. Google provides us with a concise explanation and a simple example of how to implement them, but I don’t think it’s enough to cover all the different cases.
Unfortunately, Google doesn’t give us any snippets for making SharedViewModel class compatible with Dagger, Koin, or other dependency injection framework. Koin has its own implementation example of SharedViewModel class but its lifecycle must be connected to an activity. It has only minor functionality if we want to create an app with many fragments and one activity. In this case, SharedViewModel objects will occupy memory throughout the whole lifecycle of the activity, and thus, the whole lifecycle of the application. There is only one exception, when the user recreates all activities but doesn’t kill the app.
Data passing schema
Let’s think about how to pass data between fragments using SharedViewModel class.
It’s simply Google’s example which I have mentioned before. In this case we need to define our data processing methods like
getDataFromAPI() in one of SharedViewModel class instance. But which? Both? This difficulty encouraged me to find another solution.
I know, it seems to be messy but believe me, it’s easy to understand. As you can see, this trick includes a single SharedViewModel instance connected to a parent fragment and other fragments which we want to transfer the data too. Now every fragment has its own SharedViewModel instance and shares it with the other ones. In this case, if we need to transfer something to
BViewModel we can do it from
FragmentC for example.
You can also easy separate your methods from LiveData objects. For this purpose, you need the standard ViewModel class so your fragments won’t know about the other fragment data methods. This is what I actually chose.
In my opinion that solution provides the cleanest data sharing method. Each fragment has its own ViewModel instance (to process data operations and survive configuration changes) as well as access to each SharedViewModel instance (to share the data) which we provide to it.
Here we need to think how to correctly implement ViewModel objects in our app. I will focus on a schema which I prefer. Our goal is to have the same instances of one SharedViewModel class in two or more fragments to share the data. There are two ways to achieve this:
- Your app is made with multiple activities and fragments
- Your app is made with one activity and multiple fragments
Regardless of how your app is made and wether you use a Dagger, or not, you shouldn’t implement the
@Reusable annotations, because your ViewModel objects will save your data state while the app is active. What is more, we cannot assign a single ViewModel object for two fragment scopes (
@FragmentBScope for example) so custom scopes aren’t the solution.
Multiple activities and fragments
- If you have multiple activities and fragments, Google’s I/O app is a good example to help you understand how to implement ViewModel objects correctly. To summarize, you can easily assign your SharedViewModel objects lifecycles to current activity and share data between the fragments using ViewModel objects around the activity scope. Additionally, if you use Koin, you should read about its SharedViewModel class implementation because they use the same concept which I have described above for default settings (ViewModel objects lifecycle is connected with the lifecycle of the activity).
One activity and multiple fragments
- This case is slightly more difficult. The scope of our shared data expands along with the complexity of the app. The implementation is very similar to the multiple fragments and activity example. But in this case all of our SharedViewModel objects lifecycles should be assigned to the one activity. Hence the scope of ViewModel instances is as big as singleton’s (but not completely). Singleton objects will be active during the whole application lifecycle while our ViewModel objects exists during the activity lifecycle. In this case, the user invokes activity
onDestroy()method but doesn’t kill the app, and when they come back to the app they will get another instance of activity and all the new instances of the SharedViewModel objects.
As you can see ViewModel objects can be implemented in many different ways and you can configure it as you wish. Now it’s your turn to choose the solution which will be relevant for your application.