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

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

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

CA.apk #8

Keita Kagurazaka

September 10, 2019
Tweet

More Decks by Keita Kagurazaka

Other Decks in Programming

Transcript

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

    View Slide

  2. androidx.activity 1.0.0から

    View Slide

  3. Fragmentのbackハンドリング

    View Slide

  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()
    }

    View Slide

  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)
    }

    View Slide

  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

    View Slide

  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は有効なものが
    登録逆順で呼び出される

    View Slide

  8. androidx.activity 1.0.0 &
    androidx.fragment 1.1.0から

    View Slide

  9. layout.xmlの指定

    View Slide

  10. レイアウトIDコンストラクタ
    ● Activity or FragmentのコンストラクタにlayoutのリソースIDを
    指定できるように
    ● setContentView or onCreateViewを省略できる
    ● Activityの場合DataBindingするのは面倒になる

    View Slide

  11. ViewModelの取得方法

    View Slide

  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)
    }

    View Slide

  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() })

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  17. androidx.fragment 1.2.0から
    (現在はalpha03)

    View Slide

  18. Activity破棄対策

    View Slide

  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)
    }

    View Slide

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

    View Slide

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

    View Slide

  22. これからのSavedStateのハンドリング in ViewModel
    class DetailViewModel(
    private val savedStateHandle: SavedStateHandle
    ) : ViewModel() {
    fun someProcess() {
    savedStateHandle.set("key_value", 100)
    val saved = savedStateHandle.get("key_value")
    }
    }
    Daggerの場合 -> Ask me later!

    View Slide

  23. これまでのFragmentコンテナ
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    android:id="@+id/content"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

    View Slide

  24. Fragment表示

    View Slide

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

    View Slide

  26. これからのFragmentコンテナ
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    android:id="@+id/content"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

    View Slide

  27. これからのFragmentコンテナ
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    android:id="@+id/content"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

    Fragment transaction時の
    アニメーションを適切に
    ハンドリングしてくれる

    View Slide

  28. androidx.lifecycle 2.1.0から

    View Slide

  29. LiveDataのストリーム変換

    View Slide

  30. これまでのLiveData transformations
    data class User(val name: String)
    // in ViewModel
    private val mutableUser = MutableLiveData()
    val user: LiveData = Transformations.map(mutableUser) { it.name }

    View Slide

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

    View Slide

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

    View Slide

  33. LiveData transformations
    data class User(val name: String)
    // in ViewModel
    private val mutableUser = MutableLiveData()
    val user: LiveData = mutableUser.map { it.name }
    .distinctUntilChanged()
    待望のdistinctUntilChanged追加

    View Slide

  34. kotlinx.coroutines

    View Slide

  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 {
    // 色々
    }
    }
    }

    View Slide

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

    View Slide

  37. これからのcoroutine起動 in ViewModel
    // in ViewModel
    fun onClicked() {
    viewModelScope.launch {
    // 色々
    }
    }
    onCleared()でキャンセルされる
    CoroutineScope

    View Slide

  38. androidx.lifecycle 2.2.0から
    (現在はalpha04)

    View Slide

  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()
    }
    }

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  43. これからのcoroutine起動 in LifecycleOwner
    class DetailFragment : Fragment() {
    init {
    lifecycleScope.launchWhenStarted {
    // Fragment transactionなどのライフサイクル制限がある処理
    }
    }
    }
    onStart()で開始し、
    onStop()でキャンセルされる
    coroutineを起動

    View Slide

  44. まとめ
    ● MVVM with coroutines + LiveDataで書くための仕組みがそ
    ろってきた
    ● minSdkVersionを23にして、このスタイルで書くのが標準に
    なっていくでしょう

    View Slide

  45. Thanks!

    View Slide