Share
Embed
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
Transcript
43 slides
-
About me Satoru Fujiwara (@satorufujiwara) Android Engineer @CyberAgent ,Inc Organizer
of Shibuya.apk ༁ॻ : Kotlin ΠϯɾΞΫγϣϯ ஶॻ : Android ΞϓϦઃܭύλʔϯೖ -
Kotlin & Me FRESH! ΛKotlinͰ։ൃ (20153݄ʙ) 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 ΠϯɾΞΫγϣϯ (༁ 201710݄31ग़൛) -
Architecture Componentsͱ • AndroidͷΞʔΩςΫνϟͷͨΊͷϥΠϒϥϦ܈ • Google I/0 2017ͰॳΊͯЋ൛͕ެ։͞ΕΔ • 201711݄6ʹ҆ఆ൛ͱͳΔ
1.0.0͕ެ։͞Ε͍ͯΔ • ʮݎ࿚Ͱɺςετ͕͘͢͠ɺอक͍͢͠ΞϓϦʯΛ࡞Δ -
Architecture ComponentsͷϞδϡʔϧ • LifeCycles - ActivityFragmentͷϥΠϑαΠΫϧΛѻ͏ɺίΞͱͳΔϥΠϒϥϦ • ViewModel - UIʹؔ࿈ͨ͠σʔλΛอ࣋ɺཧ͢ΔͨΊͷΫϥε
• LiveData - ObserverύλʔϯΛ࣋ͭσʔλอ࣋༻ͷΫϥε • Room - SQLiteͷϥούʔ • Paging - σʔλΛෳճʹ͚ͯಡΈࠐΉ ্هͷ͏ͪඞཁͳͷΛΈ߹Θͤͯ͏ -
// ಋೖํ๏ // 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൛ } -
ViewModelͷऔಘ • ඞͣ ViewModelProvider::get Λ༻͍ͯऔಘ͢Δ • ViewModelProvider ViewModelProviders::of Ͱऔಘ͢Δ
val viewModelProvider: ViewModelProvider = ViewModelProviders.of(this) //ViewModelΛܧঝͨ͠ΫϥεΛࢦఆ͢Δ val viewModel: MainViewModel = viewModelProvider.get(MainViewModel::class.java) -
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) -
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); } -
DefaultFactory • Class::newInstance Λݺͼग़ͯ͠ΠϯελϯεΛੜ͢Δ • AndroidViewModelΛܧঝ͍ͯ͠Δ߹ • ίϯετϥΫλʹҾΛ༩͍͑ͨ߹ಠࣗͷ Factory Λ͏ඞ
ཁ͕͋Δ modelClass.getConstructor(Application.class).newInstance(mApplication) -
//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()); } } -
ViewModelProvider.Factory • ಠࣗͰΠϯελϯεཧ͢Δͷେม • Dagger2(+Android֦ு)Λ͍·͠ΐ͏ • Architecture Components × Dagger2
× Kotlin ϋϚΓϙΠϯτ ͕͍͔ͭ͘… • (Α͏͘Kotlinͷ) -
// ಋೖํ๏ // 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" } -
Dagger2 Λ༻͍ͨViewModelProvider.Factory • Dagger2 ͷ MultiBinding ػೳΛ͏ • ಉ͡ΫϥεͰ͋ͬͯ Key
Λࢦఆ͢Δ͜ͱͰɺͦΕʹରԠͨ͠ ΠϯελϯεΛऔಘͰ͖Δ @Module internal abstract class MainModule { @Binds @IntoMap @ViewModelKey(MainViewModel::class) //Key໊ʹରԠ͢ΔΠϯελϯεҾ abstract fun bindMainViewModel(viewModel: MainViewModel): ViewModel } -
@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 //ରԠ͢Δ ); } -
Dagger2 Λ༻͍ͨViewModelProvider.Factory • Dagger2 ʹΑͬͯੜ͞ΕΔ Map<Class<? extends ViewModel>, Provider<ViewModel>> ͔Β
ViewModel ΛऔΓग़͢ FactoryΛ࡞ Εྑ͍ -
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) } } } -
//Kotlin class ViewModelFactory @Inject constructor(private val creators: Map<Class<out ViewModel>, Provider<ViewModel>>)
//Java expected Map<Class<? extends ViewModel>, Provider<ViewModel>> -
//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> -
//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>> -
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 // ؒҧ͍ -
LiveDataͱ • ObserverύλʔϯΛ࣋ͭσʔλอ࣋༻ͷΫϥε • ؔ observe ΛͬͯΛࢹ͢Δ • ୈҰҾʹ LifecycleOwner
(Activity/Fragment)Λࢦఆ͢Δ͜ͱͰɺϥΠϑαΠΫ ϧʹΑΔࢹͷఀࢭ͕Ͱ͖Δ viewModel.repos.observe(this, Observer { //reposͷ༰͕มߋ͞ΕͨΒݺͼग़͞ΕΔϥϜμ Log.d("MainActivity", "Repository List = $it") }) -
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") }) -
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! } -
Observer • Observer ΠϯλϑΣʔεͷϝιουͷҾʹ @Nullable ͕͍ͭ ͍ͯΔ͔Β //java public interface
Observer<T> { void onChanged(@Nullable T t); } -
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! } -
RxJava2ͷConsumerΠϯλϑΣʔε • Observer ͱಉ༷ͷ Single Abstract Method ΠϯλϑΣʔε • ͨͩ͠
@Nullable ͍͍ͭͯͳ͍ //Java public interface Consumer<T> { void accept(T t) throws Exception; } -
͓·͚ : 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() -
Architecture Components × Kotlin • Dagger2ͱྑ͘ͳΖ͏ • ֦ுؔͰ͍͍ײ͡ʹ • QiitaʹࢀߟͷϦϙδτϦ͋ΔͷͰͥͻ
• Kotlin + Architecture Component + Dagger2ʹΑΔAndroidΞϓϦઃܭ (goo.gl/sQijNq) • Architecture Components + Flux (+ Kotlin)ʹΑΔAndroidΞϓϦઃܭ (goo.gl/tsUwbi )