Kotlin and Android Arch Components Adit Lal @aditlal
Introduction An application with a solid architecture should be: •Easy to scale and maintain. •Each component should be isolated and decoupled. •Easy to test Kotlin and Android Arch Components
Kotlin and Android Arch Components
Components Create an UI that automatically responds to lifecycle events. Lifecycle Kotlin and Android Arch Components
Components Create an UI that automatically responds to lifecycle events. Lifecycle LiveData Build data objects that notify views when underlying data changes Kotlin and Android Arch Components
Components Create an UI that automatically responds to lifecycle events. Lifecycle LiveData ViewModel Build data objects that notify views when underlying data changes Store UI related data that isn’t destroyed on app rotation Kotlin and Android Arch Components
Components Create an UI that automatically responds to lifecycle events. Lifecycle LiveData ViewModel Room Build data objects that notify views when underlying data changes Store UI related data that isn’t destroyed on app rotation Access your data with the power of SQLite and safety of in-app objects. Kotlin and Android Arch Components
Lifecycle States and Events Kotlin and Android Arch Components
Kotlin and Android Arch Components Lifecycle
Kotlin and Android Arch Components Lifecycle
Kotlin and Android Arch Components
Kotlin and Android Arch Components /** * Displays a message when app comes to foreground and goes to background. */ class AppLifecycleObserver @Inject constructor(context: Context) : LifecycleObserver { @OnLifecycleEvent(Lifecycle.Event.ON_START) fun onEnterForeground() { enterForegroundToast.showAfterCanceling(enterBackgroundToast) } @OnLifecycleEvent(Lifecycle.Event.ON_STOP) fun onEnterBackground() { enterBackgroundToast.showAfterCanceling(enterForegroundToast) } } Lifecycle
Kotlin and Android Arch Components class AppLifecycleObserver @Inject constructor(context: Context) : LifecycleObserver { @OnLifecycleEvent(Lifecycle.Event.ON_START) fun onEnterForeground() { enterForegroundToast.showAfterCanceling(enterBackgroundToast) } @OnLifecycleEvent(Lifecycle.Event.ON_STOP) fun onEnterBackground() { enterBackgroundToast.showAfterCanceling(enterForegroundToast) } } Lifecycle ProcessLifecycleOwner.get().lifecycle.addObserver(appLifecycleObserver)
Kotlin and Android Arch Components /** * Displays a message when app comes to foreground and goes to background. */ class AppLifecycleObserver @Inject constructor(context: Context) : LifecycleObserver { @OnLifecycleEvent(Lifecycle.Event.ON_START) fun onEnterForeground() { enterForegroundToast.showAfterCanceling(enterBackgroundToast) } @OnLifecycleEvent(Lifecycle.Event.ON_STOP) fun onEnterBackground() { enterBackgroundToast.showAfterCanceling(enterForegroundToast) } } Lifecycle
Kotlin and Android Arch Components /** * Displays a message when app comes to foreground and goes to background. */ class AppLifecycleObserver @Inject constructor(context: Context) : LifecycleObserver { @OnLifecycleEvent(Lifecycle.Event.ON_START) fun onEnterForeground() { enterForegroundToast.showAfterCanceling(enterBackgroundToast) } @OnLifecycleEvent(Lifecycle.Event.ON_STOP) fun onEnterBackground() { enterBackgroundToast.showAfterCanceling(enterForegroundToast) } } Lifecycle
LiveData LiveData is an observable data holder. It lets the components in your app, usually the UI, observe data objects for changes. Kotlin and Android Arch Components
Kotlin and Android Arch Components LiveData
Kotlin and Android Arch Components LiveData
Kotlin and Android Arch Components LiveData
LiveData var userLiveData : MutableLiveData<List<User>> = MutableLiveData();
LiveData var userLiveData : MutableLiveData<List<User>> = MutableLiveData(); userLiveData.value = User(id=1, name="John Doe”)
LiveData var userLiveData : MutableLiveData<List<User>> = MutableLiveData(); userLiveData.value = User(id=1, name="John Doe”) //Activity or fragment viewModel.getUsers().observe( this, Observer { result -> run { //Handle Result } })
LiveData
ViewModel Observes the lifecycle state of the view, maintaining consistency during configuration changes and other Android lifecycle events. The ViewModel class is designed to store and manage UI-related data so that the data survives configuration changes such as screen rotations.  Kotlin and Android Arch Components
ViewModel
ViewModel
class NoteViewModel @Inject constructor(var repo: NotesRepository) : ViewModel() { fun getNotes(sort: String): LiveData<List<Note>> { return repo.getAllNotes(sort = sort) } fun addNote(note: Note): LiveData<Note> { return repo.addNote(note = note) } fun deleteNote(note: Note): LiveData<Int> { return repo.deleteNote(note) } ViewModel
class NoteViewModel @Inject constructor(var repo: NotesRepository) : ViewModel() { fun getNotes(sort: String): LiveData<List<Note>> { return repo.getAllNotes(sort = sort) } fun addNote(note: Note): LiveData<Note> { return repo.addNote(note = note) } fun deleteNote(note: Note): LiveData<Int> { return repo.deleteNote(note) } ViewModel
class NoteViewModel @Inject constructor(var repo: NotesRepository) : ViewModel() { fun getNotes(sort: String): LiveData<List<Note>> { return repo.getAllNotes(sort = sort) } fun addNote(note: Note): LiveData<Note> { return repo.addNote(note = note) } fun deleteNote(note: Note): LiveData<Int> { return repo.deleteNote(note) } ViewModel
class NotesListActivity : AppCompatActivity(){ private lateinit var notesList: ArrayList<Note> @Inject lateinit var viewModelFactory: ViewModelFactory private val viewModel by lazy { ViewModelProviders.of( this@NotesListActivity, viewModelFactory).get(NoteViewModel::class.java) } ... } ViewModel
ViewModel
@Inject lateinit var viewModelFactory: ViewModelFactory private val viewModel by lazy { ViewModelProviders.of(this@NotesListActivity, viewModelFactory).get(NoteViewModel::class.java) } //Activity viewModel.getNotes(sort = sort).observe( this, Observer { data -> run { notesList.clear() notesList.addAll(data!!) adapter.updateItems(notesList) } }) ViewModel
Room Persistence library for Android Kotlin and Android Arch Components
Entities and Dao’s Kotlin and Android Arch Components Room
@Entity(tableName = "user_table") data class User(var created: Date = Date(), var name: String = "", @PrimaryKey var id: Int = 0) Entities
interface BaseDao<T> { @Insert fun insert(vararg obj: T) } @Dao abstract class DataDao : BaseDao<Data>() { @Query("SELECT * FROM Data") abstract fun getData(): List<Data> } Dao
@Dao interface UserDao { @Insert(onConflict = OnConflictStrategy.REPLACE) fun insert(user: User) @Delete fun delete(user: User) @Query("SELECT * FROM user_table") fun getUsers(): List<User> } Dao
@Dao interface UserDao { @Insert(onConflict = OnConflictStrategy.REPLACE) fun insert(user: User) @Delete fun delete(user: User) @Query("SELECT * FROM user_table") fun getUsers(): List<User> } Dao
@Dao interface UserDao { @Insert(onConflict = OnConflictStrategy.REPLACE) fun insert(user: User) @Delete fun delete(user: User) @Query("SELECT * FROM user_table") fun getUsers(): LiveData<List<User>> } Dao
@Transaction open fun updateData(users: List<User>) { deleteAllUsers() insertAll(users) } Dao
class UserAndAllPets { @Embedded var user: User? = null @Relation(parentColumn = “userId”, entityColumn = “owner”) var pets: List<Pet> = ArrayList() } //UserDao.kt @Transaction @Query(“SELECT * FROM Users”) List<UserAndAllPets> getUsers(); Dao
@Database(entities = arrayOf(User::class), version = 1, exportSchema = true) @TypeConverters(Converters::class) abstract class AppDatabase : RoomDatabase() { abstract fun UserDao(): UserDao } Database
class MyApp : Application() { lateinit var mDataBase: AppDatabase override fun onCreate() { super.onCreate() mDataBase = Room.databaseBuilder(this, AppDatabase::class.java, "users").build() } } Database
@Provides @Singleton fun provideDB(context: Context): AppDatabase = Room.databaseBuilder(context, AppDatabase::class.java, “users") .fallbackToDestructiveMigration() .build() Database
Paging Efficient Lists and data loading Kotlin and Android Arch Components
Paging
Paging
class MyPagedListAdapter(diffCallback: DiffCallback<User>) : PagedListAdapter<User, MyPagedListAdapter.ViewHolder>(diffCallback) { PagedListAdapter override fun onBindViewHolder(holder: MyPagedListAdapter.ViewHolder?, position: Int) { getItem(position)?.let { holder?.setData(it) } } override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): MyPagedListAdapter.ViewHolder { val view = LayoutInflater.from(parent?.context).inflate(R.layout.item_user, parent, false) return ViewHolder(view) } }
class MyPagedListAdapter(diffCallback: DiffCallback<User>) : PagedListAdapter<User, MyPagedListAdapter.ViewHolder>(diffCallback) { PagedListAdapter override fun onBindViewHolder(holder: MyPagedListAdapter.ViewHolder?, position: Int) { getItem(position)?.let { holder?.setData(it) } } override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): MyPagedListAdapter.ViewHolder { val view = LayoutInflater.from(parent?.context).inflate(R.layout.item_user, parent, false) return ViewHolder(view) } }
class MyPagedListAdapter(diffCallback: DiffCallback<User>) : PagedListAdapter<User, MyPagedListAdapter.ViewHolder>(diffCallback) { PagedListAdapter override fun onBindViewHolder(holder: MyPagedListAdapter.ViewHolder?, position: Int) { getItem(position)?.let { holder?.setData(it) } } override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): MyPagedListAdapter.ViewHolder { val view = LayoutInflater.from(parent?.context).inflate(R.layout.item_user, parent, false) return ViewHolder(view) } }
DiffCallback private val diffCallback = object : DiffUtil.ItemCallback<User>() { override fun areItemsTheSame(oldItem: User, newItem: User): Boolean = oldItem.id == newItem.id override fun areContentsTheSame(oldItem: User, newItem: User): Boolean = oldItem == newItem } }
Query @Query("SELECT * FROM users ORDER WHERE age>:age ORDER by name DESC, id ASC") abstract fun usersOlderThan(age: Int): LivePagedListProvider<Int, User>
ViewModel class UserViewModel(val db:AppDB) : ViewModel() { val users: LiveData<PagedList<User>> fun getUsersOlderThan(age) { users = db.userDao().usersOlderThan(age) .create(0, PagedList.Config.Builder() .setPageSize(20) .setPrefetchDistance(20) .setEnablePlaceholders(false) .build()) } }
Activity viewModel.getUsersOlderThan(5).observe(this, pagedList -> { usersAdapter.setList(pagedList); });
Thank you Questions Kotlin and Android Arch Components @aditlal

Android Architecture Components with Kotlin

  • 1.
    Kotlin and AndroidArch Components Adit Lal @aditlal
  • 2.
    Introduction An application witha solid architecture should be: •Easy to scale and maintain. •Each component should be isolated and decoupled. •Easy to test Kotlin and Android Arch Components
  • 3.
    Kotlin and AndroidArch Components
  • 4.
    Components Create an UIthat automatically responds to lifecycle events. Lifecycle Kotlin and Android Arch Components
  • 5.
    Components Create an UIthat automatically responds to lifecycle events. Lifecycle LiveData Build data objects that notify views when underlying data changes Kotlin and Android Arch Components
  • 6.
    Components Create an UIthat automatically responds to lifecycle events. Lifecycle LiveData ViewModel Build data objects that notify views when underlying data changes Store UI related data that isn’t destroyed on app rotation Kotlin and Android Arch Components
  • 7.
    Components Create an UIthat automatically responds to lifecycle events. Lifecycle LiveData ViewModel Room Build data objects that notify views when underlying data changes Store UI related data that isn’t destroyed on app rotation Access your data with the power of SQLite and safety of in-app objects. Kotlin and Android Arch Components
  • 8.
    Lifecycle States and Events Kotlinand Android Arch Components
  • 9.
    Kotlin and AndroidArch Components Lifecycle
  • 10.
    Kotlin and AndroidArch Components Lifecycle
  • 11.
    Kotlin and AndroidArch Components
  • 12.
    Kotlin and AndroidArch Components /** * Displays a message when app comes to foreground and goes to background. */ class AppLifecycleObserver @Inject constructor(context: Context) : LifecycleObserver { @OnLifecycleEvent(Lifecycle.Event.ON_START) fun onEnterForeground() { enterForegroundToast.showAfterCanceling(enterBackgroundToast) } @OnLifecycleEvent(Lifecycle.Event.ON_STOP) fun onEnterBackground() { enterBackgroundToast.showAfterCanceling(enterForegroundToast) } } Lifecycle
  • 13.
    Kotlin and AndroidArch Components class AppLifecycleObserver @Inject constructor(context: Context) : LifecycleObserver { @OnLifecycleEvent(Lifecycle.Event.ON_START) fun onEnterForeground() { enterForegroundToast.showAfterCanceling(enterBackgroundToast) } @OnLifecycleEvent(Lifecycle.Event.ON_STOP) fun onEnterBackground() { enterBackgroundToast.showAfterCanceling(enterForegroundToast) } } Lifecycle ProcessLifecycleOwner.get().lifecycle.addObserver(appLifecycleObserver)
  • 14.
    Kotlin and AndroidArch Components /** * Displays a message when app comes to foreground and goes to background. */ class AppLifecycleObserver @Inject constructor(context: Context) : LifecycleObserver { @OnLifecycleEvent(Lifecycle.Event.ON_START) fun onEnterForeground() { enterForegroundToast.showAfterCanceling(enterBackgroundToast) } @OnLifecycleEvent(Lifecycle.Event.ON_STOP) fun onEnterBackground() { enterBackgroundToast.showAfterCanceling(enterForegroundToast) } } Lifecycle
  • 15.
    Kotlin and AndroidArch Components /** * Displays a message when app comes to foreground and goes to background. */ class AppLifecycleObserver @Inject constructor(context: Context) : LifecycleObserver { @OnLifecycleEvent(Lifecycle.Event.ON_START) fun onEnterForeground() { enterForegroundToast.showAfterCanceling(enterBackgroundToast) } @OnLifecycleEvent(Lifecycle.Event.ON_STOP) fun onEnterBackground() { enterBackgroundToast.showAfterCanceling(enterForegroundToast) } } Lifecycle
  • 16.
    LiveData LiveData is anobservable data holder. It lets the components in your app, usually the UI, observe data objects for changes. Kotlin and Android Arch Components
  • 17.
    Kotlin and AndroidArch Components LiveData
  • 18.
    Kotlin and AndroidArch Components LiveData
  • 19.
    Kotlin and AndroidArch Components LiveData
  • 20.
    LiveData var userLiveData :MutableLiveData<List<User>> = MutableLiveData();
  • 21.
    LiveData var userLiveData :MutableLiveData<List<User>> = MutableLiveData(); userLiveData.value = User(id=1, name="John Doe”)
  • 22.
    LiveData var userLiveData :MutableLiveData<List<User>> = MutableLiveData(); userLiveData.value = User(id=1, name="John Doe”) //Activity or fragment viewModel.getUsers().observe( this, Observer { result -> run { //Handle Result } })
  • 23.
  • 24.
    ViewModel Observes the lifecycle stateof the view, maintaining consistency during configuration changes and other Android lifecycle events. The ViewModel class is designed to store and manage UI-related data so that the data survives configuration changes such as screen rotations.  Kotlin and Android Arch Components
  • 25.
  • 26.
  • 27.
    class NoteViewModel @Inject constructor(varrepo: NotesRepository) : ViewModel() { fun getNotes(sort: String): LiveData<List<Note>> { return repo.getAllNotes(sort = sort) } fun addNote(note: Note): LiveData<Note> { return repo.addNote(note = note) } fun deleteNote(note: Note): LiveData<Int> { return repo.deleteNote(note) } ViewModel
  • 28.
    class NoteViewModel @Inject constructor(varrepo: NotesRepository) : ViewModel() { fun getNotes(sort: String): LiveData<List<Note>> { return repo.getAllNotes(sort = sort) } fun addNote(note: Note): LiveData<Note> { return repo.addNote(note = note) } fun deleteNote(note: Note): LiveData<Int> { return repo.deleteNote(note) } ViewModel
  • 29.
    class NoteViewModel @Inject constructor(varrepo: NotesRepository) : ViewModel() { fun getNotes(sort: String): LiveData<List<Note>> { return repo.getAllNotes(sort = sort) } fun addNote(note: Note): LiveData<Note> { return repo.addNote(note = note) } fun deleteNote(note: Note): LiveData<Int> { return repo.deleteNote(note) } ViewModel
  • 30.
    class NotesListActivity :AppCompatActivity(){ private lateinit var notesList: ArrayList<Note> @Inject lateinit var viewModelFactory: ViewModelFactory private val viewModel by lazy { ViewModelProviders.of( this@NotesListActivity, viewModelFactory).get(NoteViewModel::class.java) } ... } ViewModel
  • 31.
  • 32.
    @Inject lateinit varviewModelFactory: ViewModelFactory private val viewModel by lazy { ViewModelProviders.of(this@NotesListActivity, viewModelFactory).get(NoteViewModel::class.java) } //Activity viewModel.getNotes(sort = sort).observe( this, Observer { data -> run { notesList.clear() notesList.addAll(data!!) adapter.updateItems(notesList) } }) ViewModel
  • 33.
    Room Persistence library forAndroid Kotlin and Android Arch Components
  • 34.
    Entities and Dao’s Kotlinand Android Arch Components Room
  • 35.
    @Entity(tableName = "user_table") dataclass User(var created: Date = Date(), var name: String = "", @PrimaryKey var id: Int = 0) Entities
  • 36.
    interface BaseDao<T> { @Insert funinsert(vararg obj: T) } @Dao abstract class DataDao : BaseDao<Data>() { @Query("SELECT * FROM Data") abstract fun getData(): List<Data> } Dao
  • 37.
    @Dao interface UserDao { @Insert(onConflict= OnConflictStrategy.REPLACE) fun insert(user: User) @Delete fun delete(user: User) @Query("SELECT * FROM user_table") fun getUsers(): List<User> } Dao
  • 38.
    @Dao interface UserDao { @Insert(onConflict= OnConflictStrategy.REPLACE) fun insert(user: User) @Delete fun delete(user: User) @Query("SELECT * FROM user_table") fun getUsers(): List<User> } Dao
  • 39.
    @Dao interface UserDao { @Insert(onConflict= OnConflictStrategy.REPLACE) fun insert(user: User) @Delete fun delete(user: User) @Query("SELECT * FROM user_table") fun getUsers(): LiveData<List<User>> } Dao
  • 40.
    @Transaction open fun updateData(users:List<User>) { deleteAllUsers() insertAll(users) } Dao
  • 41.
    class UserAndAllPets { @Embedded varuser: User? = null @Relation(parentColumn = “userId”, entityColumn = “owner”) var pets: List<Pet> = ArrayList() } //UserDao.kt @Transaction @Query(“SELECT * FROM Users”) List<UserAndAllPets> getUsers(); Dao
  • 42.
    @Database(entities = arrayOf(User::class), version= 1, exportSchema = true) @TypeConverters(Converters::class) abstract class AppDatabase : RoomDatabase() { abstract fun UserDao(): UserDao } Database
  • 43.
    class MyApp :Application() { lateinit var mDataBase: AppDatabase override fun onCreate() { super.onCreate() mDataBase = Room.databaseBuilder(this, AppDatabase::class.java, "users").build() } } Database
  • 44.
    @Provides @Singleton fun provideDB(context: Context):AppDatabase = Room.databaseBuilder(context, AppDatabase::class.java, “users") .fallbackToDestructiveMigration() .build() Database
  • 45.
    Paging Efficient Lists anddata loading Kotlin and Android Arch Components
  • 46.
  • 47.
  • 48.
    class MyPagedListAdapter(diffCallback: DiffCallback<User>): PagedListAdapter<User, MyPagedListAdapter.ViewHolder>(diffCallback) { PagedListAdapter override fun onBindViewHolder(holder: MyPagedListAdapter.ViewHolder?, position: Int) { getItem(position)?.let { holder?.setData(it) } } override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): MyPagedListAdapter.ViewHolder { val view = LayoutInflater.from(parent?.context).inflate(R.layout.item_user, parent, false) return ViewHolder(view) } }
  • 49.
    class MyPagedListAdapter(diffCallback: DiffCallback<User>): PagedListAdapter<User, MyPagedListAdapter.ViewHolder>(diffCallback) { PagedListAdapter override fun onBindViewHolder(holder: MyPagedListAdapter.ViewHolder?, position: Int) { getItem(position)?.let { holder?.setData(it) } } override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): MyPagedListAdapter.ViewHolder { val view = LayoutInflater.from(parent?.context).inflate(R.layout.item_user, parent, false) return ViewHolder(view) } }
  • 50.
    class MyPagedListAdapter(diffCallback: DiffCallback<User>): PagedListAdapter<User, MyPagedListAdapter.ViewHolder>(diffCallback) { PagedListAdapter override fun onBindViewHolder(holder: MyPagedListAdapter.ViewHolder?, position: Int) { getItem(position)?.let { holder?.setData(it) } } override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): MyPagedListAdapter.ViewHolder { val view = LayoutInflater.from(parent?.context).inflate(R.layout.item_user, parent, false) return ViewHolder(view) } }
  • 51.
    DiffCallback private val diffCallback= object : DiffUtil.ItemCallback<User>() { override fun areItemsTheSame(oldItem: User, newItem: User): Boolean = oldItem.id == newItem.id override fun areContentsTheSame(oldItem: User, newItem: User): Boolean = oldItem == newItem } }
  • 52.
    Query @Query("SELECT * FROMusers ORDER WHERE age>:age ORDER by name DESC, id ASC") abstract fun usersOlderThan(age: Int): LivePagedListProvider<Int, User>
  • 53.
    ViewModel class UserViewModel(val db:AppDB): ViewModel() { val users: LiveData<PagedList<User>> fun getUsersOlderThan(age) { users = db.userDao().usersOlderThan(age) .create(0, PagedList.Config.Builder() .setPageSize(20) .setPrefetchDistance(20) .setEnablePlaceholders(false) .build()) } }
  • 54.
  • 55.
    Thank you Questions Kotlin andAndroid Arch Components @aditlal