1 of 42

1

Lesson 8:

App architecture (UI layer)

This work is licensed under the Apache 2 license.

Android Development with Kotlin v1.0

This work is licensed under the Apache 2 license.

2 of 42

About this lesson

Lesson 8: App architecture (UI layer)

2

Android Development with Kotlin

This work is licensed under the Apache 2 license.

3 of 42

3

Android app architecture

Android Development with Kotlin

This work is licensed under the Apache 2 license.

4 of 42

Avoid short-term hacks

  • External factors, such as tight deadlines, can lead to poor decisions about app design and structure.
  • Decisions have consequences for future work (app can be harder to maintain long-term).
  • Need to balance on-time delivery and future maintenance burden.

4

Android Development with Kotlin

This work is licensed under the Apache 2 license.

5 of 42

Examples of short-term hacks

  • Tailoring your app to a specific device
  • Blindly copying and pasting code into your files
  • Placing all business logic in activity file
  • Hardcoding user-facing strings in your code

5

Android Development with Kotlin

This work is licensed under the Apache 2 license.

6 of 42

Why you need good app architecture

  • Clearly defines where specific business logic belongs
  • Makes it easier for developers to collaborate
  • Makes your code easier to test
  • Lets you benefit from already-solved problems
  • Saves time and reduces technical debt as you extend your app

6

Android Development with Kotlin

This work is licensed under the Apache 2 license.

7 of 42

Android Jetpack

  • Android libraries that incorporate best practices and provide backward compatibility in your apps
  • Jetpack comprises the androidx.* package libraries

7

Android Development with Kotlin

This work is licensed under the Apache 2 license.

8 of 42

Separation of concerns

8

ViewModel

LiveData

Data Layer

UI Controller�(Activity/Fragment)

res/layout

Android Development with Kotlin

This work is licensed under the Apache 2 license.

9 of 42

Architecture components

  • Architecture design patterns, like MVVM and MVI, describe a loose template for what the structure of your app should be.
  • Jetpack architecture components help you design robust, testable, and maintainable apps.

9

Android Development with Kotlin

This work is licensed under the Apache 2 license.

10 of 42

10

ViewModel

Android Development with Kotlin

This work is licensed under the Apache 2 license.

11 of 42

Gradle: lifecycle extensions

In app/build.gradle file:

dependencies {

implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"

implementation "androidx.activity:activity-ktx:$activity_version"

}

11

Android Development with Kotlin

This work is licensed under the Apache 2 license.

12 of 42

ViewModel

  • Prepares data for the UI
  • Must not reference activity, fragment, or views in view hierarchy
  • Scoped to a lifecycle (which activity and fragment have)
  • Enables data to survive configuration changes
  • Survives as long as the scope is alive

12

Android Development with Kotlin

This work is licensed under the Apache 2 license.

13 of 42

Lifetime of a ViewModel

13

Android Development with Kotlin

This work is licensed under the Apache 2 license.

14 of 42

Kabaddi Kounter

14

Android Development with Kotlin

This work is licensed under the Apache 2 license.

15 of 42

ViewModel class

15

abstract class ViewModel

Android Development with Kotlin

This work is licensed under the Apache 2 license.

16 of 42

Implement a ViewModel

class ScoreViewModel : ViewModel() {

var scoreA : Int = 0

var scoreB : Int = 0

fun incrementScore(isTeamA: Boolean) {

if (isTeamA) {

scoreA++

}

else {

scoreB++

}

}

}

16

Android Development with Kotlin

This work is licensed under the Apache 2 license.

17 of 42

Load and use a ViewModel

class MainActivity : AppCompatActivity() {

// Delegate provided by androidx.activity.viewModels

val viewModel: ScoreViewModel by viewModels()

override fun onCreate(savedInstanceState: Bundle?) {

...

val scoreViewA: TextView = findViewById(R.id.scoreA)

scoreViewA.text = viewModel.scoreA.toString()

}

17

Android Development with Kotlin

This work is licensed under the Apache 2 license.

18 of 42

Using a ViewModel

val scoreViewA: TextView = findViewById(R.id.scoreA)

val plusOneButtonA: Button = findViewById(R.id.plusOne_teamA)

plusOneButtonA.setOnClickListener {

viewModel.incrementScore(true)

scoreViewA.text = viewModel.scoreA.toString()

}

18

Within MainActivity onCreate():

Android Development with Kotlin

This work is licensed under the Apache 2 license.

19 of 42

19

Data binding

Android Development with Kotlin

This work is licensed under the Apache 2 license.

20 of 42

ViewModels and data binding

  • ViewModels can work in concert with data binding
  • App architecture without data binding

20

ViewModel

Views

(defined in XML layout)

UI Controller

(activity/fragment with click listeners)

ViewModel

Views

(defined in XML layout)

Android Development with Kotlin

This work is licensed under the Apache 2 license.

21 of 42

Data binding in XML revisited

<layout>

<data>

<variable>

name="viewModel"

type="com.example.kabaddikounter.ScoreViewModel" />

</data>

<ConstraintLayout ../>

</layout>

21

Specify ViewModels in the data tag of a binding.

Android Development with Kotlin

This work is licensed under the Apache 2 license.

22 of 42

Attaching a ViewModel to a data binding

class MainActivity : AppCompatActivity() {

val viewModel: ScoreViewModel by viewModels()

override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState)

val binding: ActivityMainBinding = DataBindingUtil.setContentView(this,

R.layout.activity_main)

binding.viewModel = viewModel

...

22

Android Development with Kotlin

This work is licensed under the Apache 2 license.

23 of 42

Using a ViewModel from a data binding

In activity_main.xml:

<TextView

android:id="@+id/scoreViewA"

android:text="@{viewModel.scoreA.toString()}" />

...

23

Android Development with Kotlin

This work is licensed under the Apache 2 license.

24 of 42

ViewModels and data binding

override fun onCreate(savedInstanceState: Bundle?) {

...

val binding: ActivityMainBinding = DataBindingUtil.setContentView(this,

R.layout.activity_main)

binding.plusOneButtonA.setOnClickListener {

viewModel.incrementScore(true)

binding.scoreViewA.text = viewModel.scoreA.toString()

}

}

24

Android Development with Kotlin

This work is licensed under the Apache 2 license.

25 of 42

25

LiveData

Android Development with Kotlin

This work is licensed under the Apache 2 license.

26 of 42

Observer design pattern

  • Subject maintains list of observers to notify when state changes.
  • Observers receive state changes from subject and execute appropriate code.
  • Observers can be added or removed at any time.

26

Android Development with Kotlin

This work is licensed under the Apache 2 license.

27 of 42

Observer design pattern diagram

27

notify

notify

observe()

observe()

Observable

Observer

Observer

Android Development with Kotlin

This work is licensed under the Apache 2 license.

28 of 42

LiveData

  • A lifecycle-aware data holder that can be observed
  • Wrapper that can be used with any data including lists �(for example, LiveData<Int> holds an Int)
  • Often used by ViewModels to hold individual data fields
  • Observers (activity or fragment) can be added or removed
    • observe(owner: LifecycleOwner, observer: Observer)

removeObserver(observer: Observer)

28

Android Development with Kotlin

This work is licensed under the Apache 2 license.

29 of 42

LiveData versus MutableLiveData

29

LiveData<T>

MutableLiveData<T>

  • getValue()
  • getValue()
  • postValue(value: T)
  • setValue(value: T)

T is the type of data that’s stored in LiveData or MutableLiveData.

Android Development with Kotlin

This work is licensed under the Apache 2 license.

30 of 42

Use LiveData in ViewModel

class ScoreViewModel : ViewModel() {

private val _scoreA = MutableLiveData<Int>(0)

val scoreA: LiveData<Int>

get() = _scoreA

fun incrementScore(isTeamA: Boolean) {

if (isTeamA) {

_scoreA.value = _scoreA.value!! + 1

}

...

30

Android Development with Kotlin

This work is licensed under the Apache 2 license.

31 of 42

Add an observer on LiveData

Set up click listener to increment ViewModel score:

binding.plusOneButtonA.setOnClickListener {

viewModel.incrementScore(true)

}

31

Create observer to update team A score on screen:

val scoreA_Observer = Observer<Int> { newValue ->

binding.scoreViewA.text = newValue.toString()

}

Add the observer onto scoreA LiveData in ViewModel:

viewModel.scoreA.observe(this, scoreA_Observer)

Android Development with Kotlin

This work is licensed under the Apache 2 license.

32 of 42

Two-way data binding

  • We already have two-way binding with ViewModel and LiveData.
  • Binding to LiveData in XML eliminates need for an observer in code.

32

Android Development with Kotlin

This work is licensed under the Apache 2 license.

33 of 42

Example layout XML

<layout>

<data>

<variable>

name="viewModel"

type="com.example.kabaddikounter.ScoreViewModel" />

</data>

<ConstraintLayout ..>

<TextView ...

android:id="@+id/scoreViewA"

android:text="@{viewModel.scoreA.toString()}" />

...

</ConstraintLayout>

</layout>

33

Android Development with Kotlin

This work is licensed under the Apache 2 license.

34 of 42

Example Activity

class MainActivity : AppCompatActivity() {

val viewModel: ScoreViewModel by viewModels()

override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState)

val binding: ActivityMainBinding = DataBindingUtil� .setContentView(this, R.layout.activity_main)

binding.viewModel = viewModel

binding.lifecycleOwner = this

binding.plusOneButtonA.setOnClickListener {

viewModel.incrementScore(true)

}

...

34

Android Development with Kotlin

This work is licensed under the Apache 2 license.

35 of 42

Example ViewModel

class ScoreViewModel : ViewModel() {

private val _scoreA = MutableLiveData<Int>(0)

val scoreA : LiveData<Int>

get() = _scoreA

private val _scoreB = MutableLiveData<Int>(0)

val scoreB : LiveData<Int>

get() = _scoreB

fun incrementScore(isTeamA: Boolean) {

if (isTeamA) {

_scoreA.value = _scoreA.value!! + 1

} else {

_scoreB.value = _scoreB.value!! + 1

}

}

}

35

Android Development with Kotlin

This work is licensed under the Apache 2 license.

36 of 42

36

Transform LiveData

Android Development with Kotlin

This work is licensed under the Apache 2 license.

37 of 42

Manipulating LiveData with transformations

LiveData can be transformed into a new LiveData object.

  • map()
  • switchMap()

37

Android Development with Kotlin

This work is licensed under the Apache 2 license.

38 of 42

Example LiveData with transformations

val result: LiveData<String> = Transformations.map(viewModel.scoreA) {

x -> if (x > 10) "A Wins" else ""

}

38

Android Development with Kotlin

This work is licensed under the Apache 2 license.

39 of 42

39

Summary

Android Development with Kotlin

This work is licensed under the Apache 2 license.

40 of 42

Summary

40

Android Development with Kotlin

This work is licensed under the Apache 2 license.

41 of 42

Learn More

41

Android Development with Kotlin

This work is licensed under the Apache 2 license.

42 of 42

Pathway

Practice what you’ve learned by�completing the pathway:

Lesson 8: App architecture (UI layer)

42

Android Development with Kotlin

This work is licensed under the Apache 2 license.