Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Android Architecture ComponentのLifecyle対応コンポーネン...

Android Architecture ComponentのLifecyle対応コンポーネントのおさらい / Review of Lifecyle aware components of AAC

Yasutaka Kawamoto

January 25, 2020
Tweet

More Decks by Yasutaka Kawamoto

Other Decks in Programming

Transcript

  1. • Տຊ ହ޹(͔Θ΋ͱ ΍͔ͨ͢) • ॴଐɿגࣜձࣾ tech vein 
 (େࡕࢢதԝ۠ຊொ)

    • ϞόΠϧΞϓϦΤϯδχΞ
 (AndroidଟΊɺiOS΋ŧŔŕŪũƄŝſ) • GitHub: kwmt ɺtwitter: kwmt27 • Google I/O2018 ॳࢀՃ • ෱Ԭग़਎ • ΫϥϑτϏʔϧ޷͖ ࣗݾ঺հ
  2. ViewModelͷੜଘظؒ onCreate ࣌ؒ ViewModelੜ੒ onStart onResume onPause onStop onDestroy onCreate

    onStart onResume ىಈ ճస όοΫΩʔͰΞϓϦऴྃ Activity ViewModel
  3. ViewModelͷੜଘظؒ onCreate ࣌ؒ ViewModelੜ੒ onStart onResume onPause onStop onDestroy onCreate

    onStart onResume onPause onStop onDestroy ىಈ ճస όοΫΩʔͰΞϓϦऴྃ onCleared Activity ViewModel
  4. ViewModelͷੜଘظؒ onCreate ࣌ؒ ViewModelੜ੒ onStart onResume onPause onStop onDestroy onCreate

    onStart onResume onPause onStop onDestroy ىಈ ճస όοΫΩʔͰΞϓϦऴྃ onCleared Activity ViewModel
  5. override fun onCreate(savedInstanceState: Bundle?) { // লུ val chronometer =

    findViewById(R.id.chronometer) val startTime = viewModel.startTime if (startTime == null) { val time = SystemClock.elapsedRealtime() viewModel.startTime = time chronometer.base = time } else { chronometer.base = startTime } chronometer.start() } Activity class ChronoViewModel: ViewModel() { var startTime: Long? = null } ViewModel
  6. • Kotlin ϓϩύςΟσϦήʔτΛ࢖͏ํ๏ ViewModelΠϯελϯεͷऔಘํ๏ (஫ҙ)fragment-ktxͷ1.1.0Ҏ্Λ࢖͏ඞཁ͋Γ val viewModel by viewModels<MyViewModel>() https://developer.android.com/jetpack/androidx/releases/lifecycle#2.2.0-alpha03

    ViewModelProviders.of(this) .get(MyViewModel::class.java) • ViewModelProviderΛ࢖͏ํ๏ viewModel = ViewModelProvider(this) .get(MyViewModel::class.java) 2.2.0-alpha03͔ΒDeprecated
  7. ViewModelʹߋ৽͍ͨ͠ViewΛ ࣋ͨͤͨΒ͍͍Μ͡Όʁ class MyViewModel : ViewModel() { val initialTime =

    SystemClock.elapsedRealtime() private val timer = Timer() var timerTextView: TextView? = null init{ timer.scheduleAtFixedRate(object : TimerTask() { override fun run() { timerTextView?.text = "$ඵ਺ ඵܦա" } }, 1000, 1000) } }
  8. ViewModelʹߋ৽͍ͨ͠ViewΛ ࣋ͨͤͨΒ͍͍Μ͡Όʁ class MyViewModel : ViewModel() { val initialTime =

    SystemClock.elapsedRealtime() private val timer = Timer() var timerTextView: TextView? = null init{ timer.scheduleAtFixedRate(object : TimerTask() { override fun run() { timerTextView?.text = "$ඵ਺ ඵܦա" } }, 1000, 1000) } } • ΞϓϦऴྃ࣌ʹϝϞϦϦʔΫ͢ΔͷͰɺͰ͖·ͤΜɻ
  9. ViewModelʹߋ৽͍ͨ͠ViewΛ ࣋ͨͤͨΒ͍͍Μ͡Όʁ class MyViewModel : ViewModel() { val initialTime =

    SystemClock.elapsedRealtime() private val timer = Timer() var timerTextView: TextView? = null init{ timer.scheduleAtFixedRate(object : TimerTask() { override fun run() { timerTextView?.text = "$ඵ਺ ඵܦա" } }, 1000, 1000) } } • ΞϓϦऴྃ࣌ʹϝϞϦϦʔΫ͢ΔͷͰɺͰ͖·ͤΜɻ AACͷLiveDataΛ࢖͍·͢ʂ
  10. LiveDataΛ࢖͏ class MyViewModel : ViewModel() { val initialTime = SystemClock.elapsedRealtime()

    private val timer = Timer() private val elapsedTime = MutableLiveData<Long>() fun getElapsedTime(): LiveData<Long> = elapsedTime init{ timer.scheduleAtFixedRate(object : TimerTask() { override fun run() { elapsedTime.postValue(ඵ਺) } }, 1000, 1000) } } viewModel.getElapsedTime()
 .observe(this, Observer { timerTextView.text = "${t}ඵܦա" }) • Activityଆ
  11. LiveDataΛ࢖͏ class MyViewModel : ViewModel() { val initialTime = SystemClock.elapsedRealtime()

    private val timer = Timer() private val elapsedTime = MutableLiveData<Long>() fun getElapsedTime(): LiveData<Long> = elapsedTime init{ timer.scheduleAtFixedRate(object : TimerTask() { override fun run() { elapsedTime.postValue(ඵ਺) } }, 1000, 1000) } } viewModel.getElapsedTime()
 .observe(this, Observer { timerTextView.text = "${t}ඵܦա" }) • Activityଆ
  12. ͜͜Ͱղܾ͍ͨ͠՝୊ Activity override fun onResume(){ locationManager.requestLocationUpdates(লུ) } override fun onPause(){

    locationManager.removeUpdates(লུ) } • AndroidͷϥΠϒϥϦ΍API͸͜ͷΑ͏ͳॲཧΛ͢Δ΋ͷ ͕ଟ͍ͷͰɺActivity΍FragmentΫϥε͕๲ΒΜͰ͖ͯɺ ಡΈʹ͘͘ͳΓ·͢ɻ
  13. Activity override fun onCreate(){ LocationLifecyle(this) } Lifecycle class LocationLifecycle(lifecycleOwner: LifecycleOwner)

    ɹɹɹ: LifecycleObserver { init { lifecycleOwner.lifecycle.addObserver(this) } @OnLifecycleEvent(ON_RESUME) fun addLocationListener() { locationManager.requestLocationUpdates(লུ) } @OnLifecycleEvent(ON_PAUSE) fun removeLocationListener() { locationManager.removeUpdates(লུ) } }
  14. Activity override fun onCreate(){ LocationLifecyle(this) } Lifecycle class LocationLifecycle(lifecycleOwner: LifecycleOwner)

    ɹɹɹ: LifecycleObserver { init { lifecycleOwner.lifecycle.addObserver(this) } @OnLifecycleEvent(ON_RESUME) fun addLocationListener() { locationManager.requestLocationUpdates(লུ) } @OnLifecycleEvent(ON_PAUSE) fun removeLocationListener() { locationManager.removeUpdates(লུ) } } interfaceΛ࣮૷
  15. Activity override fun onCreate(){ LocationLifecyle(this) } Lifecycle class LocationLifecycle(lifecycleOwner: LifecycleOwner)

    ɹɹɹ: LifecycleObserver { init { lifecycleOwner.lifecycle.addObserver(this) } @OnLifecycleEvent(ON_RESUME) fun addLocationListener() { locationManager.requestLocationUpdates(লུ) } @OnLifecycleEvent(ON_PAUSE) fun removeLocationListener() { locationManager.removeUpdates(লུ) } } interfaceΛ࣮૷ observerΛొ࿥
  16. Activity override fun onCreate(){ LocationLifecyle(this) } Lifecycle class LocationLifecycle(lifecycleOwner: LifecycleOwner)

    ɹɹɹ: LifecycleObserver { init { lifecycleOwner.lifecycle.addObserver(this) } @OnLifecycleEvent(ON_RESUME) fun addLocationListener() { locationManager.requestLocationUpdates(লུ) } @OnLifecycleEvent(ON_PAUSE) fun removeLocationListener() { locationManager.removeUpdates(লུ) } } interfaceΛ࣮૷ observerΛొ࿥ ϥΠϑαΠΫϧʹґ ଘ͍ͨ͠ϝιουʹ ΞϊςʔγϣϯΛͭ ͚Δ
  17. class FugaFragment : Fragment() { interface OnClickListener { fun onClick()

    } private var onClickListener: OnClickListener? = null // লུ override fun onActivityCreated { // লུ onClickListener = requireActivity() as? OnClickListener // ϘλϯͳͲͷΫϦοΫΠϕϯτͰonClickΛݺͿ onClickListener?.onClick() } } class HogeActivity : AppCompatActivity(), FugaFragment.OnClickListener { // লུ override fun onClick() { // ͳʹ͔ } }
  18. class FugaFragment : Fragment() { interface OnClickListener { fun onClick()

    } private var onClickListener: OnClickListener? = null // লུ override fun onActivityCreated { // লུ onClickListener = requireActivity() as? OnClickListener // ϘλϯͳͲͷΫϦοΫΠϕϯτͰonClickΛݺͿ onClickListener?.onClick() } } class HogeActivity : AppCompatActivity(), FugaFragment.OnClickListener { // লུ override fun onClick() { // ͳʹ͔ } } ௨஌༻interfaceΛఆٛ ϑΟʔϧυʹม਺༻ҙ Activity͕interfaceΛ࣮૷ͯͨ͠ΒΩϟετ ௨஌༻ͷinterfaceΛ࣮૷
  19. class HogeActivity : AppCompatActivity() { private val viewModel by viewModels<HogeViewModel>()

    override fun onCreate(savedInstanceState: Bundle?) { // লུ viewModel.clickEvent.observe(this) { textView.text = it } } } private val viewModel by viewModels<HogeViewModel> ({ requireActivity() }) viewModel.onClick() class HogeViewModel : ViewModel() { val clickEvent = MutableLiveData<String>() fun onClick() { clickEvent.value = "Ϙλϯ͕ԡ͞ΕͨΑ" } }
  20. MediatorLiveDataΛ࢖͏ class ZipLIveDataViewModel : ViewModel() { val editText1 = MutableLiveData<String>()

    val editText2 = MutableLiveData<String>() val isEnabled = MediatorLiveData<Boolean>() init { val observer = Observer<String> { isEnabled.value = editText1.value?.isNotEmpty() ?: false && editText2.value?.isNotEmpty() ?: false } isEnabled.addSource(editText1, observer) isEnabled.addSource(editText2, observer) } }
  21. MediatorLiveDataͷϥούʔؔ਺Λ࡞Δ fun <A, B> zipLiveData(a: LiveData<A>, b: LiveData<B>): LiveData<Pair<A, B>>

    { return MediatorLiveData<Pair<A, B>>().apply { var lastA: A? = null var lastB: B? = null fun update() { val localLastA = lastA val localLastB = lastB if (localLastA != null && localLastB != null) { this.value = Pair(localLastA, localLastB) } } addSource(a) { lastA = it update() } addSource(b) { lastB = it update() } } } https://medium.com/@gauravgyal/combine-results-from-multiple-async-requests-90b6b45978f7
  22. MediatorLiveDataͷϥούʔؔ਺Λ࢖͏ class ZipLIveDataViewModel : ViewModel() { val editText1 = MutableLiveData<String>()

    val editText2 = MutableLiveData<String>() val isEnabled: LiveData<Boolean> = zipLiveData(editText1, editText2) .map { (t1, t2) -> t1.isNotEmpty() && t2.isNotEmpty() } }
  23. ൺ΂Δ class ZipLIveDataViewModel : ViewModel() { val editText1 = MutableLiveData<String>()

    val editText2 = MutableLiveData<String>() val isEnabled: LiveData<Boolean> = zipLiveData(editText1, editText2) .map { (t1, t2) -> t1.isNotEmpty() && t2.isNotEmpty() } } class ZipLIveDataViewModel : ViewModel() { val editText1 = MutableLiveData<String>() val editText2 = MutableLiveData<String>() val isEnabled = MediatorLiveData<Boolean>() init { val observer = Observer<String> { isEnabled.value = editText1.value?.isNotEmpty() ?: false && editText2.value?.isNotEmpty() ?: false } isEnabled.addSource(editText1, observer) isEnabled.addSource(editText2, observer) } }
  24. ભҠݩʹ໭Εͳ͍࣮૷ྫ override fun onViewCreated() { viewModel.nextLiveData .observe(viewLifecycleOwner) { nextViewType ->

    // ը໘ભҠ͢Δ } button .setOnClickListener{viewModel.onClickNextButton()}
 } fun onClickNextButton() { // APIϨεϙϯεͷ݁ՌʹΑͬͯɺը໘ભҠΛม͍͑ͨ৔߹ val data = repository.fetchData() // লུ _nextLiveData.value = nextType } Fragment ViewModel
  25. ભҠݩʹ໭Εͳ͍࣮૷ྫ override fun onViewCreated() { viewModel.nextLiveData .observe(viewLifecycleOwner) { nextViewType ->

    // ը໘ભҠ͢Δ } button .setOnClickListener{viewModel.onClickNextButton()}
 } fun onClickNextButton() { // APIϨεϙϯεͷ݁ՌʹΑͬͯɺը໘ભҠΛม͍͑ͨ৔߹ val data = repository.fetchData() // লུ _nextLiveData.value = nextType } Fragment ViewModel όοΫͰ໭͖ͬͯͨͱ͖ʹɺ onViewCreated͕ݺ͹Εͯɺ observe͍ͯ͠ΔonChanged͕ݺ͹ΕΔͨΊ
  26. LiveDataͷϥούʔΫϥεΛ࡞Δ https://medium.com/androiddevelopers/livedata-with-snackbar-navigation-and-other-events-the-singleliveevent-case-ac2622673150 data class Event<out T>(private val content: T) {

    var hasBeenHandled = false private set fun getContentIfNotHandled(): T? { return if (hasBeenHandled) { null } else { hasBeenHandled = true content } } }
  27. LiveDataͷϥούʔΫϥε(Event)Λ࢖͏ override fun onViewCreated() { viewModel.nextLiveData .observe(viewLifecycleOwner) { event ->

    event.getContentIfNotHandled()?.let {nextViewType -> // ը໘ભҠ͢Δ
 } } button .setOnClickListener{viewModel.onClickNextButton()}
 } fun onClickNextButton() { // APIϨεϙϯεͷ݁ՌʹΑͬͯɺը໘ભҠΛม͍͑ͨ৔߹ val data = repository.fetchData() // লུ _nextLiveData.value = Event(nextType) } Fragment ViewModel
  28. LiveDataͷϥούʔΫϥε(Event)Λ࢖͏ override fun onViewCreated() { viewModel.nextLiveData .observe(viewLifecycleOwner) { event ->

    event.getContentIfNotHandled()?.let {nextViewType -> // ը໘ભҠ͢Δ
 } } button .setOnClickListener{viewModel.onClickNextButton()}
 } fun onClickNextButton() { // APIϨεϙϯεͷ݁ՌʹΑͬͯɺը໘ભҠΛม͍͑ͨ৔߹ val data = repository.fetchData() // লུ _nextLiveData.value = Event(nextType) } Fragment ViewModel EventΛηοτ͢ΔΑ͏ʹมߋ
  29. LiveDataͷϥούʔΫϥε(Event)Λ࢖͏ override fun onViewCreated() { viewModel.nextLiveData .observe(viewLifecycleOwner) { event ->

    event.getContentIfNotHandled()?.let {nextViewType -> // ը໘ભҠ͢Δ
 } } button .setOnClickListener{viewModel.onClickNextButton()}
 } fun onClickNextButton() { // APIϨεϙϯεͷ݁ՌʹΑͬͯɺը໘ભҠΛม͍͑ͨ৔߹ val data = repository.fetchData() // লུ _nextLiveData.value = Event(nextType) } Fragment ViewModel getContentIfNotHandled()ͷ࢓૊ΈͰɺ όοΫͰ໭͖ͬͯͨͱ͖͸null͕ฦΔ ͨΊɺը໘ભҠ͠ͳ͘ͳΓɺ ݩͷը໘ʹ໭Δ͜ͱ͕Ͱ͖Δ EventΛηοτ͢ΔΑ͏ʹมߋ