Android architecture components are part of Android Jetpack. They are a collection of libraries that help you design robust, testable, and maintainable apps. In this talk, We will cover LiveData, ViewModel, Room and lifecycle components. We will go through practical code example to understand modern android app architecture especially MVVM architecture.
Building Modern Apps using Android Architecture Components
1. Building Modern Apps using
Android Architecture
Components
Hassan Abid
GDE Android - Singapore
@hassanabidpk
2.
3. Google I/O Talks
! Android Jetpack: what's new in Android Support Library
! Android Jetpack: what's new in Architecture Components
! Modern Android development: Android Jetpack, Kotlin, and more
! Android Jetpack: how to smartly use Fragments in your UI
! Android Jetpack: manage UI navigation with Navigation Controller
! Android Jetpack: manage infinite lists with RecyclerView and Paging
! Android Jetpack: easy background processing with WorkManager
! Android Slices: build interactive results for Google Search
! Android Jetpack: sweetening Kotlin development with Android KTX
! Protips: a fresh look at advanced topics for Android experts
4. Android Dev Summit 2018 Talks
! https://www.youtube.com/playlist?list=PLWz5rJ2EKKc8WFYCR9esqGGY0vOZm2l6e
5. AndroidX : Android Extension Libraries
! AndroidX fully replaces the Support Library by
providing feature parity and new libraries
! Added to Android Open Source Project
11. Adding Components to your project
allprojects {
repositories {
jcenter()
google()
}
}
12. Example : LifeCycle - include ViewModel and
LiveData (Pre-AndroidX)
dependencies {
def lifecycle_version = “2.0.0“
// ViewModel and LiveData
implementation "android.arch.lifecycle:extensions:$lifecycle_version"
// alternatively - just ViewModel. use -ktx for Kotlin
implementation "android.arch.lifecycle:viewmodel:$lifecycle_version"
}
13. Example : LifeCycle (AndroidX)
dependencies {
def lifecycle_version = "2.0.0"
// ViewModel and LiveData
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
// alternatively - just ViewModel use -ktx for Kotlin
implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version" }
16. LifeCycle Problem
internal class MyLocationListener(
private val context: Context,
private val callback: (Location) -> Unit
) {
fun start() {
// connect to system location service
}
fun stop() {
// disconnect from system location service
}
}
17. LifeCycle Problem
class MyActivity : AppCompatActivity() {
private lateinit var myLocationListener: MyLocationListener
override fun onCreate(...) {
myLocationListener = MyLocationListener(this) { location ->
// update UI
}
}
public override fun onStart() {
super.onStart()
myLocationListener.start()
// manage other components that need to respond
// to the activity lifecycle
}
public override fun onStop() {
super.onStop()
myLocationListener.stop()
// manage other components that need to respond
// to the activity lifecycle
}
}
18. Solution
! LifeCycle Owners : Objects with LifeCycle like Activities and
Fragments
! LifeCycle Observers : Observe Lifecycle owners and are
notified life cycle changes
19. Solution
class MyActivity : AppCompatActivity() {
private lateinit var myLocationListener: MyLocationListener
override fun onCreate(...) {
myLocationListener = MyLocationListener(this, lifecycle) { location ->
// update UI
}
Util.checkUserStatus { result ->
if (result) {
myLocationListener.enable()
}
}
}
}
20. Solution
internal class MyLocationListener(
private val context: Context,
private val lifecycle: Lifecycle,
private val callback: (Location) -> Unit
) {
private var enabled = false
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun start() {
if (enabled) {
// connect
}
}
fun enable() {
enabled = true
if (lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) {
// connect if not connected
}
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun stop() {
// disconnect if connected
}
}
22. Fragment View Lifecycle (ViewModel)
viewModel.plantAndGardenPlantings.observe(viewLifecycleOwner, Observer { result ->
if (result != null && result.isNotEmpty())
adapter.submitList(result)
})
23. LifeCycle Benefits
! Avoid Lifecycle related UI state loss: View Model
! Observability for your UI: LiveData
! Avoid Lifecycle related memory leak
! LiveData transformations
! UI-Database observations : LiveData /Room
! XML-ViewModel observations: DataBinding
! Query and Observe UI Lifecycle State
24. Use cases for lifecycle-aware components
! Switching between coarse and fine-grained location
updates.
! Stopping and starting video buffering.
! Starting and stopping network connectivity.
! Pausing and resuming animated drawables.
26. Databinding
A support library that allows you to bind UI components in your
layouts to data sources in your app using a declarative format
rather than programmatically.
TextView textView = findViewById(R.id.sample_text);
textView.setText(viewModel.getUserName());
<TextView
android:text="@{viewmodel.userName}" />
30. ViewModel
! Provide data for UI Components
! Survive Configuration changes
ViewModel
Hold UI Data
Repository
API for loading and saving
data
Activity
Drawing UI
User Interactions
Presenter
Process Data for UI
31. ViewModel
class PlantDetailViewModel() : ViewModel() {
val plant: LiveData<Plant>
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val plantDetailViewModel = ViewModelProviders.of(this, factory)
.get(PlantDetailViewModel::class.java)
}
Don’t store activity context in
ViewModel
33. LiveData
LiveData is an observable data holder. It is lifecycle aware
public class ProductViewModel extends AndroidViewModel {
private final LiveData<ProductEntity> mObservableProduct;
// ....
public LiveData<ProductEntity> getObservableProduct() {
return mObservableProduct;
}
}
// Observe product data and
model.getObservableProduct().observe(this, new Observer<ProductEntity>() {
@Override
public void onChanged(@Nullable ProductEntity productEntity) {
// update UI
}
});
36. Room
! Object Mapping for SQLite Database
! Raw Queries are supported
! Support for RxJava
! Work with java.util.Optional
37. Room - Entity
@Entity(tableName = "plants")
data class Plant(
@PrimaryKey @ColumnInfo(name = "id") val plantId: String,
val name: String,
val description: String,
val growZoneNumber: Int,
val wateringInterval: Int = 7,
val imageUrl: String = ""
)
38. Room - Dao
@Dao
interface PlantDao {
@Query("SELECT * FROM plants ORDER BY name")
fun getPlants(): LiveData<List<Plant>>
@Query("SELECT * FROM plants WHERE growZoneNumber = :growZoneNumber ORDER BY name")
fun getPlantsWithGrowZoneNumber(growZoneNumber: Int): LiveData<List<Plant>>
@Query("SELECT * FROM plants WHERE id = :plantId")
fun getPlant(plantId: String): LiveData<Plant>
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertAll(plants: List<Plant>)
}
Observable queries
39. Room - RoomDatabase
@Database(entities = [GardenPlanting::class, Plant::class], version = 1, exportSchema = false)
@TypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase() {
abstract fun gardenPlantingDao(): GardenPlantingDao
abstract fun plantDao(): PlantDao
...
}
43. Paging
! Fetch from database, Network, or both into RecycleView
! Extension of observable List Pattern (e.g LiveData<List>)
! Configurable Load Size, prefetch, placeholders
! Integrates with Room, LiveData, RX
! Stable release
44. Paging - core components 1/2
! PagedList
○ Load data in pages
○ Async data loading
! DataSource and DataSource.Factory
○ Base class for loading snapshots of data into PagedList
○ Backed by Network or Database
45. Paging - core components 2/2
! LivePagedListBuilder
○ Build a LiveData<PagedList> based on DataSource.Factory and
a PagedList.config
! BoundaryCallBack
○ Signals when PagedList has reached end of available data
! PagedListAdapter
○ A RecyclerView.Adapter that presents data from PagedLists
54. Navigation - NavigationController
val navController = Navigation.findNavController(this, R.id.garden_nav_fragment)
// Set up ActionBar
setSupportActionBar(binding.toolbar)
NavigationUI.setupActionBarWithNavController(this, navController, drawerLayout)
// Set up navigation menu
binding.navigationView.setupWithNavController(navController)
55. Navigation - NavigationOnClickListener
private fun createOnClickListener(plantId: String): View.OnClickListener {
val bundle = bundleOf(PlantDetailFragment.ARG_ITEM_ID to plantId)
return Navigation.createNavigateOnClickListener(
R.id.action_plant_list_fragment_to_plant_detail_fragment, bundle)
}
<action
android:id="@+id/action_plant_list_fragment_to_plant_detail_fragment"
app:destination="@id/plant_detail_fragment"/>
59. Typical Flow (Compress Images example)
class CompressWorker(context : Context, params : WorkerParameters)
: Worker(context, params) {
override fun doWork(): Result {
// Do the work here--in this case, compress the stored images.
// In this example no parameters are passed; the task is
// assumed to be "compress the whole library."
myCompress()
// Indicate success or failure with your return value:
return Result.SUCCESS
// (Returning RETRY tells WorkManager to try this task again
// later; FAILURE says not to try again.)
}
}
60. Typical Flow (Compress Images example)
// Create a Constraints object that defines when the task should run
val myConstraints = Constraints.Builder()
.setRequiresDeviceIdle(true)
.setRequiresCharging(true)
// Many other constraints are available, see the
// Constraints.Builder reference
.build()
// ...then create a OneTimeWorkRequest that uses those constraints
val compressionWork = OneTimeWorkRequestBuilder<CompressWorker>()
.setConstraints(myConstraints)
.build()
WorkManager.getInstance().enqueue(compressionWork)
61. Typical Flow - Cancelling a task
val compressionWorkId:UUID = compressionWork.getId()
WorkManager.getInstance().cancelWorkById(compressionWorkId)
62. Samples and Code Lab
Android SunFlower https://github.com/googlesamples/android-sunflower
Universal Music Player https://github.com/googlesamples/android-UniversalMusicPlayer/blob/master/app/build.gradle
Architecture Components https://github.com/googlesamples/android-architecture-components
Plaid 2.0 https://github.com/nickbutcher/plaid
Code Labs https://codelabs.developers.google.com/?cat=Android
Guide to Architecture Components https://developer.android.com/jetpack/docs/guide
Code Highlighter : https://romannurik.github.io/SlidesCodeHighlighter/
63. Conclusion
! Finally Android platform got an Architecture
! Highly Recommended for New Apps
! Try out samples and code-labs