Slide 1

Slide 1 text

Introduction to Android Architecture Components @Alex_Zhukovich alexzh.com Andrzej Jóźwiak ajoz.github.io

Slide 2

Slide 2 text

Android JetPack

Slide 3

Slide 3 text

https://android-developers.googleblog.com/2018/05/use-android-jetpack-to-accelerate-your.html

Slide 4

Slide 4 text

Support Libraries AndroidX

Slide 5

Slide 5 text

https://github.com/foobaz42/CompanyContacts

Slide 6

Slide 6 text

Activity ViewModel Repository Local data source Remote data source

Slide 7

Slide 7 text

ViewModel Lifecycle LiveData Room

Slide 8

Slide 8 text

Lifecycle

Slide 9

Slide 9 text

public class TestFragment extends Fragment{ @Override void onStart() { registerComponent1(...); registerComponent2(...); registerComponent3(...); } @Override void onStop() { unregisterComponent1(...); unregisterComponent2(...); unregisterComponent3(...); } }

Slide 10

Slide 10 text

public class TestActivity extends AppCompatActivity { private Component component1; @Override void onCreate(Bundle bundle) { Component1 = new Component( this // Lifecycle object ) } ... } public class Component1 implements LifecycleObserver { public Component1(Lifecycle lifecycle) { lifecycle.addObserver(this) } @OnLifecycleEvent(Lifecycle.Event.ON_START) void start() { ... } @OnLifecycleEvent(Lifecycle.Event.ON_STOP) void stop() { ... } @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) void destroy() { lifecycle.removeObserver(this) } }

Slide 11

Slide 11 text

Lifecycle Lifecycle Observer Lifecycle Owner Lifecycle Registry

Slide 12

Slide 12 text

public abstract class Lifecycle { public enum State { DESTROYED, INITIALIZED, CREATED, STARTED, RESUMED; } public enum Event { ON_CREATE, ON_START, ON_RESUME, ON_PAUSE, ON_STOP, ON_DESTROY, ON_ANY } }

Slide 13

Slide 13 text

Fragment AppCompatActivity LifecycleService

Slide 14

Slide 14 text

700 ms

Slide 15

Slide 15 text

ViewModel

Slide 16

Slide 16 text

View Business Logic Data Repository UI Events Data Change Events Create Update Delete Read

Slide 17

Slide 17 text

View ViewModel Model UI Events Property Change Events Create Update Delete Read ViewModel data Model Change Events

Slide 18

Slide 18 text

? custom Application with static fields or stopping any configuration changes in the AndroidManifest.xml onSaveInstanceState(Bundle) onCreate(Bundle) Object onRetainNonConfigurationInstance() Object getLastNonConfigurationInstance() retaining a Fragment with setRetainInstance(true)

Slide 19

Slide 19 text

large amounts of business related data data that takes long to load data that takes long to process partial data that is still loading data that is hard to serialize data that is NOT related to View data that is NOT related to the Activity context data that is NOT Lifecycle reference ViewModel

Slide 20

Slide 20 text

class TeamsViewModel(): ViewModel() { fun someDomainRelatedMethod() { // very very very import stuff ... } }

Slide 21

Slide 21 text

class TeamsActivity : AppCompatActivity(){ // It is not necessary to hold this reference private lateinit var viewModel: TeamsViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) viewModel = ViewModelProviders.of(this) .get(TeamsViewModel::class.java) // we can use it now or later ... viewModel.someDomainRelatedMethod() } }

Slide 22

Slide 22 text

LiveData

Slide 23

Slide 23 text

data holder observed within a lifecycle Observer paired with LifecycleOwner this is NOT a Stream this is NOT an Rx Observable this is NOT a Sequence LiveData MutableLiveData MediatorLiveData

Slide 24

Slide 24 text

LiveData + ViewModel

Slide 25

Slide 25 text

class TeamsViewModel(): ViewModel() { private val teams: MutableLiveData> by lazy { MutableLiveData().also { loadTeams() } } fun loadTeams() { // Async operations to load data // from database/cache/cloud teams.value = // values } fun getTeams(): LiveData> { return teams } }

Slide 26

Slide 26 text

class TeamsActivity : AppCompatActivity(){ private lateinit var viewModel: TeamsViewModel override fun onCreate(savedInstanceState: Bundle?) { // ... viewModel = ViewModelProviders.of(this) .get(TeamsViewModel::class.java) viewModel.getTeams().observe( this, Observer>{ teams -> // update UI with the acquired data } ) } }

Slide 27

Slide 27 text

class TeamsViewModel(apl: Application): AndroidViewModel(apl) { fun someDomainRelatedMethod() { // Application instance is available through // - getApplication() method in Java // - application property in Kotlin } }

Slide 28

Slide 28 text

class TeamsViewModel( private val teamRepository: TeamRepository ): ViewModel() { fun fetchTeams(): LiveData> { // accessing the injected dependency return teamRepository.fetchTeams() } }

Slide 29

Slide 29 text

class TeamsFactory( private val repository: TeamRepository ) : ViewModelProvider.Factory { override fun create( modelClass: Class): TeamsViewModel { if (modelClass.isAssignableFrom( TeamsViewModel::class.java)) { return TeamsViewModel(repository) } throw IllegalArgumentException(“Unknown model class!”) } }

Slide 30

Slide 30 text

class TeamsActivity : AppCompatActivity(){ private lateinit var viewModel: TeamsViewModel override fun onCreate(savedInstanceState: Bundle?) { val repository = //injecting the dependency viewModel = ViewModelProviders // ViewModelProvider.Factory has to be specified // manually .of(this, TeamsFactory(repository)) .get(TeamsViewModel::class.java) } }

Slide 31

Slide 31 text

MediatorLiveData X X X X Y Y Y Y values returned by the “Mediator” func: X -> Y source: LiveData func: X -> Y Transformations.map(source, func) values returned by the “Source”

Slide 33

Slide 33 text

Room

Slide 34

Slide 34 text

No content

Slide 35

Slide 35 text

API 1 API 8 API 21 API 26 API 28 API 3 API 11 API 24 API 27 SQLite 3.4 SQLite 3.5 SQLite 3.6 SQLite 3.7 SQLite 3.8 SQLite 3.9 SQLite 3.18 SQLite 3.19 SQLite 3.22

Slide 36

Slide 36 text

val db = dbHelper.readableDatabase // Define a projection that specifies which columns from the database // you will actually use after this query. val projection = arrayOf(BaseColumns._ID, FeedEntry.COLUMN_NAME_TITLE, FeedEntry.COLUMN_NAME_SUBTITLE) // Filter results WHERE "title" = 'My Title' val selection = "${FeedEntry.COLUMN_NAME_TITLE} = ?" val selectionArgs = arrayOf("My Title") // How you want the results sorted in the resulting Cursor val sortOrder = "${FeedEntry.COLUMN_NAME_SUBTITLE} DESC" val cursor = db.query( FeedEntry.TABLE_NAME, // The table to query projection, // The array of columns to return (pass null to get all) selection, // The columns for the WHERE clause selectionArgs, // The values for the WHERE clause null, // don't group the rows null, // don't filter by row groups sortOrder // The sort order )

Slide 37

Slide 37 text

val db = dbHelper.readableDatabase // Define a projection that specifies which columns from the database // you will actually use after this query. val projection = arrayOf(BaseColumns._ID, FeedEntry.COLUMN_NAME_TITLE, FeedEntry.COLUMN_NAME_SUBTITLE) // Filter results WHERE "title" = 'My Title' val selection = "${FeedEntry.COLUMN_NAME_TITLE} = ?" val selectionArgs = arrayOf("My Title") // How you want the results sorted in the resulting Cursor val sortOrder = "${FeedEntry.COLUMN_NAME_SUBTITLE} DESC" val cursor = db.query( FeedEntry.TABLE_NAME, // The table to query projection, // The array of columns to return (pass null to get all) selection, // The columns for the WHERE clause selectionArgs, // The values for the WHERE clause null, // don't group the rows null, // don't filter by row groups sortOrder // The sort order ) SELECT id, title, subTitle FROM Feed WHERE title = ? ORDER BY subTitle DESC

Slide 38

Slide 38 text

SELECT * FROM employees WHERE team_id = :teamId

Slide 39

Slide 39 text

@Dao interface EmployeeDao { @Query("SELECT * FROM employees WHERE team_id = :teamId") fun getEmployeesByTeamId(): List }

Slide 40

Slide 40 text

@Insert fun insert(employee: Employee): Long @Insert fun insertListOfEmployees(employees: List) @Insert fun insertEmployees(vararg employee: Employee): List

Slide 41

Slide 41 text

@Update fun updateEmployee(employee: Employee): Int @Update fun updateEmployees(employees: List): Int @Update fun updateEmployee(employee: Employee)

Slide 42

Slide 42 text

@Delete fun deleteEmployee(employee: Employee): Int @Delete fun deleteEmployee(employees: List) @Delete fun deleteEmployee(vararg employee: Employee): Int

Slide 43

Slide 43 text

@Dao interface EmployeesDAO { @Query("SELECT * FROM employees WHERE team_id = :teamId") fun getEmployeesByTeamId(teamId: Long): LiveData> @Insert(onConflict = OnConflictStrategy. REPLACE) fun insertEmployee(employee: Employee) @Update fun updateEmployee(employee: Employee): Int @Delete fun deleteEmployee(employee: Employee): Int }

Slide 44

Slide 44 text

@Entity data class Employee( @PrimaryKey val id: Long, val name: String, val phone: String)

Slide 45

Slide 45 text

@Entity(tableName = "employees") data class Employee( @PrimaryKey @ColumnInfo(name = "employee_id") val id: Long, @ColumnInfo(name = "employee_name") val name: String, @ColumnInfo(name = "employee_phone") val phone: String)

Slide 46

Slide 46 text

@Database(entities = [Team::class, Employee::class], version = 1) abstract class ContactsDatabase: RoomDatabase() { abstract fun teamsDao(): TeamsDAO abstract fun employeesDao(): EmployeesDAO }

Slide 47

Slide 47 text

@Alex_Zhukovich alexzh.com Andrzej Jóźwiak ajoz.github.io