$30 off During Our Annual Pro Sale. View Details »

Kotlin x Architecture Components

satorufujiwara
January 18, 2018
1.4k

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

satorufujiwara

January 18, 2018
Tweet

Transcript

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

    of Shibuya.apk ༁ॻ : Kotlin ΠϯɾΞΫγϣϯ ஶॻ : Android ΞϓϦઃܭύλʔϯೖ໳
  2. 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೔ग़൛)
  3. Architecture ComponentsͷϞδϡʔϧ • LifeCycles - Activity΍FragmentͷϥΠϑαΠΫϧΛѻ͏ɺίΞͱͳΔϥΠϒϥϦ • ViewModel - UIʹؔ࿈ͨ͠σʔλΛอ࣋ɺ؅ཧ͢ΔͨΊͷΫϥε

    • LiveData - ObserverύλʔϯΛ࣋ͭσʔλอ࣋༻ͷΫϥε • Room - SQLiteͷϥούʔ • Paging - σʔλΛෳ਺ճʹ෼͚ͯಡΈࠐΉ ্هͷ͏ͪඞཁͳ΋ͷΛ૊Έ߹Θͤͯ࢖͏
  4. // ಋೖํ๏ // 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൛ }
  5. ViewModelͷऔಘ • ඞͣ ViewModelProvider::get Λ༻͍ͯऔಘ͢Δ • ViewModelProvider ͸ ViewModelProviders::of Ͱऔಘ͢Δ

    val viewModelProvider: ViewModelProvider = ViewModelProviders.of(this) //ViewModelΛܧঝͨ͠ΫϥεΛࢦఆ͢Δ val viewModel: MainViewModel = viewModelProvider.get(MainViewModel::class.java)
  6. 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)
  7. 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); }
  8. //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()); } }
  9. // ಋೖํ๏ // 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" }
  10. Dagger2 Λ༻͍ͨViewModelProvider.Factory • Dagger2 ͷ MultiBinding ػೳΛ࢖͏ • ಉ͡ΫϥεͰ͋ͬͯ΋ Key

    Λࢦఆ͢Δ͜ͱͰɺͦΕʹରԠͨ͠ ΠϯελϯεΛऔಘͰ͖Δ @Module internal abstract class MainModule { @Binds @IntoMap @ViewModelKey(MainViewModel::class) //Key໊ʹରԠ͢ΔΠϯελϯε͸Ҿ਺ abstract fun bindMainViewModel(viewModel: MainViewModel): ViewModel }
  11. @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 //ରԠ͢Δ஋ ); }
  12. 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) } } }
  13. //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>
  14. //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>>
  15. 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 // ؒҧ͍
  16. LiveDataͱ͸ • ObserverύλʔϯΛ࣋ͭσʔλอ࣋༻ͷΫϥε • ؔ਺ observe Λ࢖ͬͯ஋Λ؂ࢹ͢Δ • ୈҰҾ਺ʹ LifecycleOwner

    (Activity/Fragment)Λࢦఆ͢Δ͜ͱͰɺϥΠϑαΠΫ ϧʹΑΔ؂ࢹͷఀࢭ͕Ͱ͖Δ viewModel.repos.observe(this, Observer { //reposͷ಺༰͕มߋ͞ΕͨΒݺͼग़͞ΕΔϥϜμ Log.d("MainActivity", "Repository List = $it") })
  17. 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") })
  18. 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! }
  19. 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! }
  20. RxJava2ͷConsumerΠϯλϑΣʔε • Observer ͱಉ༷ͷ Single Abstract Method ΠϯλϑΣʔε • ͨͩ͠

    @Nullable ͸͍͍ͭͯͳ͍ //Java public interface Consumer<T> { void accept(T t) throws Exception; }
  21. ͓·͚ : 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()
  22. Architecture Components × Kotlin • Dagger2ͱ஥ྑ͘ͳΖ͏ • ֦ுؔ਺Ͱ͍͍ײ͡ʹ • Qiitaʹ͸ࢀߟͷϦϙδτϦ΋͋ΔͷͰͥͻ

    • Kotlin + Architecture Component + Dagger2ʹΑΔAndroidΞϓϦઃܭ (goo.gl/sQijNq) • Architecture Components + Flux (+ Kotlin)ʹΑΔAndroidΞϓϦઃܭ (goo.gl/tsUwbi )