Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

@satofurujiwara Kotlin × Architecture Components ࣮ફ 4

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

// ಋೖํ๏ // 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൛ }

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

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)

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

// ಋೖํ๏ // 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" }

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

@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 //ରԠ͢Δ஋ ); }

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

//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

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

@satofurujiwara LiveData ͱ Kotlin 28

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

ࢀߟݩ

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

Thank you! Have a Nice Kotlin!