For the More Curious: Managing Dependencies

FlickrFetchr provides a layer of abstraction over the source of Flickr photo metadata. Other components (such as PhotoGalleryFragment) use this abstraction to fetch Flickr data without worrying about where the data is coming from.

FlickrFetchr itself does not know how to download JSON data from Flickr. Instead, FlickrFetchr relies on FlickrApi to know the endpoint URL, to connect to that endpoint, and to perform the actual work of downloading the JSON data. FlickrFetchr is said to have a dependency on FlickrApi.

You are initializing FlickrApi inside the FlickrFetchr init block:

    class FlickrFetchr {
        ...
        private val flickrApi: FlickrApi

        init {
            val retrofit: Retrofit = Retrofit.Builder()
                    .baseUrl("https://www.flickr.com/")
                    .addConverterFactory(ScalarsConverterFactory.create())
                    .build()

            flickrApi = retrofit.create(FlickrApi::class.java)
        }

        fun fetchContents(): LiveData<String> {
            ...
        }
    }

This works well for a simple application, but there are a few potential issues to consider.

First, it is difficult to unit test FlickrFetchr. Recall from Chapter 20 that the goal of a unit test is to verify the behavior of a class and its interactions with other classes. To properly unit test FlickrFetchr, you need to isolate it from the real FlickrApi. But this is a difficult – if not impossible – task, because FlickrApi is initialized inside of the FlickrFetchr init block.

Hence, there is no way to provide a mock instance of FlickrApi to FlickrFetchr for testing purposes. This is problematic, because any test you run against fetchContents() will result in a network request. The success of your tests would be dependent on network state and the availability of the Flickr back-end API at the time of running the test.

Another issue is that FlickrApi is tedious to instantiate. You must build and configure an instance of Retrofit before you can build an instance of FlickrApi. This implementation requires you to duplicate five lines of Retrofit configuration code anywhere you want to create a FlickrApi instance.

Finally, creating a new instance of FlickrApi everywhere you want to use it results in unnecessary object creation. Object creation is expensive relative to the scarce resources available on a mobile device. Whenever practical, you should share instances of a class across your app and avoid needless object allocation. FlickrApi is a perfect candidate for sharing, since there is no variable instance state.

Dependency injection (or DI) is a design pattern that addresses these issues by centralizing the logic for creating dependencies, such as FlickrApi, and supplying the dependencies to the classes that need them. By applying DI to PhotoGallery, you could easily pass an instance of FlickrApi into FlickrFetchr each time a new instance of FlickrFetchr was constructed. Using DI would allow you to:

  • encapsulate the initialization logic of FlickrApi into a common place outside of FlickrFetchr

  • use a singleton instance of FlickrApi throughout the app

  • substitute a mock version of FlickrApi when unit testing

Applying the DI pattern to FlickrFetchr might look something like this:

    class FlickrFetchr(flickrApi: FlickrApi) {
        fun fetchContents(): LiveData<String> {
            ...
        }
    }

Note that DI does not enforce the singleton pattern for all dependencies. FlickrFetchr is passed an instance of FlickrApi on construction. This mechanism for constructing FlickrFetchr gives you the flexibility to provide a new instance or a shared instance of FlickrApi based on your use case.

DI is a broad topic with many facets that extend well beyond Android. This section just scratches the surface. There are entire books dedicated to the concept of DI and many libraries to make DI easier to implement. If you want to use DI in your app, you should consider using one of these libraries. It will help guide you through the process of DI and reduce the amount of code you need to write to implement the pattern.

At the time of this writing, Dagger 2 is the official Google-recommended library for implementing DI on Android. You can find detailed documentation, code samples, and tutorials about DI with Dagger 2 at google.github.io/​dagger.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.137.192.3