Clean Android Code articles series:

To better showcase approaches to architecture, let’s write some more code and make our sample an actual app.

Flickr on a map

As an example, let’s make an app that shows Google Maps, fetches information from Flickr and shows pins for pictures. I have deliberately picked maps, because it is usually tricky to test them.

But let’s start with a data layer — something that would fetch us information.

I don’t think there are strict requirements on how you want to structure your project in terms of modules. In some projects I would have data layer as a separate module — it would allow a different set of dependencies there, because it doesn’t make sense to let data layer know about android support library, or recycler view, or, basically, anything that concerns UI. For smaller projects I put data layer in a separate package, so it would also not re-compile when it’s not necessary.

Flickr API

To get started with a data layer you would need access to Flickr API. To get it, you need to register on Flickr, then go and request your API key. It’s a good idea to keep your API key safe. Some people would put the key directly into the app code and push code to github, and in most cases it’s totally fine, but in other situation, like doing a commercial project, that would be not enough.

In Building with Gradle part of the this article series we have already created buildsystem/secret.gradle file. If you don’t yet have it, then be sure to created before proceeding.

secret.gradle could be empty for you for now, if you don’t have any release keystore there. Add following entries to your file:

ext {



flickr_api_key = '<your filckr key>'

flickr_api_secret = '<your flickr secret>'

//you can even add more things here, like a path to your release keystore and some passwords }

Don’t add this file to source control and make sure to keep it safe, keep a copy in several secure places.

Now we can pass parameters from gradle into our code. To do that we can use buildConfigField, and later access it by BuildConfig.MY_VARIABLE_NAME. We can even have separate keys for stage and production flavours:

productFlavors {

stage {

applicationId globalConf.androidApplicationIdStage

buildConfigField "String", "FLICKR_API_KEY", "\"${globalConf.flickr_api_key}\""

buildConfigField "String", "FLICKR_API_SECRET", "\"${globalConf.flickr_api_secret}\""

}

production {

applicationId globalConf.androidApplicationIdProduction

buildConfigField "String", "FLICKR_API_KEY", "\"${globalConf.flickr_api_key}\""

buildConfigField "String", "FLICKR_API_SECRET", "\"${globalConf.flickr_api_secret}\""

}

}

Data Layer

A data layer is a set of classes that know how to get data. In terms of MV(C/P/VM), data layer is a Model. Actual data objects I prefer to write as Entities — these would be just simple objects with no logic on them, their job is to keep data.

Class that would be like a main hub of a particular kind of data I call Repository. Repository’s job is to retrieve data, prepare and process it when necessary and keep it for future access. Sound like a lot of things to do, so not to make it too huge we will split responsibilities between different classes, that would do network access, database access and caching for us.

Let’s make our data layer to access Flickr API with help of Retrofit and OkHttp libraries.

First thing we need to do is make our API Service and response entity, and we also need to create OkHttp Client with a FlickrAPIService attached to it.

Next step is to build a repository. In the most simple case, repository would be talking to service directly. In case some complex data processing is needed — another class (eg. Network) could take this responsibility.

FlickrRepo.kt

@Singleton

class FlickrRepo @Inject constructor(

private val flickrApi: FlickrAPIService

) {



fun searchPhotos(lat: Double, lon: Double, radius: Int) : Observable<List<Photo>> {

return flickrApi.searchPhotos(lat, lon, radius)

.map {

it.photo

}

}



}

We also write a simple test

@RunWith(PowerMockRunner::class)

@PrepareForTest()

class FlickrRepoTest {



@Mock private lateinit var flickrApiSerivce: FlickrAPIService



@InjectMocks lateinit var classToTest: FlickrRepo



val photos = listOf(

Photo(),

Photo(),

Photo()

)



val flickrResponse = FlickrResponse(

photo = photos

)



val lat = 1.0

val lon = 2.0

val radius = 10



@Before

fun setup() {

given(flickrApiSerivce.searchPhotos(

any(), any(), any(),

any(), any(), any(), any(),

any(), any(), any())

).willReturn(Observable.just(flickrResponse))

}



@Test

fun searchesForPhotos() {

// when

classToTest.searchPhotos(lat, lon, radius).subscribe()



// then

verify(flickrApiSerivce).searchPhotos(lat, lon, radius)

}



@Test

fun processesFlickrResponse() {

// given

val testSubscriber = TestSubscriber<List<Photo>>()



// when

classToTest.searchPhotos(lat, lon, radius).subscribe(testSubscriber)



// then

testSubscriber.assertValue(photos)

}





}

A few words about code coverage. Jacoco is a good tool to measure coverage, but, initially, it was made for Java. Kotlin, on the other hand, generates some additional methods and classes that all end up in the resulting class-file, and while Jacoco runs on byte-code level it grabs everything, thus showing you that coverage is not so good. We can workaround that partially by providing excludes in jacoco gradle task, but some things, like properties with generated getters and setters, will still affect the coverage. Just keep that in mind. Also, there is already a request on Jacoco github page to improve filtering options, so hopefully some time later coverage for Kotlin will be measured more accurately.

Basically, that makes our data layer for now.

In terms of extendability — we can add some caching mechanisms to save data coming from the network, add more repositories talking to other data providers, like databases, for example. Caching can be abstracted up to a point that at first you would use SharedPreferences, and then will be able to easily switch implementation to disk cache, for example.

Repository itself can also serve as a in-memory cache by keeping entity lists and instances in it’s properties and returning as needed.

And on the good side — it knows nothing about UI layers and is completely independent. You can move it out to another module and keep as a library, or even share it between different projects.

You can find relevant branch on Github

Let’s proceed to writing some UI classes that would consume data provided by our repository.

Clean Android Code articles series: