Kotlin + MVP + Dagger 2 + Retrofit = Sample Android Application

Ogulcan Orhan
MindOrks
Published in
7 min readMar 18, 2018

--

I guess, it’s a classic post-title for these days :).

In this article, I would like to show how to implement MVP (Model-View-Presenter) pattern with using Dagger2 and also by Kotlin.

Design Patterns are very remarkable topic for mobile development. And as developers (not just android or mobile developers), we are trying to write cleaner and more testable codes and applications.

For the Android, with MVP, we are able to take most of logic out from the activities (or fragments). So that we can test it without using instrumentation tests. And with Dagger2, it is easier to apply concept of dependency injection.

So, this article/application will cover two design patterns: MVP and Dependency Injection. You may heard typicode, was used as server. It will be abstracted by using Retrofit and RxJava2 will be used to make requests it as observables which is another hot topic for mobile development.

Note: This article is not for starters. It does not answer questions like: What’s dagger or dagger in the past, dagger basics or Retrofit 101 etc.

TL;DR

If you are just interested in project, not the story, here is github repository:

Let’s start with common discussions.

What is MVP ? Why do we need?

In Android we have a problem arising from the fact that activities are closely coupled to interface and data access mechanisms.

The Model-View-Presenter pattern allows to separate the presentation layer from the logic, so that everything about how the interface works is separated from how we represent it on screen.

MVP lets us to make views independent from data source and it will be easier to test each layer.

Libraries

Build.gradle file

As you may see from the title; I have used dagger2, retrofit, rxjava2 as libraries. I will not go into details about what’s dagger2 or retrofit.

Project Structure

Here is I have implemented MVP in Android. Let's start with how project structure looks like:

Project structure

This project structured into 5 packages:

  • api: Where Retrofit resides in.
  • di: Where dagger2 resides in aka dependency injection.
  • models: Data models.
  • ui: Activities and also with presenter and contract.
  • util: Some tweaks.

After the structure of project, I guess it would be more accurate to tell from the outside to the inside. Means: server → api →dependency injection (app) → ui (mvp).

Retrofit and Kotlin Data Classes

Typicode offers a simple server to us: Posts, users, albums. Depending on typicode, here is how retrofit interface should like:

Unlike Java, Kotlin has a great feature. Companion objects:

In Kotlin, an interface can have a companion object but it is not part of the contract that must be implemented by classes that implement the interface. It is just an object associated to the interface that has one singleton instance. So it is a place you can store things, but doesn't mean anything to the implementation class.

Another great feature is data classes. It’s really easy to create pojos in Kotlin:

data class Album(val id: Int, val userId: Int, val title: String)data class User(val id: Int, val name: String, val username: String)data class Post(val id: Int, val userId: Int, val title: String, val body: String)

With these three lines (remember, each will be placed in its own separate file) compiler automatically derives the following members from all properties declared in the primary constructor.

Dependency Injection: Dagger2

So I have used an interface to declare api requests and data classes. Next step should be Dagger2 to implement dependency injection.

Dagger uses Components and Modules to define which dependencies will be injected in which objects.

This application will contain an activity (named as MainActivity) and several fragments. So we will need three components: ApplicationComponent, ActivityComponent (where presenter resides in) and FragmentComponent (where presenter and api service reside in).

Here is ApplicationComponent.kt:

@Component(modules = arrayOf(ApplicationModule::class))
interface ApplicationComponent {

fun inject(application: BaseApp)

}

And ApplicationModule.kt:

@Module
class ApplicationModule(private val baseApp: BaseApp) {

@Provides
@Singleton
@PerApplication
fun provideApplication(): Application {
return baseApp
}
}

It’s quite simple. It just injects application and provides it when needed. Let’s assume, if we want to use Calligraphy, Crashliytcs, Timber or Stetho, application module should inject those, too.

Activity/Fragment Component and Module are also similar to Application. Additionally, provides presenter and api service:

@Module
class ActivityModule(private var activity: Activity) {

@Provides
fun provideActivity(): Activity {
return activity
}

@Provides
fun providePresenter(): MainContract.Presenter {
return MainPresenter()
}

}

FragmentModule.kt:

@Module
class FragmentModule {

@Provides
fun provideAboutPresenter(): AboutContract.Presenter {
return AboutPresenter()
}

@Provides
fun provideListPresenter(): ListContract.Presenter {
return ListPresenter()
}

@Provides
fun provideApiService(): ApiServiceInterface {
return ApiServiceInterface.create()
}
}

Here is I implemented Dagger on Application scope:

class BaseApp: Application() {

lateinit var component: ApplicationComponent

override fun onCreate() {
super.onCreate()

instance = this
setup()

if (BuildConfig.DEBUG) {
// Maybe TimberPlant etc.
}
}

fun setup() {
component = DaggerApplicationComponent.builder()
.applicationModule(ApplicationModule(this)).build()
component.inject(this)
}

fun getApplicationComponent(): ApplicationComponent {
return component
}

companion object {
lateinit var instance: BaseApp private set
}
}

MVP Implementation

As mentioned above, MVP separates views from the logic. So as an initial step there is a BaseContract to abstract Presenter and View:

class BaseContract {

interface Presenter<in T> {
fun subscribe()
fun unsubscribe()
fun attach(view: T)
}

interface View {

}
}

Since there are three layouts as Main and List and About, there are three contracts: MainContract, ListContact and AboutContract:

What is Contract?

Presenter and its corresponding View is defined in a Contract interface class, making the code more readable and the connection between the two easier to understand.

class MainContract {

interface View: BaseContract.View {
fun showAboutFragment()
fun showListFragment()
}

interface Presenter: BaseContract.Presenter<MainContract.View> {
fun onDrawerOptionAboutClick()
}
}

Main screen is an activity that controls fragment layout to show list or about. This is why view has just two functions.

List screen contains a list, as the name implies, that fetches data from server. During the request there will be a progress, if request fails there will be an error message, if request is okay data will be shown. And finally there will be a detail view for each item on the list.

Here is how ListContract looks like:

class ListContract {

interface View: BaseContract.View {
fun showProgress(show: Boolean)
fun showErrorMessage(error: String)
fun loadDataSuccess(list: List<Post>)
fun loadDataAllSuccess(model: DetailsViewModel)
}

interface Presenter: BaseContract.Presenter<View> {
fun loadData()
fun loadDataAll()
fun deleteItem(item: Post)
}
}

About view has just a text view that shows example message. Even it’s from xml or remote we should declare functionalities as well:

class AboutContract {

interface View: BaseContract.View {
fun showProgress(show: Boolean)
fun loadMessageSuccess(message: String)
// fun loadMessageError() // if it's a real request, this fun should be implemented, too
}

interface Presenter: BaseContract.Presenter<View> {
fun loadMessage() // assume that a retrofit request
}
}

What is Presenter?

In MVP, Presenter retrieves the data from the Model, applies the UI logic and manages the state of the View, decides what to display and reacts to user input notifications from the View.

I will just show how List Presenter looks like:

For the last step, here is how to inject ActivityComponent on MainActivity:

package com.ogulcan.android.mvp.app.ui.main

import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.view.Menu
import android.view.MenuItem
import com.ogulcan.android.mvp.app.R
import com.ogulcan.android.mvp.app.di.component.DaggerActivityComponent
import com.ogulcan.android.mvp.app.di.module.ActivityModule
import com.ogulcan.android.mvp.app.ui.about.AboutFragment
import com.ogulcan.android.mvp.app.ui.list.ListFragment
import javax.inject.Inject

/**
* Created by ogulcan on 07/02/2018.
*/
class MainActivity: AppCompatActivity(), MainContract.View {

@Inject lateinit var presenter: MainContract.Presenter

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
injectDependency()

presenter.attach(this)
}

override fun onResume() {
super.onResume()
test()
}

override fun showAboutFragment() {
if (supportFragmentManager.findFragmentByTag(AboutFragment.TAG) == null) {
supportFragmentManager.beginTransaction()
.addToBackStack(null)
.setCustomAnimations(AnimType.FADE.getAnimPair().first, AnimType.FADE.getAnimPair().second)
.replace(R.id.frame, AboutFragment().newInstance(), AboutFragment.TAG)
.commit()
} else {
// Maybe an animation like shake hello text
}
}

override fun showListFragment() {
supportFragmentManager.beginTransaction()
.disallowAddToBackStack()
.setCustomAnimations(AnimType.SLIDE.getAnimPair().first, AnimType.SLIDE.getAnimPair().second)
.replace(R.id.frame, ListFragment().newInstance(), ListFragment.TAG)
.commit()
}

override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.menu, menu)
return true
}

override fun onOptionsItemSelected(item: MenuItem?): Boolean {
when(item!!.itemId) {
R.id.nav_item_info -> {
presenter.onDrawerOptionAboutClick()
return true
}
else -> {

}
}

return super.onOptionsItemSelected(item)
}

override fun onBackPressed() {
val fragmentManager = supportFragmentManager
val fragment = fragmentManager.findFragmentByTag(AboutFragment.TAG)

if (fragment == null) {
super.onBackPressed()
} else {
supportFragmentManager.popBackStack()
}
}

private fun injectDependency() {
val activityComponent = DaggerActivityComponent.builder()
.activityModule(ActivityModule(this))
.build()

activityComponent.inject(this)
}

private fun test() {
//hello.setText("Hello world with kotlin extensions")
}

enum class AnimType() {
SLIDE,
FADE;

fun getAnimPair(): Pair<Int, Int> {
when(this) {
SLIDE -> return Pair(R.anim.slide_left, R.anim.slide_right)
FADE -> return Pair(R.anim.fade_in, R.anim.fade_out)
}

return Pair(R.anim.slide_left, R.anim.slide_right)
}
}
}

What happens step by step once application start?

  • On BaseApp: Application Component will be initialized with using Dagger2. API service will be ready.
  • On MainActivity: Main Activity is default activity for the application. It contains a frame layout to represent list or about fragments. After dagger injection, view will be attached to presenter.
  • On ListFragment: After attachment, list fragment will be shown as default. It means that ListFragment will be shown. View of list fragment has also its presenter. So, posts list will be fetched from remote server.
  • On AboutFragment: If user taps option, about fragment will be shown with animation.

Thank you so much for coming so far!

If you want to know more about this sample application please see github repository.

If you liked this article, please share. So people can also read it.
Please get in touch with me via Github, Twitter or LinkedIn.

--

--