Kotlin x Architecture Components

B0f8177e3259f29952640f98bf795965?s=47 satorufujiwara
January 18, 2018
950

Kotlin x Architecture Components

#Events

どこでもKotlin #5 〜Android特集〜
https://m3-engineer.connpass.com/event/74261/

#Links

Kotlin + Architecture Component + Dagger2によるAndroidアプリ設計
https://goo.gl/sQijNq

Architecture Components + Flux (+ Kotlin)によるAndroidアプリ設計
https://goo.gl/tsUwbi

Twitter: 直接@Injectつけてうまくいく方法について
https://goo.gl/9tZQkm

Kotlinの型を知る ~後編~ プラットフォーム型とCollections
https://goo.gl/ApknrK

B0f8177e3259f29952640f98bf795965?s=128

satorufujiwara

January 18, 2018
Tweet

Transcript

  1. Kotlin × Architecture Components ࣮ફ 2018.01.18 Ͳ͜Ͱ΋Kotlin #5 @satorufujiwara

  2. About me Satoru Fujiwara (@satorufujiwara) Android Engineer @CyberAgent ,Inc Organizer

    of Shibuya.apk ༁ॻ : Kotlin ΠϯɾΞΫγϣϯ ஶॻ : Android ΞϓϦઃܭύλʔϯೖ໳
  3. Kotlin & Me FRESH! ΛKotlinͰ։ൃ (2015೥3݄ʙ) Kotlin Advent Calendar -

    2016೥ɺKotlinͰAndroid։ൃ͢Δํ΁ (2015೥, goo.gl/k97pqw ) - JavaͰॻ͍ͯͨAndroidΤϯδχΞ͕ॳΊͯKotlinͰॻ͍ͯΈͨ (2016೥, goo.gl/huvTvT ) - KotlinͷܕΛ஌Δ ~ޙฤ~ ϓϥοτϑΥʔϜܕͱCollections (2017೥, goo.gl/ApknrK ) Kotlinೖ໳·Ͱͷॿ૸ಡຊ ( goo.gl/5vUT7o ) Kotlin ΠϯɾΞΫγϣϯ (຋༁ 2017೥10݄31೔ग़൛)
  4. @satofurujiwara Kotlin × Architecture Components ࣮ફ 4

  5. Architecture Componentsͱ͸ • AndroidͷΞʔΩςΫνϟͷͨΊͷϥΠϒϥϦ܈ • Google I/0 2017ͰॳΊͯЋ൛͕ެ։͞ΕΔ • 2017೥11݄6೔ʹ҆ఆ൛ͱͳΔ

    1.0.0͕ެ։͞Ε͍ͯΔ • ʮݎ࿚Ͱɺςετ͕͠΍͘͢ɺอक͠΍͍͢ΞϓϦʯΛ࡞Δ
  6. Architecture ComponentsͷϞδϡʔϧ • LifeCycles - Activity΍FragmentͷϥΠϑαΠΫϧΛѻ͏ɺίΞͱͳΔϥΠϒϥϦ • ViewModel - UIʹؔ࿈ͨ͠σʔλΛอ࣋ɺ؅ཧ͢ΔͨΊͷΫϥε

    • LiveData - ObserverύλʔϯΛ࣋ͭσʔλอ࣋༻ͷΫϥε • Room - SQLiteͷϥούʔ • Paging - σʔλΛෳ਺ճʹ෼͚ͯಡΈࠐΉ ্هͷ͏ͪඞཁͳ΋ͷΛ૊Έ߹Θͤͯ࢖͏
  7. // ಋೖํ๏ // build.gradle repositories { jcenter() google() // Google

    Maven RepositoryΛ௥Ճ } dependencies { // Lifecycles ͷΈ (ViewModel ΍ LiveData ͕ෆཁͷ৔߹) implementation "android.arch.lifecycle:runtime:1.0.3" kapt “android.arch.lifecycle:compiler:1.0.0" //Kotlinͷ৔߹͸kaptΛ༻͍Δ // ViewModelͱLiveData implementation "android.arch.lifecycle:extensions:1.0.0" kapt "android.arch.lifecycle:compiler:1.0.0" // Room implementation "android.arch.persistence.room:runtime:1.0.0" kapt "android.arch.persistence.room:compiler:1.0.0" // Paging implementation “android.arch.paging:runtime:1.0.0-alpha4-1" //·ͩAlpha൛ }
  8. ࠓ೔ͷ࿩ͷείʔϓ • (LifeCycles) • ViewModel • LiveData ओʹViewModelͱLiveDataΛKotlinͰ࢖͏৔߹ͷ࿩Λ͠·͢

  9. ViewModelͱ͸ • UI ʹؔ࿈ͨ͠σʔλΛอ࣋ɺ؅ཧ͢ΔͨΊͷΫϥε • ը໘ճసͷΑ͏ͳActivity͕࠶ੜ੒͞ΕΔ৔߹ʹ͓͍ͯ΋ɺΠϯ ελϯε͕ੜ͖࢒ΓɺσʔλΛอ࣋͠ଓ͚Δ • ্هΛ࣮ݱ͢ΔͨΊʹ಺෦తʹ͸setRetainInstance(true)ͳ HolderFragment಺ʹͦͷΠϯελϯε͕อ࣋͞Ε͍ͯΔ

  10. ViewModelͷऔಘ • ඞͣ ViewModelProvider::get Λ༻͍ͯऔಘ͢Δ • ViewModelProvider ͸ ViewModelProviders::of Ͱऔಘ͢Δ

    val viewModelProvider: ViewModelProvider = ViewModelProviders.of(this) //ViewModelΛܧঝͨ͠ΫϥεΛࢦఆ͢Δ val viewModel: MainViewModel = viewModelProvider.get(MainViewModel::class.java)
  11. ViewModelͷऔಘ • ViewModelProviders::of ͷҾ਺͸ FragmentActivity or Fragment • ࢦఆͨ͠Ϋϥε͕ҧ͑͹ ViewModel

    ͷΠϯελϯε΋ҟͳΔ • ಉ͡ FragmentActivity Λࢦఆ͢Ε͹ ViewModel ͸ಉ͡Πϯελϯε // in MainFragment viewModelA = ViewModelProviders.of(activity!!).get(MainViewModel::class.java) // viewModelAͱ͸ผͷΠϯελϯε viewModelB = ViewModelProviders.of(this).get(MainViewModel::class.java)
  12. ViewModelProvider.Factory • ViewModelΠϯελϯεੜ੒Λߦ͏ͨΊͷViewModelProvider.Factory Λࢦ ఆ͢Δ͜ͱ΋Ͱ͖Δ • ࢦఆ͠ͳ͚Ε͹σϑΥϧτͷ Factory ͕࢖ΘΕΔ //java

    public static ViewModelProvider of(@NonNull FragmentActivity activity, @NonNull Factory factory) { checkApplication(activity); return new ViewModelProvider(ViewModelStores.of(activity), factory); } public static ViewModelProvider of(@NonNull FragmentActivity activity) { initializeFactoryIfNeeded(checkApplication(activity)); return new ViewModelProvider(ViewModelStores.of(activity), sDefaultFactory); }
  13. DefaultFactory • Class::newInstance Λݺͼग़ͯ͠ΠϯελϯεΛੜ੒͢Δ • AndroidViewModelΛܧঝ͍ͯ͠Δ৔߹͸ • ίϯετϥΫλʹҾ਺Λ༩͍͑ͨ৔߹͸ಠࣗͷ Factory Λ࢖͏ඞ

    ཁ͕͋Δ modelClass.getConstructor(Application.class).newInstance(mApplication)
  14. //Android Architecture Blueprints todo-mvvm-live public class ViewModelFactory extends ViewModelProvider.NewInstanceFactory {

    private static volatile ViewModelFactory INSTANCE; private final Application mApplication; private final TasksRepository mTasksRepository; public static ViewModelFactory getInstance(Application application) { if (INSTANCE == null) { synchronized (ViewModelFactory.class) { if (INSTANCE == null) { INSTANCE = new ViewModelFactory(application, Injection.provideTasksRepository(application.getApplicationContext())); } } } return INSTANCE; } private ViewModelFactory(Application application, TasksRepository repository) { mApplication = application; mTasksRepository = repository; } @Override public <T extends ViewModel> T create(Class<T> modelClass) { if (modelClass.isAssignableFrom(StatisticsViewModel.class)) { return (T) new StatisticsViewModel(mApplication, mTasksRepository); } else if (modelClass.isAssignableFrom(TaskDetailViewModel.class)) { return (T) new TaskDetailViewModel(mApplication, mTasksRepository); } else if (modelClass.isAssignableFrom(AddEditTaskViewModel.class)) { return (T) new AddEditTaskViewModel(mApplication, mTasksRepository); } else if (modelClass.isAssignableFrom(TasksViewModel.class)) { return (T) new TasksViewModel(mApplication, mTasksRepository); } throw new IllegalArgumentException("Unknown ViewModel class: " + modelClass.getName()); } }
  15. ViewModelProvider.Factory • ಠࣗͰΠϯελϯε؅ཧ͢Δͷ͸େม • Dagger2(+Android֦ு)Λ࢖͍·͠ΐ͏ • Architecture Components × Dagger2

    × Kotlin ͸ϋϚΓϙΠϯτ ͕͍͔ͭ͘… • (Α͏΍͘Kotlinͷ࿩)
  16. // ಋೖํ๏ // build.gradle ext.dagger_version = "2.14.1" dependencies { implementation

    "com.google.dagger:dagger:$dagger_version" implementation "com.google.dagger:dagger-android:$dagger_version" implementation "com.google.dagger:dagger-android-support:$dagger_version" // for support libs kapt "com.google.dagger:dagger-compiler:$dagger_version" kapt "com.google.dagger:dagger-android-processor:$dagger_version" }
  17. Dagger2 Λ༻͍ͨViewModelProvider.Factory • Dagger2 ͷ MultiBinding ػೳΛ࢖͏ • ಉ͡ΫϥεͰ͋ͬͯ΋ Key

    Λࢦఆ͢Δ͜ͱͰɺͦΕʹରԠͨ͠ ΠϯελϯεΛऔಘͰ͖Δ @Module internal abstract class MainModule { @Binds @IntoMap @ViewModelKey(MainViewModel::class) //Key໊ʹରԠ͢ΔΠϯελϯε͸Ҿ਺ abstract fun bindMainViewModel(viewModel: MainViewModel): ViewModel }
  18. @Module internal abstract class MainModule { @Binds @IntoMap @ViewModelKey(MainViewModel::class) //Key໊

    abstract fun bindMainViewModel(viewModel: MainViewModel): ViewModel //Ҿ਺͕ରԠ͢Δ஋ } //gererated Java code in DaggerAppComponents.java private Map<Class<? extends ViewModel>, Provider<ViewModel>> getMapOfClassOfAndProviderOfViewModel() { return ImmutableMap.<Class<? extends ViewModel>, Provider<ViewModel>>of( MainViewModel.class, //Key໊ (Provider) mainViewModelProvider //ରԠ͢Δ஋ ); }
  19. Dagger2 Λ༻͍ͨViewModelProvider.Factory • Dagger2 ʹΑͬͯੜ੒͞ΕΔ Map<Class<? extends ViewModel>, Provider<ViewModel>> ͔Β

    ViewModel ΛऔΓग़͢ FactoryΛ࡞ Ε͹ྑ͍
  20. class ViewModelFactory @Inject constructor( private val creators: Map<Class<out ViewModel>, @JvmSuppressWildcards

    Provider<ViewModel>> // @JvmSuppressWildcards͕ඞਢ ) : ViewModelProvider.Factory { @Suppress("UNCHECKED_CAST") override fun <T : ViewModel> create(modelClass: Class<T>): T { var creator: Provider<ViewModel>? = creators[modelClass] if (creator == null) { for ((key, value) in creators) { if (modelClass.isAssignableFrom(key)) { creator = value break } } } if (creator == null) throw IllegalArgumentException("unknown model class " + modelClass) try { return creator.get() as T } catch (e: Exception) { throw RuntimeException(e) } } }
  21. //Kotlin class ViewModelFactory @Inject constructor(private val creators: Map<Class<out ViewModel>, Provider<ViewModel>>)

  22. //Kotlin class ViewModelFactory @Inject constructor(private val creators: Map<Class<out ViewModel>, Provider<ViewModel>>)

    //Java expected Map<Class<? extends ViewModel>, Provider<ViewModel>>
  23. //Kotlin class ViewModelFactory @Inject constructor(private val creators: Map<Class<out ViewModel>, Provider<ViewModel>>)

    //Java expected Map<Class<? extends ViewModel>, Provider<ViewModel>> //Java actual : wildcard(?)͕ͭ͘ Map<Class<? extends ViewModel>,? extends Provider<ViewModel>> //Java in java.inject package public interface Provider<T>
  24. //Kotlin class ViewModelFactory @Inject constructor(private val creators: Map<Class<out ViewModel>, @JvmSuppressWildcards

    Provider<ViewModel>>)
  25. //Kotlin class ViewModelFactory @Inject constructor(private val creators: Map<Class<out ViewModel>, @JvmSuppressWildcards

    Provider<ViewModel>>) //Java expected Map<Class<? extends ViewModel>, Provider<ViewModel>> //Java actual : ظ଴Ͳ͓Γ Map<Class<? extends ViewModel>, Provider<ViewModel>>
  26. Architecture Components × Dagger2 × Kotlin • ಠࣗͷ ViewModelProvider.Factory ΛInject͢Δ

    • ViewModelProvider Λܦ༝ͤͣʹ ViewModelΛ௚઀ Inject ͢Δͷ͸ؒ ҧ͍ @Inject lateinit var viewModelFactory: ViewModelProvider.Factory val viewModel: MainViewModel by lazy { ViewModelProviders.of(this, viewModelFactory).get(MainViewModel::class.java) } @Inject lateinit var viewModel : MainViewModel // ؒҧ͍
  27. ࢀߟ : ௚઀@Inject͚ͭͯ͏·͍͘͘ํ๏΋͋Δ ( goo.gl/9tZQkm )

  28. @satofurujiwara LiveData ͱ Kotlin 28

  29. LiveDataͱ͸ • ObserverύλʔϯΛ࣋ͭσʔλอ࣋༻ͷΫϥε • ؔ਺ observe Λ࢖ͬͯ஋Λ؂ࢹ͢Δ • ୈҰҾ਺ʹ LifecycleOwner

    (Activity/Fragment)Λࢦఆ͢Δ͜ͱͰɺϥΠϑαΠΫ ϧʹΑΔ؂ࢹͷఀࢭ͕Ͱ͖Δ viewModel.repos.observe(this, Observer { //reposͷ಺༰͕มߋ͞ΕͨΒݺͼग़͞ΕΔϥϜμ Log.d("MainActivity", "Repository List = $it") })
  30. LiveData::observe • ֦ுؔ਺Λఆٛ͢Δ͜ͱͰͪΐͬͱ͍͍ײ͡ʹͳΔ fun <T> LiveData<T>.observeBy(owner: LifecycleOwner, observer: (T?) ->

    Unit) = observe(owner, Observer<T> { observer.invoke(it) }) //after viewModel.repos.observeBy(this) { Log.d("MainActivity", "Repository List = $it") } //before viewModel.repos.observe(this, Observer { Log.d("MainActivity", "Repository List = $it") })
  31. LiveData::observe • ࣮͸ઌ΄Ͳͷ֦ுؔ਺ͷؔ਺ܕͷҾ਺ observer ͷҾ਺͸ T Ͱ͸ ͳ͘ T? (ৗʹNullable)

    fun <T> LiveData<T>.observeBy(owner: LifecycleOwner, observer: (T?) -> Unit) = observe(owner, Observer<T> { observer.invoke(it) }) fun <T> LiveData<T>.observeBy(owner: LifecycleOwner, observer: (T) -> Unit) = observe(owner, Observer<T> { observer.invoke(it) // Error! }) viewModel.repos.observeBy(this) { val l = it.length //Error! }
  32. Observer • Observer ΠϯλϑΣʔεͷϝιουͷҾ਺ʹ @Nullable ͕͍ͭ ͍ͯΔ͔Β //java public interface

    Observer<T> { void onChanged(@Nullable T t); }
  33. Observer಺Ͱͷnull-check • ֦ுؔ਺಺Ͱnull-checkΛ͢Ε͹͍͍ײ͡ʹ fun <T> LiveData<T>.observeNonNullBy(owner: LifecycleOwner, observer: (T) ->

    Unit) = observe(owner, Observer<T> { it ?: return@Observer observer.invoke(it) }) viewModel.repos.observeBy(this) { val l = it.length //OK! }
  34. ࢀߟݩ

  35. Observerͷ@Nullable • কདྷͷΞοϓσʔτͰফ͑Δ͔΋ʁ • ͦͷ৔߹͸Non-null͔Nullable͔Λ֤ࣗͰ൑அ͢Δ͜ͱʹͳΔ

  36. RxJava2ͷConsumerΠϯλϑΣʔε • Observer ͱಉ༷ͷ Single Abstract Method ΠϯλϑΣʔε • ͨͩ͠

    @Nullable ͸͍͍ͭͯͳ͍ //Java public interface Consumer<T> { void accept(T t) throws Exception; }
  37. ܕύϥϝʔλʹNonNull/NullableΛࢦఆͨ͠ͱ͖ͷڍಈ

  38. JavaͰఆٛ͞ΕͨܕύϥϝʔλͱNullableܕ • NullableܕΛࢦఆ͢Ε͹NullableܕɺNon-nullܕΛࢦఆ͢Ε͹ϓ ϥοτϑΥʔϜܕ • ʮܕύϥϝʔλ͸ɺnull ڐ༰ܕͷܕͷఆٛʹ͸࠷ޙʹٙ໰ූ͕ඞ ཁͰ͋Γɺٙ໰ූ͕ͳ͍ܕ͸ null ඇڐ༰ܕͰ͋Δͱ͍͏ϧʔϧ

    ʹରͯ͠ͷ།Ұͷྫ֎ʯ(Kotlin ΠϯɾΞΫγϣϯ 6.1.10)
  39. ܕύϥϝʔλΛNonNullʹ͍ͨ͠৔߹͸ • ܕύϥϝʔλʹ্ݶڥքͱͯ͠AnyܕΛࢦఆ͢Δ ( T:Any )

  40. ܕύϥϝʔλΛNonNullʹ͍ͨ͠৔߹͸ • ܕύϥϝʔλʹ্ݶڥքͱͯ͠AnyܕΛࢦఆ͢Δ ( T:Any ) • Kotlin ΠϯɾΞΫγϣϯ 9.1.4

  41. ͓·͚ : RxJava2 to LiveData • LiveData ͷ RxJava ༻֦ுΛಋೖ͢ΔࡍʹҎԼͷΑ͏ʹ֦ுؔ਺

    Λఆٛ͢Ε͹ɺϝιουνΣΠϯ͕ܨ͕Γ͍͍ײ͡ʹͳΔ fun <T> Publisher<T>.toLiveData() = LiveDataReactiveStreams.fromPublisher(this) val repos: LiveData<List<Repo>> = dispatcher.onRefreshRepo .map { it.data } .observeOn(AndroidSchedulers.mainThread()) //͜͜·Ͱ͸Flowable .toLiveData()
  42. Architecture Components × Kotlin • Dagger2ͱ஥ྑ͘ͳΖ͏ • ֦ுؔ਺Ͱ͍͍ײ͡ʹ • Qiitaʹ͸ࢀߟͷϦϙδτϦ΋͋ΔͷͰͥͻ

    • Kotlin + Architecture Component + Dagger2ʹΑΔAndroidΞϓϦઃܭ (goo.gl/sQijNq) • Architecture Components + Flux (+ Kotlin)ʹΑΔAndroidΞϓϦઃܭ (goo.gl/tsUwbi )
  43. Thank you! Have a Nice Kotlin!