Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Architecting Android Apps the Clean Way (DevFes...

Architecting Android Apps the Clean Way (DevFest Lagos 2018)

As your android applications grow in size, using the default structure android provides would lead to classes having more than one responsibility, which makes code difficult to maintain, test, and read.

To solve this problem, there is need for an architecture which will help bring "separation of concerns" to your code, making it easily maintainable, testable, and readable.

In this talk, you will learn how to architecture your android applications in a clean way by leveraging concepts of Clean Architecture, Android Architecture Components, and some Software Design Principles/Patterns.

Chizoba Ogbonna

November 03, 2018
Tweet

More Decks by Chizoba Ogbonna

Other Decks in Programming

Transcript

  1. Presentation Layer • Views - Activities and Fragments MVC |

    MVP | MVVM ViewModel LiveData 3 LiveData 3 LiveData 3 Activity/Fragment • ViewModel with LiveData(s)
  2. // MainActivity.kt override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) viewModel

    = ViewModelProviders.of(this).get(MainViewModel::class.java) makeGithubSearchQuery() } private fun makeGithubSearchQuery() { viewModel!!.users.observe(this, Observer { users -> // Update UI }) }
  3. // MainViewModel.kt class MainViewModel(val repository : GitHubRepository) : ViewModel() {

    private var users = MutableLiveData<List<UserDataLayerModel>>() get() { GetUsersUseCase(repository).invoke(this) return users } }
  4. Presentation Layer • Views - Activities and Fragments MVC |

    MVP | MVVM ViewModel LiveData 3 LiveData 3 LiveData 3 Activity/Fragment • ViewModel with LiveData(s)
  5. Proprietary + Confidential Feature’s Y ViewModel Data X’s LiveData Data

    Y’s LiveData Dashboard Shows data from feature X Shows data from feature Y Dashboard’s ViewModel Feature’s X ViewModel
  6. // MainViewModel.kt class MainViewModel : ViewModel() { private var usersLiveData:

    UsersLiveData private var viewModelResult: LiveData<List<UserUILayerModel>> val users: LiveData<List<UserUILayerModel>> get() { usersLiveData.loadData() return viewModelResult } init { usersLiveData = UsersLiveData(GitHubRepository(NetworkDataSource())) viewModelResult = Transformations.map(usersLiveData) { data -> convertDataToUIModel(data) } } }
  7. // UsersLiveData.kt (Custom Live Data) class UsersLiveData(val repository: GitHubRepository) :

    MutableLiveData<UserDataLayerModel>() { init { loadData() } fun loadData() { GetUsersUseCase(repository).invoke(this) } }
  8. • Lifecycle Aware Data Loading with Architecture Components - Ian

    Lake • LiveData beyond the ViewModel - Reactive patterns using Transformations and MediatorLiveData - Jose Alcérreca • LiveData with SnackBar, Navigation and other events (the SingleLiveEvent case) - Jose Alcérreca Learn more
  9. // UseCase class GetUsersUseCase(private val gitHubRepository: GitHubRepository) { fun execute():

    UserDataLayerModel { return gitHubRepository.lagosJavaDevs } }
  10. Data Layer • Cache • Local Database • Remote Database

    Repository Repository Cache Local DB Cloud DB
  11. class GitHubRepository(private val networkDataSource: GitHubDataSource){ lateinit var userDataLayerModel: UserDataLayerModel? =

    null fun getLagosJavaDevs(): UserDataLayerModel? { try { userDataLayerModel = RetrofitUtil.createService().javaDevsInLagos.execute().body() } catch (e: IOException) { e.printStackTrace() } return userDataLayerModel } }
  12. Data Layer • Cache • Local Database • Remote Database

    Repository Repository Cache Local DB Cloud DB
  13. class GitHubRepository(private val networkDataSource: GitHubDataSource){ fun getLagosJavaDevs(): UserDataLayerModel { return

    networkDataSource.getLagosJavaDevs("lagos", "java") } } interface GitHubDataSource { fun getLagosJavaDevs(location: String, language: String): UserDataLayerModel }
  14. class NetworkDataSource : GitHubDataSource { override fun getLagosJavaDevs(location: String, language:

    String): UserDataLayerModel? { var userDataLayerModel: UserDataLayerModel? = null try { userDataLayerModel = RetrofitUtil.createService().javaDevsInLagos.execute().body() } catch (e: IOException) { e.printStackTrace() } return userDataLayerModel } }
  15. Proprietary + Confidential The Dependency Rule Source: Lorem ipsum dolor

    sit amet, consectetur adipiscing elit. Duis non erat sem Presentation Data Domain
  16. // build.gradle // Presentation layer dependencies { implementation project(':data') implementation

    project(':domain') } // Data Layer dependencies { implementation project(':domain') } // Domain layer dependencies { }
  17. Proprietary + Confidential Presentation Data Domain Dependency Flow vs Data

    Flow Source: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis non erat sem Presentation Data Domain
  18. Proprietary + Confidential ViewModel with LiveData Activity/Fragment Use cases Entities

    Repository DataSources Presentation Layer Domain Layer Data Layer <I> <I>
  19. Proprietary + Confidential ViewModel with LiveData Activity/Fragment Use cases Entities

    Repository DataSources Presentation Layer Domain Layer Data Layer
  20. My Advice Presentation layer (MVVM, MVP, MVC) Lagos Presentation +

    Data layer (Repository) Presentation + Domain (Use cases) + Data layer
  21. • The Principles of Clean Architecture - Robert C. Martin

    • Guide to app architecture - Android Docs • Google I/O 2018 app - Architecture and Testing • Clean Architecture for Android with Kotlin - Antonio Leiva Further Reading