This post is published at Android Weekly.
This post doesn’t teach how to use Rx or Dagger 2. Please read some tutorials about Rx and Dagger 2 before reading this post.
Low coupling high cohesion is a desired feature in software development. In a low coupling software, components are less dependent to other components. So that changes in one component doesn’t effect other components. In this post, i will try to explain how to make an android app loosely coupled. Firstly, i will show an example project which is tightly coupled. Secondly, i will add Rx programming to the project. Lastly, I will add Dagger 2. We will see how the project becomes loosely coupled step by step. The example project is an android app that discovers services in your network and lists them. Whenever a service is found, it is added to the list and when a service is lost, it is removed from the list.
In Android, you can discover services in your network with NsdManager. I am going to use example described here: https://developer.android.com/training/connect-devices-wirelessly/nsd.html
Lets start with the first project.
MyDiscoveryListener class is implementing NsdManager.DiscoveryListener and listening network changes. It takes MainActivity as a parameter because whenever a service is found, it is added to the RecyclerView adapter and it is done by the MainActivity’s addAppliance method.
This is the main Activity. It has a RecyclerView to list found services. startDiscovery is the Button OnClick method which starts the discovery service. stopDiscovery is the Button OnClick method that stops the discovery. As you see, discoveryListener is holding MainActivity as a property and MainActivity is holding discoveryListener as a property, there is a circular dependency.
This is the Adapter class of RecyclerView. It implements addAppliance and removeAppliance methods for updating the list. As you noticed, notify methods are called in runOnUiThread runnable. This is because discovery services run in background thread. In order to update the UI element, we need this Runnable.
Part 2 – Adding Rx
In this part, we are going to include Rx into our project and refactor it with Rx. We will see how Rx changes and contributes to our coding structure.
The class above wraps the NsdManager.DiscoveryListener with a Rx Observable. In Part 1, MyDiscoveryListener was used and an instance of the Activity class is passed as parameter. However, in the new implementation we dont see this. So Activity is decoupled from DiscoveryListener. In the new implementation, Service Found and Service Lost events are published with the Appliance data. Subscribers handle these published events, in our case, Activity is handling the events.
The above class is the new Activity class. When Start Discovery button is clicked, startDiscovery method is called. We create a subscription in startDiscovery method. In this subscription, we also define that we want to observe the event on AndroidSchedulers.mainThread() thread. This is the UI thread of Android. Furthermore, we implement call method of rx.functions.Action1 which listens discovery changes. Depending on the service found or lost, we add or remove Appliance to the list. This call method is called on UI thread of Android because we stated that we want to observe result on AndroidSchedulers.mainThread() thread. When stop discovery button is clicked or Activity is paused, we unsubscribe from the discovery change event, because we no more need discovery.
The above class is the new Adapter class. This class is same with Part 1 Adapter class except one thing. Notify methods are not called with runOnUiThread. This is another good part of Rx. We stated that we want to observe the event result on Android Ui Thread in Activity class when subscribing to the Observable. So we dont need to use runOnUiThread, it is automatically called on UI Thread.
This completes Part 2.
Part 3 – Adding Dagger 2
In this part, we are going to include Dagger 2 into our project and refactor it with Dagger 2. We will see how Dagger 2 changes and contributes to our coding structure. In this part, we create a second activity. And we inject RxDiscoveryManager to both activities using Dagger 2. Firstly, we create a Dagger Component and Module as below for injecting RxDiscoveryManager.
DiscoveryModule class is the place where we create the instance that we want to inject. So we provide RxDiscoveryManager instance in this class with providesDiscoveryManager method.
In DiscoveryComponent class, we define the classes that we want to inject RxDiscoveryManager. These are the two Activity classes. Module and Component classes are Dagger 2 related classes, so you should get familiar with Dagger 2 before reading this post.
We changed the MainActivity to use Dagger 2. As you see in the code above, we used @Inject annotation to inject RxDiscoveryManager. Secondly, we added ((App) getApplication()).getComponent().inject(this); to the onCreate method of the Activity. By this way, we can use RxDiscoveryManager. We added a new button that starts the second Activity, which lists the discoveries same as MainActivity. Here is the second Activity:
As you see in the code above, we injected RxDiscoveryManager to this activity,too. So you can inject classes to other classes using Dagger 2. You can increase the code reusability with Dagger 2. And it also helps in decoupling dependencies and writing clean code.
This completes the post. I am new to Rx and Dagger. I see that many developers use Rx and Dagger. I wanted to see how Rx and Dagger can help in development and created 3 projects as in this post. So it helped me to understand their usage a bit. If you have any comments or you see some wrong usage, please comment below this post and so that i can learn. If you need full project codes, i can share them, too.