Slide 1

Slide 1 text

Inside Jetpack Architecture Components Hiroyuki Mori @moridroid

Slide 2

Slide 2 text

• Started as a Venture between Softbank and Yahoo • One of biggest search in Japan • Not a part of big

Slide 3

Slide 3 text

What & Why Architecture Components

Slide 4

Slide 4 text

2015

Slide 5

Slide 5 text

View View Controller Application Logic Network Persistent Model onSendClick sendComment

Slide 6

Slide 6 text

onSendClick sendComment View View Controller Application Logic Network Persistent Model

Slide 7

Slide 7 text

View View Controller Application Logic Network Persistent Model

Slide 8

Slide 8 text

View View Controller Application Logic Network Persistent Model subscribe

Slide 9

Slide 9 text

View View Controller Application Logic Network Persistent Model onSendClick sendComment

Slide 10

Slide 10 text

2016

Slide 11

Slide 11 text

No content

Slide 12

Slide 12 text

2016

Slide 13

Slide 13 text

2017

Slide 14

Slide 14 text

2017

Slide 15

Slide 15 text

2017

Slide 16

Slide 16 text

2018

Slide 17

Slide 17 text

2018 -JGFDZDMFT -JWF%BUB 7JFX.PEFM 3PPN 1BHJOH %BUB#JOEJOH /BWJHBUJPO 8PSL.BOBHFS %PXOMPBE.BOBHFS .FEJB1MBZCBDL 1FSNJTTJPOT /PUJDBUJPOT 4IBSJOH 4MJDFT "OJNBUJPO5SBOTJUJPOT "VUP 578FBS &NPKJ 'SBHNFOU -BZPVU 1BMFUUF "QQ$PNQBU "OESPJE,59 .VMUJEFY 5FTU

Slide 18

Slide 18 text

-JGFDZDMFT -JWF%BUB 7JFX.PEFM 3PPN 1BHJOH %BUB#JOEJOH /BWJHBUJPO 8PSL.BOBHFS

Slide 19

Slide 19 text

Lifcycle problems

Slide 20

Slide 20 text

In early days

Slide 21

Slide 21 text

Steve Pomeroy https://github.com/xxv/android-lifecycle CC-BY-SA 4.0 Today

Slide 22

Slide 22 text

LifecycleObserver

Slide 23

Slide 23 text

Android System NetworkReceiver Network state Foo Activity observe observe notify notify state changed

Slide 24

Slide 24 text

class NetworkReceiver(val listener : (NetworkInfo) -> Unit) : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { val con = context.getSystemService(ConnectivityManager::class.java) listener(con.activeNetworkInfo) } }

Slide 25

Slide 25 text

class NetworkReceiver(val listener : (NetworkInfo) -> Unit) : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { val con = context.getSystemService(ConnectivityManager::class.java) listener(con.activeNetworkInfo) } }

Slide 26

Slide 26 text

class NetworkReceiver(val listener : (NetworkInfo) -> Unit) : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { val con = context.getSystemService(ConnectivityManager::class.java) listener(con.activeNetworkInfo) } }

Slide 27

Slide 27 text

class MainActivity : AppCompatActivity() { private lateinit var networkReceiver : NetworkReceiver override fun onCreate(...) { networkReceiver = NetworkReceiver { // do something } val filter = IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION) registerReceiver(networkReceiver, filter) } override fun onDestroy() { unregisterReceiver(networkReceiver) } }

Slide 28

Slide 28 text

class MainActivity : AppCompatActivity() { private lateinit var networkReceiver : NetworkReceiver override fun onCreate(...) { networkReceiver = NetworkReceiver { // do something } val filter = IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION) registerReceiver(networkReceiver, filter) } override fun onDestroy() { unregisterReceiver(networkReceiver) } }

Slide 29

Slide 29 text

class MainActivity : AppCompatActivity() { private lateinit var networkReceiver : NetworkReceiver override fun onCreate(...) { networkReceiver = NetworkReceiver { // do something } val filter = IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION) registerReceiver(networkReceiver, filter) } override fun onDestroy() { unregisterReceiver(networkReceiver) } }

Slide 30

Slide 30 text

class NetworkObserver(val owner : LifecycleOwner, private val context: Context, private val listener: (NetworkInfo) -> Unit) : LifecycleObserver { private val receiver = object : BroadcastReceiver() { ... } }

Slide 31

Slide 31 text

class NetworkObserver(val owner : LifecycleOwner, private val context: Context, private val listener: (NetworkInfo) -> Unit) : LifecycleObserver { init { owner.lifecycle.addObserver(this) } private val receiver = object : BroadcastReceiver() { ... } }

Slide 32

Slide 32 text

... @OnLifecycleEvent(Lifecycle.Event.ON_CREATE) fun register() { val filter = IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION) context.registerReceiver(receiver, filter) } @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) fun unregister() { context.unregisterReceiver(receiver) } }

Slide 33

Slide 33 text

class MainActivity : AppCompatActivity() { override fun onCreate(...) { NetworkObserver(this, this) { // do something } } }

Slide 34

Slide 34 text

Observer Pattern vs Pub-Sub Pattern

Slide 35

Slide 35 text

<> Subject

Slide 36

Slide 36 text

<> Subject ConcreteSubject ConcreteSubject

Slide 37

Slide 37 text

<> Subject ConcreteSubject ConcreteSubject <> Observer ConcreteObserve r ConcreteObserve r observe

Slide 38

Slide 38 text

<> Subject ConcreteSubject ConcreteSubject <> Observer ConcreteObserve r ConcreteObserve r event notify

Slide 39

Slide 39 text

No content

Slide 40

Slide 40 text

No content

Slide 41

Slide 41 text

No content

Slide 42

Slide 42 text

No content

Slide 43

Slide 43 text

LiveData

Slide 44

Slide 44 text

No content

Slide 45

Slide 45 text

Activity#0 created onCreate onStart onResume onPause onStop onDestroy Device rotated Activity#1 created Exception Call Web API

Slide 46

Slide 46 text

LiveData addObserver

Slide 47

Slide 47 text

LiveData Observer value

Slide 48

Slide 48 text

LiveData Observer Lifecycle Owner

Slide 49

Slide 49 text

Lifecycle Owner LiveData LifecycleBoundObserver Observer addObserver addObserver dispatch events active onChanged LiveData

Slide 50

Slide 50 text

Lifecycle Owner LiveData LifecycleBoundObserver Observer addObserver addObserver dispatch events inactive onChanged LiveData

Slide 51

Slide 51 text

Lifecycle Owner LiveData LifecycleBoundObserver Observer removeObserver addObserver dispatch events destroyed onChanged

Slide 52

Slide 52 text

object CatRepository { private val _cat = MutableLiveData() fun loadCat(catId : Int) : LiveData { fetch(catId) { newCat -> _cat.postValue(newCat) } return _cat } }

Slide 53

Slide 53 text

class CatActivity : AppCompatActivity() { override fun onCreate(...) { val catId = intent.getIntExtra("cat_id") CatRepository.loadCat(catId).observe(this, Observer { cat -> showCat(cat)}) } }

Slide 54

Slide 54 text

class CatActivity : AppCompatActivity() { override fun onCreate(...) { ... val relatedCatsModule = RelatedCatsModuleAdapter { clicked -> val intent = Intent(this, CatActivity::class.java) intent.putExtra("cat_id", clicked.id) startActivity(intent) } } }

Slide 55

Slide 55 text

ViewModels

Slide 56

Slide 56 text

No content

Slide 57

Slide 57 text

Activity#0 created onCreate onStart onResume Call Web API

Slide 58

Slide 58 text

Activity#0 created onCreate onStart onResume onPause onStop onDestroy Device rotated Activity#1 created Call Web API onCreate Call Web API

Slide 59

Slide 59 text

ViewModel Provider ViewModel Store Activity/Fragment request get null ViewModel

Slide 60

Slide 60 text

return ViewModel Provider ViewModel Store Activity/Fragment request get null put ViewModel ViewModel

Slide 61

Slide 61 text

ViewModel Store Activity/Fragment ViewModel in NonConfigurationInstance

Slide 62

Slide 62 text

No content

Slide 63

Slide 63 text

public class FragmentActivity extends ComponentActivity ... { @Override public final Object onRetainNonConfigurationInstance() { ... NonConfigurationInstances nci = new NonConfigurationInstances(); nci.custom = custom; nci.viewModelStore = mViewModelStore; nci.fragments = fragments; return nci; }

Slide 64

Slide 64 text

public class FragmentActivity extends ComponentActivity ... { @Override protected void onCreate(...) { ... NonConfigurationInstances nc = (NonConfigurationInstances) getLastNonConfigurationInstance(); if (nc != null && nc.viewModelStore != null && mViewModelStore == null) { mViewModelStore = nc.viewModelStore; }

Slide 65

Slide 65 text

No content

Slide 66

Slide 66 text

Repository Model Repository

Slide 67

Slide 67 text

Repository Model Repository Activity / Fragment

Slide 68

Slide 68 text

Repository Model

Slide 69

Slide 69 text

private val _cat = MutableLiveData>() fun updateCat(new: Cat) { val currentCats = _cat.value ?: return val updatedCats = currentCats.toMutableList() .map { old -> if(old.id == new.id) new else old } _cat.postValue(updatedCats) // update storage and web service ... }

Slide 70

Slide 70 text

private val _cat = MutableLiveData>() fun updateCat(new: Cat) { val currentCats = _cat.value ?: return val updatedCats = currentCats.toMutableList() .map { old -> if(old.id == new.id) new else old } _cat.postValue(updatedCats) // update storage and web service ... }

Slide 71

Slide 71 text

private val _cat = MutableLiveData>() fun updateCat(new: Cat) { val currentCats = _cat.value ?: return val updatedCats = currentCats.toMutableList() .map { old -> if(old.id == new.id) new else old } _cat.postValue(updatedCats) // update storage and web service ... }

Slide 72

Slide 72 text

@Entity data class Cat( @PrimaryKey(autoGenerate = true) val id: Int = 0, val name: String, val age: Int, val description: String, val loved: Boolean )

Slide 73

Slide 73 text

@Dao interface CatDao { @Query("SELECT * from Cat") fun loadCats() : LiveData> @Update fun update(cat: Cat) }

Slide 74

Slide 74 text

@Database(entities = [Cat::class], version = 1) abstract class CatDatabase : RoomDatabase() { abstract fun catDao() : CatDao }

Slide 75

Slide 75 text

fun getCatDatabase(context: Context) = Room.databaseBuilder(context, CatDatabase::class.java, "catDB").build()

Slide 76

Slide 76 text

fun updateCat(new: Cat) { // update logic here catDao.updateCat(new) catService.updateData(new) }

Slide 77

Slide 77 text

Cat Database Cat table room_master_table Id, identity_hash

Slide 78

Slide 78 text

Cat Database Cat table room_master_table Id, identity_hash room_table_modification_log InvalidationTracker Updated? Version, table_id onInvalidated Observer trigger Oh, yes!

Slide 79

Slide 79 text

Cat Database Cat table room_master_table Id, identity_hash room_table_modification_log InvalidationTracker Version, table_id Observer ComputeLiveDat a addObserver query Cat Dao

Slide 80

Slide 80 text

Cat Database Cat table room_master_table Id, identity_hash room_table_modification_log InvalidationTracker Version, table_id Observer Cat Dao ComputeLiveDat a addObserver query onInvalidated

Slide 81

Slide 81 text

Why use Paging library?

Slide 82

Slide 82 text

Repository ViewModel Activity / Fragment What do I display? I need cats Give us ALL Cats OK, 1,000,000 cats here. Here you are Show this 1,000,000 cats ¯\_(π)_/¯

Slide 83

Slide 83 text

PagedList DataSource

Slide 84

Slide 84 text

PagedList DataSource

Slide 85

Slide 85 text

PagedList DataSource DataSource.Factory

Slide 86

Slide 86 text

PagedList DataSource DataSource.Factory invalidate

Slide 87

Slide 87 text

PagedList DataSource DataSource.Factory

Slide 88

Slide 88 text

View ViewModel Repository DataSource.Factory LiveData Room LivePagedListBuilder

Slide 89

Slide 89 text

View Observer ViewModel Repository DataSource.Factory LiveData Room LivePagedListBuilder LivePagedListBuilder DataSource.Factory

Slide 90

Slide 90 text

View ViewModel Repository DataSource.Factory LiveData PagedListAdapter Room Observer LivePagedListBuilder AsyncPagedListDiffer

Slide 91

Slide 91 text

View ViewModel Repository DataSource.Factory LiveData PagedListAdapter Room Observer LivePagedListBuilder AsyncPagedListDiffer

Slide 92

Slide 92 text

public abstract class PagedListAdapter ... { final AsyncPagedListDiffer mDiffer; @Nullable protected T getItem(int position) { return mDiffer.getItem(position); } }

Slide 93

Slide 93 text

public class AsyncPagedListDiffer { ... public T getItem(int index) { ... mPagedList.loadAround(index); return mPagedList.get(index); } }

Slide 94

Slide 94 text

public abstract class PagedList extends AbstractList { ... public void loadAround(int index) { ... loadAroundInternal(index); ... } }

Slide 95

Slide 95 text

class ContiguousPagedList extends PagedList ... { protected void loadAroundInternal(int index) { int appendItems = getAppendItemsRequested(...); ... mAppendItemsRequested = Math.max(...); if (mAppendItemsRequested > 0) { scheduleAppend(); } }

Slide 96

Slide 96 text

Summary