Slide 1

Slide 1 text

Android ViewModel Command + B৬ ೣԋ Tae-hwan

Slide 2

Slide 2 text

ࣗѐ • GDG Seoul ਍৔૓ • ٘۽੉٘ ա੉எ ਍৔૓ • RGP Korea • ਃӝਃ উ٘۽੉٘ ѐߊ • Blog : Է ݆਷ ѐߊ੗о غ ੗! (https://thdev.tech)

Slide 3

Slide 3 text

য়ט ঌইࠅ ղਊ • MVVM • AAC-ViewModel • AAC-ViewModel ղࠗ ௏٘ ࢓ಝࠁӝ

Slide 4

Slide 4 text

ViewModel vs AAC-ViewModel

Slide 5

Slide 5 text

ViewModel vs AAC-ViewModel ViewModel ୡӝച ߂ ࢎਊ ৘ AAC-ViewModel ୡӝച ߂ ࢎਊ ৘

Slide 6

Slide 6 text

ViewModel

Slide 7

Slide 7 text

ViewModel ੜ ঌӝ • ViewModel ҳઑ ঌӝ • ViewModelਸ ٜ݅ӝ ਤೠ بҳ • ViewModelਸ ੜ ഝਊೞӝ ਤೠ ܻ࠙ • ܻ࠙ೞ׮ ࠁݶ ࢤӝח Ҋ޹ • ؊ ա਷ ViewModel

Slide 8

Slide 8 text

ViewModel View Model DataBinding and commands ViewModel updates the model Send notifications Send notifications ViewModel ҳઑ ঌӝ

Slide 9

Slide 9 text

ViewModelਸ ٜ݅ӝ ਤೠ بҳ • View ViewModel р੄ ҙ҅ܳ ױ੺ दఃӝ ਤೠ ݾ੸ • DI(Dagger, koin) ഝਊೞৈ Ѿ೤بܳ ઴ੋ׮. • DataBinding ഝਊਵ۽ View৬੄ ੄ઓبܳ ઴ੋ׮.

Slide 10

Slide 10 text

ViewModelਸ ੜ ഝਊೞӝ ਤೠ ܻ࠙ • ViewModelҗ Model਷ ৉ೡী ٮۄ ୭؀ೠ ܻ࠙ೠ׮ • ܻ࠙ೡ ࣻ ੓ח ೠ ୭؀۽ ܻ࠙ೞৈ, ৮੹ೠ Factory pattern ୶ҳ

Slide 11

Slide 11 text

ܻ࠙ೞ׮ ࠁݶ ࢤӝח Ҋ޹ • അप਷ DataBinding੄ Ѿ೤بо ֫ইઉ ৮੹ೠ ഋకܳ ٜ݅ӝ औ૑ ঋ׮ • ழझథ ࠭৬ DataBindingਸ ୭؀ೠ ഝਊೞח ഋక۽ ࢸ҅ೡ ೙ਃо ੓׮

Slide 12

Slide 12 text

؊ ա਷ ViewModel • ղࠗীࢲ ࢎਊೞח Rx/Coroutines ١ਸ ਤೠ ۄ੉೐ ࢎ੉௿ਸ ٮܰب۾ ࢸ҅ೡ ೙ਃо ੓׮ • ૒੽ Lifecycle ଻ਊೞח ഋక۽ ѐߊ ೙ਃ • AAC-ViewModelਸ ഝਊೞѢա

Slide 13

Slide 13 text

Jetpack AAC-ViewModel

Slide 14

Slide 14 text

ViewModel = / AAC-ViewModel

Slide 15

Slide 15 text

No content

Slide 16

Slide 16 text

AAC-ViewModel ݾ੸ • The ViewModel class is designed to store and manage UI-related data in a lifecycle conscious way. The ViewModel class allows data to survive configuration changes such as screen rotations. • Android AAC ViewModel਷ ചݶ ഥ੹ द ؘ੉ఠܳ ਬ૑ೡ ࣻ ੓ח ҳઑ۽ ٣੗ੋೞ৓ਵݴ, Android Lifecycle੄ onDestroy() ௏٘о ز੘ೠ׮. https://developer.android.com/topic/libraries/architecture/viewmodel

Slide 17

Slide 17 text

AAC-ViewModel ੿ܻೞݶ • ݾ੸ : ചݶ ഥ੹ द ؘ੉ఠ ਬ૑ܳ ਤೠ ҳઑ۽ ViewModelਸ ٣੗ੋ • Activity/Fragment੄ Lifecycleী ٮۄ ViewModel ਬ૑ܳ ೞҊ ੓׮. • ղࠗীࢲ Lifecycleਸ ࢎਊೡ ࣻ ੓ب۾ ୡӝചೞҊ ੓ਵݴ, onDestroy() ز੘ द ViewModel ղ ࠗ੄ onCleared() ഐ୹ਸ ా೧ Releaseܳ ਬبೠ׮.

Slide 18

Slide 18 text

AAC-ViewModel != ViewModel

Slide 19

Slide 19 text

AAC-ViewModel • AAC ViewModel਷ Androidীࢲ ࢎਊ਷ оמೞ૑݅ • MSDN੄ ViewModelҗח ইޖ ࢚ҙহҊ, Ӓր ੉ܴ ݅ ViewModel੐ਸ ঌҊ о੗ • উ٘۽੉٘ ѐߊ ಞ੄ࢿਸ ୶ҳೞח ViewModel ੿ب۽ ౵ঈೞ੗ • ಞ੄࢚ Application ઱ੑ੉ оמೞҊ • ಞ੄࢚ AAC-ViewModel਷ ؘ੉ఠ ਬ૑ب ೧ળ׮

Slide 20

Slide 20 text

Jetpack AAC-ViewModel ౵೧஖ӝ

Slide 21

Slide 21 text

AAC-ViewModel • AAC-ViewModel਷ ইې 2ѐ۽ ࢚ࣘ߉ই ҳഅೠ׮ • ViewModel() • AndroidViewModel(application)

Slide 22

Slide 22 text

AAC-ViewModel class SharedViewModel : ViewModel() { val selected = MutableLiveData() fun select(item: Item) { selected.value = item } }

Slide 23

Slide 23 text

AAC-ViewModel ୡӝച • AAC-ViewModel ୡӝചח ViewModelProviders۽ ୡӝചೠ׮ • Activity/Fragment 2ѐ ઺ী ೞա۽ ୡӝച оמ • ӝࠄ ViewModel ࢤࢿ੗ ߧਤܳ ߩযաݶ Factoryܳ ࢚ࣘ߉ই ߹ب ҳഅ೧ঠ ೠ׮

Slide 24

Slide 24 text

AAC-ViewModel class MasterFragment : Fragment() { private lateinit var itemSelector: Selector private lateinit var model: SharedViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) model = activity?.run { ViewModelProviders.of(this).get(SharedViewModel::class.java) } ?: throw Exception("Invalid Activity") itemSelector.setOnClickListener { item -> // Update the UI } } } ViewModelProviders.of(this)

Slide 25

Slide 25 text

ViewModelProviders.of(this) • thisী Activity/Fragment۽੄ ୡӝചо оמ • Activity ࢎਊ द Activity Lifecycleਸ ٮܲ׮ • Fragment ࢎਊ द Fragment Lifecycleਸ ٮܲ׮

Slide 26

Slide 26 text

of ղࠗܳ ࠁ੗

Slide 27

Slide 27 text

ViewModelProviders.of(this) ӝࠄ ੿੄ @NonNull @MainThread public static ViewModelProvider of(@NonNull Fragment fragment) { return of(fragment, null); } @NonNull @MainThread public static ViewModelProvider of(@NonNull FragmentActivity activity) { return of(activity, null); }

Slide 28

Slide 28 text

ViewModelProviders.of(this) Factory ੿੄ @NonNull @MainThread public static ViewModelProvider of(@NonNull Fragment fragment, @Nullable Factory factory) { Application application = checkApplication(checkActivity(fragment)); if (factory == null) { factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application); } return new ViewModelProvider(fragment.getViewModelStore(), factory); } @NonNull @MainThread public static ViewModelProvider of(@NonNull FragmentActivity activity, @Nullable Factory factory) { Application application = checkApplication(activity); if (factory == null) { factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application); } return new ViewModelProvider(activity.getViewModelStore(), factory); } fragment.getViewModelStore() activity.getViewModelStore() ViewModelStore()ܳ ా೧ ӝઓ ViewModel ࢤࢿ ؘ੉ఠо թওח૑ ୓௼ ViewModelStore()ܳ ా೧ ӝઓ ViewModel ࢤࢿ ؘ੉ఠо թওח૑ ୓௼

Slide 29

Slide 29 text

Fragment.java @NonNull @Override public ViewModelStore getViewModelStore() { if (getContext() == null) { throw new IllegalStateException("Can't access ViewModels from detached fragment"); } if (mViewModelStore == null) { mViewModelStore = new ViewModelStore(); } return mViewModelStore; }

Slide 30

Slide 30 text

FragmentActivity.java @NonNull @Override public ViewModelStore getViewModelStore() { if (getApplication() == null) { throw new IllegalStateException("Your activity is not yet attached to the " + "Application instance. You can't request ViewModel before onCreate call."); } if (mViewModelStore == null) { NonConfigurationInstances nc = (NonConfigurationInstances) getLastNonConfigurationInstance(); if (nc != null) { // Restore the ViewModelStore from NonConfigurationInstances mViewModelStore = nc.viewModelStore; } if (mViewModelStore == null) { mViewModelStore = new ViewModelStore(); } } return mViewModelStore; }

Slide 31

Slide 31 text

ViewModelStore.java package androidx.lifecycle; public class ViewModelStore { private final HashMap mMap = new HashMap<>(); final void put(String key, ViewModel viewModel) { ViewModel oldViewModel = mMap.put(key, viewModel); if (oldViewModel != null) { oldViewModel.onCleared(); } } final ViewModel get(String key) { return mMap.get(key); } /** * Clears internal storage and notifies ViewModels that they are no longer used. */ public final void clear() { for (ViewModel vm : mMap.values()) { vm.onCleared(); } mMap.clear(); } }

Slide 32

Slide 32 text

ѾҴ • ViewModelStore.java • ActivityFragment.java/Fragment.javaীࢲ ViewModelStore.javaܳ о૑Ҋ੓׮. • Activity/Fragmentীࢲ ೙ਃೠ ViewModelStoreܳ HashMapਵ۽ ࠁҙೞҊ, ੉ܳ оઉৡ׮

Slide 33

Slide 33 text

ୡӝച ௏٘۽

Slide 34

Slide 34 text

AAC-ViewModel class MasterFragment : Fragment() { private lateinit var itemSelector: Selector private lateinit var model: SharedViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) model = activity?.run { ViewModelProviders.of(this).get(SharedViewModel::class.java) } ?: throw Exception("Invalid Activity") itemSelector.setOnClickListener { item -> // Update the UI } } } ViewModelProviders.of(this) get(SharedViewModel::class.java)

Slide 35

Slide 35 text

get(SharedViewModel::class.java) • Activity/Fragmentীࢲ ࠁҙ ઺ੋ ViewModelStore ё୓ܳ ੉ਊೞৈ getਸ ୊ܻ • getীࢲח ࢜۽਍ ё୓ܳ ࢤࢿೞѢա, ViewModelStoreী ӝઓ ё୓ܳ ׮द ࠛ۞ৡ׮ • HashMapী ੷੢ೞӝ ਤೠ Ҋਬఃܳ ੿੄ೠ׮.

Slide 36

Slide 36 text

get(Class) / Ҋਬః @NonNull @MainThread public T get(@NonNull Class modelClass) { String canonicalName = modelClass.getCanonicalName(); if (canonicalName == null) { throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels"); } return get(DEFAULT_KEY + ":" + canonicalName, modelClass); } private static final String DEFAULT_KEY = "androidx.lifecycle.ViewModelProvider.DefaultKey"; Default৬ canonicalNameਸ ೤୛ Ҋਬః ࢤࢿ Package name.SharedViewModel

Slide 37

Slide 37 text

get(Key, Class) @NonNull @MainThread public T get(@NonNull String key, @NonNull Class modelClass) { ViewModel viewModel = mViewModelStore.get(key); if (modelClass.isInstance(viewModel)) { //noinspection unchecked return (T) viewModel; } else { //noinspection StatementWithEmptyBody if (viewModel != null) { // TODO: log a warning. } } viewModel = mFactory.create(modelClass); mViewModelStore.put(key, viewModel); //noinspection unchecked return (T) viewModel; } ViewModelStore۽ࠗఠ getValue viewModel੉ null੉ ইפݶ return null੉ݶ ViewModelFactory۽ࠗఠ new Factoryח ӝࠄ ViewModelFactory ੿੄о ઓ੤ ViewModelStoreী put

Slide 38

Slide 38 text

ViewModelFactory

Slide 39

Slide 39 text

ViewModelFactory @NonNull @MainThread public static ViewModelProvider of(@NonNull Fragment fragment, @Nullable Factory factory) { Application application = checkApplication(checkActivity(fragment)); if (factory == null) { factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application); } return new ViewModelProvider(fragment.getViewModelStore(), factory); } @NonNull @MainThread public static ViewModelProvider of(@NonNull FragmentActivity activity, @Nullable Factory factory) { Application application = checkApplication(activity); if (factory == null) { factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application); } return new ViewModelProvider(activity.getViewModelStore(), factory); } fragment.getViewModelStore() activity.getViewModelStore() Factory ੿੄ܳ ೞ૑ ঋਵݶ AndroidViewModelFactoryࢎਊ Factory ੿੄ܳ ೞ૑ ঋਵݶ AndroidViewModelFactoryࢎਊ if (factory == null) { factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application); } if (factory == null) { factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application); }

Slide 40

Slide 40 text

ViewModelFactory Application ೞա݅ ੓ਸ ҃਋ /** * {@link Factory} which may create {@link AndroidViewModel} and * {@link ViewModel}, which have an empty constructor. */ public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory { private static AndroidViewModelFactory sInstance; /** * Retrieve a singleton instance of AndroidViewModelFactory. * * @param application an application to pass in {@link AndroidViewModel} * @return A valid {@link AndroidViewModelFactory} */ @NonNull public static AndroidViewModelFactory getInstance(@NonNull Application application) { if (sInstance == null) { sInstance = new AndroidViewModelFactory(application); } return sInstance; } private Application mApplication; /** * Creates a {@code AndroidViewModelFactory} * * @param application an application to pass in {@link AndroidViewModel} */ public AndroidViewModelFactory(@NonNull Application application) { mApplication = application;

Slide 41

Slide 41 text

ViewModelFactory ࢤࢿ੗о হח ҃਋ public static class NewInstanceFactory implements Factory { @SuppressWarnings("ClassNewInstance") @NonNull @Override public T create(@NonNull Class modelClass) { //noinspection TryWithIdenticalCatches try { return modelClass.newInstance(); } catch (InstantiationException e) { throw new RuntimeException("Cannot create an instance of " + modelClass, e); } catch (IllegalAccessException e) { throw new RuntimeException("Cannot create an instance of " + modelClass, e); } } }

Slide 42

Slide 42 text

ViewModelFactory ழझథ ࢤࢿ੗о ੓׮ݶ? class SharedViewModelFactory(data data data) : ViewModelProvider.Factory { override fun create(modelClass: Class): T { return SharedViewModel(data, data, data) as T } } ViewModelProviders.of(this, SharedViewModelFactory(data, data, data)).get(SharedViewModel::class.java)

Slide 43

Slide 43 text

ViewModel ࢤࢿ ৮ܐ

Slide 44

Slide 44 text

঱ઁ destroy ೡө?

Slide 45

Slide 45 text

ViewModel clear • ঱ઁ AAC-ViewModel੉ clear ೡө? • Activity/Fragment੄ Lifecycleੋ onDestroy()ীࢲ ഛੋ оמ

Slide 46

Slide 46 text

FragmentActivity.java /** * Destroy all fragments. */ @Override protected void onDestroy() { super.onDestroy(); if (mViewModelStore != null && !isChangingConfigurations()) { mViewModelStore.clear(); } mFragments.dispatchDestroy(); }

Slide 47

Slide 47 text

FragmentActivity.java /** * Called when the fragment is no longer in use. This is called * after {@link #onStop()} and before {@link #onDetach()}. */ @CallSuper public void onDestroy() { mCalled = true; FragmentActivity activity = getActivity(); boolean isChangingConfigurations = activity != null && activity.isChangingConfigurations(); if (mViewModelStore != null && !isChangingConfigurations) { mViewModelStore.clear(); } }

Slide 48

Slide 48 text

ViewModelStore.java /** * Clears internal storage and notifies ViewModels that they are no longer used. */ public final void clear() { for (ViewModel vm : mMap.values()) { vm.onCleared(); } mMap.clear(); }

Slide 49

Slide 49 text

ViewModel.java public abstract class ViewModel { /** * This method will be called when this ViewModel is no longer used and will be destroyed. *

* It is useful when ViewModel observes some data and you need to clear this subscription to * prevent a leak of this ViewModel. */ @SuppressWarnings("WeakerAccess") protected void onCleared() { } }

Slide 50

Slide 50 text

ViewModel = / AAC-ViewModel

Slide 51

Slide 51 text

AAC-ViewModel ঱ઁ ਬਊೞѱ ࢎਊೡө?

Slide 52

Slide 52 text

ਬਊೠ ViewModel? • Activity੄ ചݶ ഥ੹ द ؘ੉ఠ ਬ૑о ೙ਃೠ ҃਋ ਬਊೞ׮. • 1ѐ੄ Activityীࢲ ׮઺੄ Fragmentীࢲ زੌೠ ੉߮౟/ؘ੉ఠܳ ୊ܻೞҊ रਸ ҃਋ • onClearedীࢲ releaseо ೙ਃೞ׮

Slide 53

Slide 53 text

1ѐ੄ Activityীࢲ ׮઺੄ Fragment • Fragmentীࢲ fragmentо ইצ activityܳ ֈѹળ׮. • ViewModelProviders.of(activity).get(SharedViewModel::class.java)

Slide 54

Slide 54 text

RxJava disposable abstract class BaseViewModel : ViewModel() { val disposable: CompositeDisposable = CompositeDisposable() override fun onCleared() { super.onCleared() disposable.clear() } }

Slide 55

Slide 55 text

Coroutines cancel abstract class BaseViewModel : ViewModel(), CoroutineScope { val job: Job = Job() override val coroutineContext: CoroutineContext get() = Dispatchers.Default + job override fun onCleared() { super.onCleared() job.cancel() } }

Slide 56

Slide 56 text

хࢎ೤פ׮ [email protected]