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

Kotlin x Architecture Components

satorufujiwara
January 18, 2018
1.2k

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. Kotlin × Architecture
    Components ࣮ફ
    2018.01.18 Ͳ͜Ͱ΋Kotlin #5
    @satorufujiwara

    View Slide

  2. About me
    Satoru Fujiwara (@satorufujiwara)
    Android Engineer @CyberAgent ,Inc
    Organizer of Shibuya.apk
    ༁ॻ : Kotlin ΠϯɾΞΫγϣϯ
    ஶॻ : Android ΞϓϦઃܭύλʔϯೖ໳

    View Slide

  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೔ग़൛)

    View Slide

  4. @satofurujiwara
    Kotlin × Architecture
    Components ࣮ફ
    4

    View Slide

  5. Architecture Componentsͱ͸
    • AndroidͷΞʔΩςΫνϟͷͨΊͷϥΠϒϥϦ܈
    • Google I/0 2017ͰॳΊͯЋ൛͕ެ։͞ΕΔ
    • 2017೥11݄6೔ʹ҆ఆ൛ͱͳΔ 1.0.0͕ެ։͞Ε͍ͯΔ
    • ʮݎ࿚Ͱɺςετ͕͠΍͘͢ɺอक͠΍͍͢ΞϓϦʯΛ࡞Δ

    View Slide

  6. Architecture ComponentsͷϞδϡʔϧ
    • LifeCycles - Activity΍FragmentͷϥΠϑαΠΫϧΛѻ͏ɺίΞͱͳΔϥΠϒϥϦ
    • ViewModel - UIʹؔ࿈ͨ͠σʔλΛอ࣋ɺ؅ཧ͢ΔͨΊͷΫϥε
    • LiveData - ObserverύλʔϯΛ࣋ͭσʔλอ࣋༻ͷΫϥε
    • Room - SQLiteͷϥούʔ
    • Paging - σʔλΛෳ਺ճʹ෼͚ͯಡΈࠐΉ
    ্هͷ͏ͪඞཁͳ΋ͷΛ૊Έ߹Θͤͯ࢖͏

    View Slide

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

    View Slide

  8. ࠓ೔ͷ࿩ͷείʔϓ
    • (LifeCycles)
    • ViewModel
    • LiveData
    ओʹViewModelͱLiveDataΛKotlinͰ࢖͏৔߹ͷ࿩Λ͠·͢

    View Slide

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

    View Slide

  10. ViewModelͷऔಘ
    • ඞͣ ViewModelProvider::get Λ༻͍ͯऔಘ͢Δ
    • ViewModelProvider ͸ ViewModelProviders::of Ͱऔಘ͢Δ
    val viewModelProvider: ViewModelProvider = ViewModelProviders.of(this)
    //ViewModelΛܧঝͨ͠ΫϥεΛࢦఆ͢Δ
    val viewModel: MainViewModel = viewModelProvider.get(MainViewModel::class.java)

    View Slide

  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)

    View Slide

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

    View Slide

  13. DefaultFactory
    • Class::newInstance Λݺͼग़ͯ͠ΠϯελϯεΛੜ੒͢Δ
    • AndroidViewModelΛܧঝ͍ͯ͠Δ৔߹͸
    • ίϯετϥΫλʹҾ਺Λ༩͍͑ͨ৔߹͸ಠࣗͷ Factory Λ࢖͏ඞ
    ཁ͕͋Δ
    modelClass.getConstructor(Application.class).newInstance(mApplication)

    View Slide

  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 create(Class 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());
    }
    }

    View Slide

  15. ViewModelProvider.Factory
    • ಠࣗͰΠϯελϯε؅ཧ͢Δͷ͸େม
    • Dagger2(+Android֦ு)Λ࢖͍·͠ΐ͏
    • Architecture Components × Dagger2 × Kotlin ͸ϋϚΓϙΠϯτ
    ͕͍͔ͭ͘…
    • (Α͏΍͘Kotlinͷ࿩)

    View Slide

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

    View Slide

  17. Dagger2 Λ༻͍ͨViewModelProvider.Factory
    • Dagger2 ͷ MultiBinding ػೳΛ࢖͏
    • ಉ͡ΫϥεͰ͋ͬͯ΋ Key Λࢦఆ͢Δ͜ͱͰɺͦΕʹରԠͨ͠
    ΠϯελϯεΛऔಘͰ͖Δ
    @Module
    internal abstract class MainModule {
    @Binds
    @IntoMap
    @ViewModelKey(MainViewModel::class) //Key໊ʹରԠ͢ΔΠϯελϯε͸Ҿ਺
    abstract fun bindMainViewModel(viewModel: MainViewModel): ViewModel
    }

    View Slide

  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, Provider>
    getMapOfClassOfAndProviderOfViewModel() {
    return ImmutableMap., Provider>of(
    MainViewModel.class, //Key໊
    (Provider) mainViewModelProvider //ରԠ͢Δ஋
    );
    }

    View Slide

  19. Dagger2 Λ༻͍ͨViewModelProvider.Factory
    • Dagger2 ʹΑͬͯੜ੒͞ΕΔ Map,
    Provider> ͔Β ViewModel ΛऔΓग़͢ FactoryΛ࡞
    Ε͹ྑ͍

    View Slide

  20. class ViewModelFactory @Inject constructor(
    private val creators: Map,
    @JvmSuppressWildcards Provider> // @JvmSuppressWildcards͕ඞਢ
    ) : ViewModelProvider.Factory {
    @Suppress("UNCHECKED_CAST")
    override fun create(modelClass: Class): T {
    var creator: Provider? = 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)
    }
    }
    }

    View Slide

  21. //Kotlin
    class ViewModelFactory
    @Inject constructor(private val creators:
    Map, Provider>)

    View Slide

  22. //Kotlin
    class ViewModelFactory
    @Inject constructor(private val creators:
    Map, Provider>)
    //Java expected
    Map, Provider>

    View Slide

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

    View Slide

  24. //Kotlin
    class ViewModelFactory
    @Inject constructor(private val creators:
    Map, @JvmSuppressWildcards Provider>)

    View Slide

  25. //Kotlin
    class ViewModelFactory
    @Inject constructor(private val creators:
    Map, @JvmSuppressWildcards Provider>)
    //Java expected
    Map, Provider>
    //Java actual : ظ଴Ͳ͓Γ
    Map, Provider>

    View Slide

  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 // ؒҧ͍

    View Slide

  27. ࢀߟ : ௚઀@Inject͚ͭͯ͏·͍͘͘ํ๏΋͋Δ ( goo.gl/9tZQkm )

    View Slide

  28. @satofurujiwara
    LiveData ͱ Kotlin
    28

    View Slide

  29. LiveDataͱ͸
    • ObserverύλʔϯΛ࣋ͭσʔλอ࣋༻ͷΫϥε
    • ؔ਺ observe Λ࢖ͬͯ஋Λ؂ࢹ͢Δ
    • ୈҰҾ਺ʹ LifecycleOwner (Activity/Fragment)Λࢦఆ͢Δ͜ͱͰɺϥΠϑαΠΫ
    ϧʹΑΔ؂ࢹͷఀࢭ͕Ͱ͖Δ
    viewModel.repos.observe(this, Observer {
    //reposͷ಺༰͕มߋ͞ΕͨΒݺͼग़͞ΕΔϥϜμ
    Log.d("MainActivity", "Repository List = $it")
    })

    View Slide

  30. LiveData::observe
    • ֦ுؔ਺Λఆٛ͢Δ͜ͱͰͪΐͬͱ͍͍ײ͡ʹͳΔ
    fun LiveData.observeBy(owner: LifecycleOwner, observer: (T?) -> Unit) = observe(owner,
    Observer {
    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")
    })

    View Slide

  31. LiveData::observe
    • ࣮͸ઌ΄Ͳͷ֦ுؔ਺ͷؔ਺ܕͷҾ਺ observer ͷҾ਺͸ T Ͱ͸
    ͳ͘ T? (ৗʹNullable)
    fun LiveData.observeBy(owner: LifecycleOwner, observer: (T?) -> Unit) = observe(owner, Observer {
    observer.invoke(it)
    })
    fun LiveData.observeBy(owner: LifecycleOwner, observer: (T) -> Unit) = observe(owner, Observer {
    observer.invoke(it) // Error!
    })
    viewModel.repos.observeBy(this) {
    val l = it.length //Error!
    }

    View Slide

  32. Observer
    • Observer ΠϯλϑΣʔεͷϝιουͷҾ਺ʹ @Nullable ͕͍ͭ
    ͍ͯΔ͔Β
    //java
    public interface Observer {
    void onChanged(@Nullable T t);
    }

    View Slide

  33. Observer಺Ͱͷnull-check
    • ֦ுؔ਺಺Ͱnull-checkΛ͢Ε͹͍͍ײ͡ʹ
    fun LiveData.observeNonNullBy(owner: LifecycleOwner, observer: (T) -> Unit) =
    observe(owner, Observer {
    it ?: [email protected]
    observer.invoke(it)
    })
    viewModel.repos.observeBy(this) {
    val l = it.length //OK!
    }

    View Slide

  34. ࢀߟݩ

    View Slide

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

    View Slide

  36. RxJava2ͷConsumerΠϯλϑΣʔε
    • Observer ͱಉ༷ͷ Single Abstract Method ΠϯλϑΣʔε
    • ͨͩ͠ @Nullable ͸͍͍ͭͯͳ͍
    //Java
    public interface Consumer {
    void accept(T t) throws Exception;
    }

    View Slide

  37. ܕύϥϝʔλʹNonNull/NullableΛࢦఆͨ͠ͱ͖ͷڍಈ

    View Slide

  38. JavaͰఆٛ͞ΕͨܕύϥϝʔλͱNullableܕ
    • NullableܕΛࢦఆ͢Ε͹NullableܕɺNon-nullܕΛࢦఆ͢Ε͹ϓ
    ϥοτϑΥʔϜܕ
    • ʮܕύϥϝʔλ͸ɺnull ڐ༰ܕͷܕͷఆٛʹ͸࠷ޙʹٙ໰ූ͕ඞ
    ཁͰ͋Γɺٙ໰ූ͕ͳ͍ܕ͸ null ඇڐ༰ܕͰ͋Δͱ͍͏ϧʔϧ
    ʹରͯ͠ͷ།Ұͷྫ֎ʯ(Kotlin ΠϯɾΞΫγϣϯ 6.1.10)

    View Slide

  39. ܕύϥϝʔλΛNonNullʹ͍ͨ͠৔߹͸
    • ܕύϥϝʔλʹ্ݶڥքͱͯ͠AnyܕΛࢦఆ͢Δ ( T:Any )

    View Slide

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

    View Slide

  41. ͓·͚ : RxJava2 to LiveData
    • LiveData ͷ RxJava ༻֦ுΛಋೖ͢ΔࡍʹҎԼͷΑ͏ʹ֦ுؔ਺
    Λఆٛ͢Ε͹ɺϝιουνΣΠϯ͕ܨ͕Γ͍͍ײ͡ʹͳΔ
    fun Publisher.toLiveData() = LiveDataReactiveStreams.fromPublisher(this)
    val repos: LiveData> = dispatcher.onRefreshRepo
    .map { it.data }
    .observeOn(AndroidSchedulers.mainThread()) //͜͜·Ͱ͸Flowable
    .toLiveData()

    View Slide

  42. Architecture Components × Kotlin
    • Dagger2ͱ஥ྑ͘ͳΖ͏
    • ֦ுؔ਺Ͱ͍͍ײ͡ʹ
    • Qiitaʹ͸ࢀߟͷϦϙδτϦ΋͋ΔͷͰͥͻ
    • Kotlin + Architecture Component + Dagger2ʹΑΔAndroidΞϓϦઃܭ (goo.gl/sQijNq)
    • Architecture Components + Flux (+ Kotlin)ʹΑΔAndroidΞϓϦઃܭ (goo.gl/tsUwbi )

    View Slide

  43. Thank you!
    Have a Nice Kotlin!

    View Slide