1 of 45

Android Architecture Components in Action

@derohimat

@esafirm

2 of 45

BRIEF HISTORY

* Versi saya.

3 of 45

What is Android Architecture Components (AAC) ?

A new collection of libraries that help you design robust, testable, and maintainable apps for managing your UI components lifecycle and handling data persistence . — developer.android.com

4 of 45

Room

5 of 45

The Problem

  • You have to write long repetitive code, which will be time-consuming as well as prone to mistakes.
  • It is very difficult to manage SQL queries for a complex relational database.
  • As your schema changes you need to update the affected SQL queries manually. This process can be time consuming and error prone.
  • You need to use lots of boilerplate code to convert between SQL queries and Java data objects.

6 of 45

What is Room?

The Room persistence library provides an abstraction layer over SQLite to allow for more robust database access while harnessing the full power of SQLite.

Room is a part of Architecture Components, which means it works really well with ViewModel, LiveData and Paging (but does not depend on those modules!)

Introduced in Google I/O 2017.

7 of 45

Major Component of Room

Room architecture diagram

8 of 45

@Entity

Entity: Represents a table within the database.

9 of 45

Sample for @Entity

@Entitypublic class User {@PrimaryKeyprivate int uid;@ColumnInfo(name = "first_name")private String firstName;@ColumnInfo(name = "last_name")private String lastName;// Getters and setters are ignored for brevity,// but they're required for Room to work.}

User.java

10 of 45

@Dao

DAO (Data Access Object) : Contains the methods used for accessing the database.

11 of 45

Sample for @Dao

@Daopublic interface UserDao {@Query("SELECT * FROM user")List<User> getAll();@Query("SELECT * FROM user WHERE uid IN (:userIds)")List<User> loadAllByIds(int[] userIds);@Query("SELECT * FROM user WHERE first_name LIKE :first AND last_name LIKE :last LIMIT 1")User findByName(String first, String last);@Insertvoid insertAll(User... users);@Deletevoid delete(User user);}

UserDao.java

12 of 45

@Database

Database: Contains the database holder and serves as the main access point for the underlying connection to your app's persisted, relational data.

The class that's annotated with @Database should satisfy the following conditions:

  • Be an abstract class that extends RoomDatabase.
  • Include the list of entities associated with the database within the annotation.
  • Contain an abstract method that has 0 arguments and returns the class that is annotated with @Dao.

At runtime, you can acquire an instance of Database by calling Room.databaseBuilder() or Room.inMemoryDatabaseBuilder().

13 of 45

Sample for @Database

@Database(entities = {User.class}, version = 1)public abstract class AppDatabase extends RoomDatabase {public abstract UserDao userDao();}

AppDatabase.java

After creating the files above, you get an instance of the created database using the following code:

AppDatabase db = Room.databaseBuilder(getApplicationContext(),AppDatabase.class, "database-name").build();

14 of 45

Another Annotations in Room

15 of 45

Migration SQLite to Room

  1. Update the gradle dependencies
  2. Update model classes to entities
  3. Create Data Access Objects (DAOs)
  4. Create the database
  5. Update the Repository to use Room
  6. On-device testing
  7. Cleanup

16 of 45

ViewModel

17 of 45

The Problem

18 of 45

The Problem

19 of 45

What is ViewModel?

A class that designed to hold and manage UI-related data in a life-cycle conscious way

20 of 45

Simple Implementation

class ScoreViewModel : ViewModel() {var teamScore: Int = 0fun add(){� teamScore += 1}}

21 of 45

Simple Implementation

class ScoreActivity : AppCompatActivity() {val viewModel get() =ViewModelProviders.of(this).get(ScoreViewModel::class.java)�� fun bindView() {textViewScore.setText(viewModel.teamScore)� btnAdd.setOnClickListener {� viewModel.add()textViewScore.setText(viewModel.teamScore)}}}

22 of 45

ViewModel initialization

// this could be a FragmentActivity or Fragment

val viewModel get() =ViewModelProviders.of(this).get(ScoreViewModel::class.java)

  • It will return the same instance for every instance UI controller
  • All magic is handled by ViewModelProviders and it friends

23 of 45

Custom constructor

class ScoreVmFactory constructor(val team: String): ViewModelProvider.Factory {�� override fun <T : ViewModel> create(modelClass: Class<T>): T {return if (modelClass.isAssignableFrom(ScoreViewModel::class.java!!)) {ScoreViewModel(team) as T� } else {throw IllegalArgumentException("ViewModel Not Found")}}��}

24 of 45

Share data between Fragments

class ScoreViewModel : ViewModel() {var teamScore: Int = 0}��class FragmentA : Fragment() {val teamScore by lazy {ViewModelProviders.of(requreActivity()).get(ScoreViewModel::class.java)}}��class FragmentB : Fragment() {val teamScore by lazy {ViewModelProviders.of(requreActivity()).get(ScoreViewModel::class.java)}}

25 of 45

ViewModel and Context

  • Don’t put any Context except Application Context to ViewModel!
  • AndroidViewModel to the rescue ��

26 of 45

ViewModel vs onSaveInstanceState()

  • ViewModel is not created to replace onSaveInstanceState()
  • ViewModel only survive configuration change
  • onSaveInstanceState() survive through activity being killed by a system
  • onSaveInstanceState() can only save small amount of data

27 of 45

DataBinding support!

<CheckBoxandroid:id="@+id/rememberMeCheckBox"android:checked="@{viewmodel.rememberMe}"android:onCheckedChanged="@{() -> viewmodel.rememberMeChanged()}" />

val userModel = ViewModelProviders.of(this).get(UserModel.class)val binding: UserBinding = DataBindingUtil.setContentView(this, R.layout.user)// Assign the component to a property in the binding class.�binding.viewmodel = userModel

28 of 45

ViewModel highlight

  • Scoped to the UI controller
  • Survive activity configuration change
  • Only killed when the UI controller is killed
  • Separate UI implementation with the data/state
  • Can be a solution for sharing data between Fragments
  • Not designed to replace onSaveInstanceState()

29 of 45

30 of 45

LiveData

31 of 45

The Problems

  • Crashes due stopped activities
  • Manual lifecycle handling
  • Memory leaks

32 of 45

What is LiveData?

LiveData is an lifecycle aware observable data holder class.

33 of 45

Observable?

34 of 45

Simple Implementation

class ScoreViewModel : ViewMode() {val currentName by lazy { MutableLiveData<String>().also { it.setValue("100") } }}��class ScoreActivity : AppComponentActivity() {fun onCreate(savedInstanceState: Bundle) {// ViewModel is created somewhere� viewModel.currentName.observe(this, Observer {// do something with currentName})}}

35 of 45

Updating LiveData

val score = MutableLiveData<String>()��// Do this if sure you're on UI thread�viewModel.score.setValue("100")��// Otherwise just use this�viewModel.score.postValue("100")

36 of 45

Extending LiveData

class StockLiveData(symbol: String) : LiveData<BigDecimal>() {private val stockManager: StockManager = StockManager(symbol)�� private val listener = object : SimplePriceListener() {fun onPriceChanged(price: BigDecimal) {� setValue(price)}}�� override fun onActive() {� stockManager.requestPriceUpdates(listener)}�� override fun onInactive() {� stockManager.removeUpdates(listener)}}

37 of 45

LiveData’s life

  • onStarted() start in STARTED or RESUMED state
  • onAcitve() is also called in this state
  • Remove the observer in DESTROYED state
  • onInactive() called when there’s no observers left

38 of 45

Transformations

val userNames: LiveData<String> = Transformations.map(_, user -> {user.name});

39 of 45

MediatorLiveData

val updateCountLiveData = MediatorLiveData<Int>()��updateCountLiveData.addSource(score) { _ ->� updateCountLiveData.value?.plus(1)}��updateCountLiveData.addSource(forecast) { _ ->� updateCountLiveData.value?.plus(1)}

40 of 45

Room Support

@Query("SELECT * FROM weather WHERE date = :date")LiveData<WeatherEntry> getWeatherByDate(Date date);

41 of 45

DataBinding Support

<CheckBox� android:id="@+id/rememberMeCheckBox"� android:checked="@{viewmodel.rememberMe}"� android:onCheckedChanged="@{() -> viewmodel.rememberMeChanged()}" />

42 of 45

LiveData highlight

  • Reactive
  • No need to worry about unwanted update
  • Usually placed inside ViewModel
  • Can change the stream data with Transformation
  • Can combine the stream data with MediatorLiveData
  • Lots of support from another library!

43 of 45

44 of 45

References

45 of 45

THANK YOU