今後のJetpackでAndroid開発はこう変わる!

 今後のJetpackでAndroid開発はこう変わる!

CA.apk #8

0f8842fabcd31a6c9007ddcd648247db?s=128

Keita Kagurazaka

September 10, 2019
Tweet

Transcript

  1. 今後のJetpackで Android開発は こう変わる! 2019/09/10 ca.apk #8 Keita Kagurazaka

  2. androidx.activity 1.0.0から

  3. Fragmentのbackハンドリング

  4. これまでのFragmentのbackハンドリング interface OnBackPressedListener { fun onBackPressed(): Boolean } // in

    Activity override fun onBackPressed() { supportFragmentManager.fragments.reversed().forEach { if (it.isVisible && it is OnBackPressedListener) { if (it.onBackPressed()) return } } super.onBackPressed() }
  5. これからのFragmentのbackハンドリング // in Fragment override fun onAttach(context: Context) { super.onAttach(context)

    val onBackPressedCallback = requireActivity().onBackPressedDispatcher.addCallback(this) { // backが押されたときの処理 } Handler().postDelayed({ onBackPressedCallback.isEnabled = false }, 2000L) }
  6. これからのFragmentのbackハンドリング // in Fragment override fun onAttach(context: Context) { super.onAttach(context)

    val onBackPressedCallback = requireActivity().onBackPressedDispatcher.addCallback(this) { // backが押されたときの処理 } Handler().postDelayed({ onBackPressedCallback.isEnabled = false }, 2000L) } LifecycleOwner
  7. これからのFragmentのbackハンドリング // in Fragment override fun onAttach(context: Context) { super.onAttach(context)

    val onBackPressedCallback = requireActivity().onBackPressedDispatcher.addCallback(this) { // backが押されたときの処理 } Handler().postDelayed({ onBackPressedCallback.isEnabled = false }, 2000L) } callbackは有効なものが 登録逆順で呼び出される
  8. androidx.activity 1.0.0 & androidx.fragment 1.1.0から

  9. layout.xmlの指定

  10. レイアウトIDコンストラクタ • Activity or FragmentのコンストラクタにlayoutのリソースIDを 指定できるように • setContentView or onCreateViewを省略できる

    • Activityの場合DataBindingするのは面倒になる
  11. ViewModelの取得方法

  12. これまでのViewModel取得方法 // in Activity or Fragment private val viewModel: UserDetailViewModel

    by lazy { ViewModelProviders.of(this).get(UserDetailViewModel::class.java) } // in Fragment private val activityViewModel: UserDetailViewModel by lazy { ViewModelProviders.of(requireActivity()).get(UserDetailViewModel::class.java) } // in Fragment in Fragment private val parenttViewModel: UserDetailViewModel by lazy { ViewModelProviders.of(requireParentFragment()) .get(UserDetailViewModel::class.java) }
  13. これからのViewModel取得方法 // in Activity or Fragment private val viewModel: UserDetailViewModel

    by viewModels() // in Fragment private val activityViewModel: UserDetailViewModel by activityViewModels() // in Fragment in Fragment private val viewModel: UserDetailViewModel by viewModels({ requireParentFragment() })
  14. viewModelsの中身 @MainThread inline fun <reified VM : ViewModel> Fragment.viewModels( noinline

    ownerProducer: () -> ViewModelStoreOwner = { this }, noinline factoryProducer: (() -> Factory)? = null ) = createViewModelLazy(VM::class, { ownerProducer().viewModelStore }, factoryProducer)
  15. viewModelsの中身 @MainThread inline fun <reified VM : ViewModel> Fragment.viewModels( noinline

    ownerProducer: () -> ViewModelStoreOwner = { this }, noinline factoryProducer: (() -> Factory)? = null ) = createViewModelLazy(VM::class, { ownerProducer().viewModelStore }, factoryProducer) ViewModelのscopeを決める
  16. viewModelsの中身 @MainThread inline fun <reified VM : ViewModel> Fragment.viewModels( noinline

    ownerProducer: () -> ViewModelStoreOwner = { this }, noinline factoryProducer: (() -> Factory)? = null ) = createViewModelLazy(VM::class, { ownerProducer().viewModelStore }, factoryProducer) ViewModelのFactoryを指定する
  17. androidx.fragment 1.2.0から (現在はalpha03)

  18. Activity破棄対策

  19. これまでのSavedStateのハンドリング in ViewModel // in Fragment override fun onCreate(savedInstanceState: Bundle?)

    { super.onCreate(savedInstanceState) savedInstanceState?.let(viewModel::onRestoreInstanceState) } override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) viewModel.onSaveInstanceState(outState) }
  20. これからのSavedStateのハンドリング in ViewModel class DetailViewModel( private val savedStateHandle: SavedStateHandle )

    : ViewModel() { fun someProcess() { savedStateHandle.set("key_value", 100) val saved = savedStateHandle.get<Int>("key_value") } }
  21. これからのSavedStateのハンドリング in ViewModel class DetailViewModel( private val savedStateHandle: SavedStateHandle )

    : ViewModel() { fun someProcess() { savedStateHandle.set("key_value", 100) val saved = savedStateHandle.get<Int>("key_value") } } Bundleに入る値なら入れられる
  22. これからのSavedStateのハンドリング in ViewModel class DetailViewModel( private val savedStateHandle: SavedStateHandle )

    : ViewModel() { fun someProcess() { savedStateHandle.set("key_value", 100) val saved = savedStateHandle.get<Int>("key_value") } } Daggerの場合 -> Ask me later!
  23. これまでのFragmentコンテナ <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent"> <FrameLayout android:id="@+id/content" android:layout_width="match_parent" android:layout_height="match_parent" /> </androidx.constraintlayout.widget.ConstraintLayout>

  24. Fragment表示

  25. これまでのFragmentコンテナ // in Activity supportFragmentManager.beginTransaction() .replace(R.id.content, HomeFragment(), null) .commit()

  26. これからのFragmentコンテナ <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent"> <androidx.fragment.app.FragmentContainerView android:id="@+id/content" android:layout_width="match_parent" android:layout_height="match_parent" /> </androidx.constraintlayout.widget.ConstraintLayout>

  27. これからのFragmentコンテナ <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent"> <androidx.fragment.app.FragmentContainerView android:id="@+id/content" android:layout_width="match_parent" android:layout_height="match_parent" /> </androidx.constraintlayout.widget.ConstraintLayout>

    Fragment transaction時の アニメーションを適切に ハンドリングしてくれる
  28. androidx.lifecycle 2.1.0から

  29. LiveDataのストリーム変換

  30. これまでのLiveData transformations data class User(val name: String) // in ViewModel

    private val mutableUser = MutableLiveData<User>() val user: LiveData<String> = Transformations.map(mutableUser) { it.name }
  31. これからのLiveData transformations data class User(val name: String) // in ViewModel

    private val mutableUser = MutableLiveData<User>() val user: LiveData<String> = mutableUser.map { it.name } .distinctUntilChanged()
  32. LiveData transformations data class User(val name: String) // in ViewModel

    private val mutableUser = MutableLiveData<User>() val user: LiveData<String> = mutableUser.map { it.name } .distinctUntilChanged() ktxにLiveDataの 拡張関数として定義された
  33. LiveData transformations data class User(val name: String) // in ViewModel

    private val mutableUser = MutableLiveData<User>() val user: LiveData<String> = mutableUser.map { it.name } .distinctUntilChanged() 待望のdistinctUntilChanged追加
  34. kotlinx.coroutines

  35. これまでのcoroutine起動 in ViewModel class DetailViewModel : ViewModel(), CoroutineScope { private

    val job = SupervisorJob() override val coroutineContext: CoroutineContext = Dispatchers.Main.immediate + job override fun onCleared() { cancel() } fun onClicked() { launch { // 色々 } } }
  36. これからのcoroutine起動 in ViewModel // in ViewModel fun onClicked() { viewModelScope.launch

    { // 色々 } }
  37. これからのcoroutine起動 in ViewModel // in ViewModel fun onClicked() { viewModelScope.launch

    { // 色々 } } onCleared()でキャンセルされる CoroutineScope
  38. androidx.lifecycle 2.2.0から (現在はalpha04)

  39. これまでのcoroutine起動 in LifecycleOwner class DetailFragment : Fragment(), CoroutineScope { private

    lateinit var job: Job override val coroutineContext get() = Dispatchers.Main + job override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) job = SupervisorJob() launch { … } } override fun onDestroy() { cancel() super.onDestroy() } }
  40. これからのcoroutine起動 in LifecycleOwner class DetailFragment : Fragment() { override fun

    onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) lifecycleScope.launch { // 色々 } } }
  41. これからのcoroutine起動 in LifecycleOwner class DetailFragment : Fragment() { override fun

    onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) lifecycleScope.launch { // 色々 } } } onDestroy()でキャンセルされる CoroutineScope
  42. これからのcoroutine起動 in LifecycleOwner class DetailFragment : Fragment() { init {

    lifecycleScope.launchWhenStarted { // Fragment transactionなどのライフサイクル制限がある処理 } } }
  43. これからのcoroutine起動 in LifecycleOwner class DetailFragment : Fragment() { init {

    lifecycleScope.launchWhenStarted { // Fragment transactionなどのライフサイクル制限がある処理 } } } onStart()で開始し、 onStop()でキャンセルされる coroutineを起動
  44. まとめ • MVVM with coroutines + LiveDataで書くための仕組みがそ ろってきた • minSdkVersionを23にして、このスタイルで書くのが標準に

    なっていくでしょう
  45. Thanks!