From 1083075d91caf3ac38f5f9bfc4c41505cd8827d7 Mon Sep 17 00:00:00 2001 From: Arthur Ivanets Date: Fri, 17 May 2019 01:07:44 +0300 Subject: [PATCH] Improve the ViewModel creation approach. --- .../di/modules/activities/HostModule.kt | 8 +-- .../di/modules/fragments/CharactersModule.kt | 13 ++--- .../di/modules/fragments/ComicsModule.kt | 13 ++--- .../di/modules/fragments/DashboardModule.kt | 8 +-- .../di/modules/fragments/EventsModule.kt | 13 ++--- common/constants.gradle | 4 +- mvvm/build.gradle | 1 + .../mvvm/ViewModelProviderFactory.kt | 19 ++++--- .../mvvm/util/ViewModelProviderExtensions.kt | 49 +++++++++++++++++++ 9 files changed, 92 insertions(+), 36 deletions(-) create mode 100644 mvvm/src/main/java/com/arthurivanets/mvvm/util/ViewModelProviderExtensions.kt diff --git a/app/src/main/java/com/arthurivanets/sample/di/modules/activities/HostModule.kt b/app/src/main/java/com/arthurivanets/sample/di/modules/activities/HostModule.kt index 7db3cd1..a8a7f26 100644 --- a/app/src/main/java/com/arthurivanets/sample/di/modules/activities/HostModule.kt +++ b/app/src/main/java/com/arthurivanets/sample/di/modules/activities/HostModule.kt @@ -16,8 +16,7 @@ package com.arthurivanets.sample.di.modules.activities -import androidx.lifecycle.ViewModelProviders -import com.arthurivanets.mvvm.ViewModelProviderFactory +import com.arthurivanets.mvvm.util.provideViewModel import com.arthurivanets.sample.ui.host.HostActivity import com.arthurivanets.sample.ui.host.HostActivityViewModel import com.arthurivanets.sample.ui.host.HostActivityViewModelImpl @@ -30,8 +29,9 @@ class HostModule { @Provides fun provideHostViewModel(activity : HostActivity) : HostActivityViewModel { - val viewModelFactory = ViewModelProviderFactory(HostActivityViewModelImpl()) - return ViewModelProviders.of(activity, viewModelFactory).get(HostActivityViewModelImpl::class.java) + return activity.provideViewModel(HostActivityViewModelImpl::class.java) { + HostActivityViewModelImpl() + } } diff --git a/app/src/main/java/com/arthurivanets/sample/di/modules/fragments/CharactersModule.kt b/app/src/main/java/com/arthurivanets/sample/di/modules/fragments/CharactersModule.kt index 054c400..5220987 100644 --- a/app/src/main/java/com/arthurivanets/sample/di/modules/fragments/CharactersModule.kt +++ b/app/src/main/java/com/arthurivanets/sample/di/modules/fragments/CharactersModule.kt @@ -16,8 +16,7 @@ package com.arthurivanets.sample.di.modules.fragments -import androidx.lifecycle.ViewModelProviders -import com.arthurivanets.mvvm.ViewModelProviderFactory +import com.arthurivanets.mvvm.util.provideViewModel import com.arthurivanets.sample.domain.repositories.characters.CharactersRepository import com.arthurivanets.sample.ui.characters.info.CharacterInfoFragment import com.arthurivanets.sample.ui.characters.info.CharacterInfoViewModel @@ -35,16 +34,18 @@ class CharactersModule { @Provides fun provideCharactersViewModel(fragment : CharactersFragment, charactersRepository : CharactersRepository) : CharactersViewModel { - val viewModelFactory = ViewModelProviderFactory(CharactersViewModelImpl(charactersRepository)) - return ViewModelProviders.of(fragment, viewModelFactory).get(CharactersViewModelImpl::class.java) + return fragment.provideViewModel(CharactersViewModelImpl::class.java) { + CharactersViewModelImpl(charactersRepository) + } } @Provides fun provideCharacterInfoViewModel(fragment : CharacterInfoFragment, charactersRepository : CharactersRepository) : CharacterInfoViewModel { - val viewModelFactory = ViewModelProviderFactory(CharacterInfoViewModelImpl(charactersRepository)) - return ViewModelProviders.of(fragment, viewModelFactory).get(CharacterInfoViewModelImpl::class.java) + return fragment.provideViewModel(CharacterInfoViewModelImpl::class.java) { + CharacterInfoViewModelImpl(charactersRepository) + } } diff --git a/app/src/main/java/com/arthurivanets/sample/di/modules/fragments/ComicsModule.kt b/app/src/main/java/com/arthurivanets/sample/di/modules/fragments/ComicsModule.kt index 9c4530f..0aafbb3 100644 --- a/app/src/main/java/com/arthurivanets/sample/di/modules/fragments/ComicsModule.kt +++ b/app/src/main/java/com/arthurivanets/sample/di/modules/fragments/ComicsModule.kt @@ -16,8 +16,7 @@ package com.arthurivanets.sample.di.modules.fragments -import androidx.lifecycle.ViewModelProviders -import com.arthurivanets.mvvm.ViewModelProviderFactory +import com.arthurivanets.mvvm.util.provideViewModel import com.arthurivanets.sample.domain.repositories.comics.ComicsRepository import com.arthurivanets.sample.ui.comics.info.ComicsInfoFragment import com.arthurivanets.sample.ui.comics.info.ComicsInfoViewModel @@ -35,16 +34,18 @@ class ComicsModule { @Provides fun provideComicsViewModel(fragment : ComicsFragment, comicsRepository : ComicsRepository) : ComicsViewModel { - val viewModelFactory = ViewModelProviderFactory(ComicsViewModelImpl(comicsRepository)) - return ViewModelProviders.of(fragment, viewModelFactory).get(ComicsViewModelImpl::class.java) + return fragment.provideViewModel(ComicsViewModelImpl::class.java) { + ComicsViewModelImpl(comicsRepository) + } } @Provides fun provideComicsInfoViewModel(fragment : ComicsInfoFragment, comicsRepository : ComicsRepository) : ComicsInfoViewModel { - val viewModelFactory = ViewModelProviderFactory(ComicsInfoViewModelImpl(comicsRepository)) - return ViewModelProviders.of(fragment, viewModelFactory).get(ComicsInfoViewModelImpl::class.java) + return fragment.provideViewModel(ComicsInfoViewModelImpl::class.java) { + ComicsInfoViewModelImpl(comicsRepository) + } } diff --git a/app/src/main/java/com/arthurivanets/sample/di/modules/fragments/DashboardModule.kt b/app/src/main/java/com/arthurivanets/sample/di/modules/fragments/DashboardModule.kt index 369e8e4..79069c1 100644 --- a/app/src/main/java/com/arthurivanets/sample/di/modules/fragments/DashboardModule.kt +++ b/app/src/main/java/com/arthurivanets/sample/di/modules/fragments/DashboardModule.kt @@ -16,8 +16,7 @@ package com.arthurivanets.sample.di.modules.fragments -import androidx.lifecycle.ViewModelProviders -import com.arthurivanets.mvvm.ViewModelProviderFactory +import com.arthurivanets.mvvm.util.provideViewModel import com.arthurivanets.sample.ui.dashboard.DashboardFragment import com.arthurivanets.sample.ui.dashboard.DashboardViewModel import com.arthurivanets.sample.ui.dashboard.DashboardViewModelImpl @@ -30,8 +29,9 @@ class DashboardModule { @Provides fun provideDashboardViewModel(fragment : DashboardFragment) : DashboardViewModel { - val viewModelFactory = ViewModelProviderFactory(DashboardViewModelImpl()) - return ViewModelProviders.of(fragment, viewModelFactory).get(DashboardViewModelImpl::class.java) + return fragment.provideViewModel(DashboardViewModelImpl::class.java) { + DashboardViewModelImpl() + } } diff --git a/app/src/main/java/com/arthurivanets/sample/di/modules/fragments/EventsModule.kt b/app/src/main/java/com/arthurivanets/sample/di/modules/fragments/EventsModule.kt index c057ff5..79fdb77 100644 --- a/app/src/main/java/com/arthurivanets/sample/di/modules/fragments/EventsModule.kt +++ b/app/src/main/java/com/arthurivanets/sample/di/modules/fragments/EventsModule.kt @@ -16,8 +16,7 @@ package com.arthurivanets.sample.di.modules.fragments -import androidx.lifecycle.ViewModelProviders -import com.arthurivanets.mvvm.ViewModelProviderFactory +import com.arthurivanets.mvvm.util.provideViewModel import com.arthurivanets.sample.domain.repositories.events.EventsRepository import com.arthurivanets.sample.ui.events.info.EventInfoFragment import com.arthurivanets.sample.ui.events.info.EventInfoViewModel @@ -35,16 +34,18 @@ class EventsModule { @Provides fun provideEventsViewModel(fragment : EventsFragment, eventsRepository : EventsRepository) : EventsViewModel { - val viewModelFactory = ViewModelProviderFactory(EventsViewModelImpl(eventsRepository)) - return ViewModelProviders.of(fragment, viewModelFactory).get(EventsViewModelImpl::class.java) + return fragment.provideViewModel(EventsViewModelImpl::class.java) { + EventsViewModelImpl(eventsRepository) + } } @Provides fun provideEventInfoViewModel(fragment : EventInfoFragment, eventsRepository : EventsRepository) : EventInfoViewModel { - val viewModelFactory = ViewModelProviderFactory(EventInfoViewModelImpl(eventsRepository)) - return ViewModelProviders.of(fragment, viewModelFactory).get(EventInfoViewModelImpl::class.java) + return fragment.provideViewModel(EventInfoViewModelImpl::class.java) { + EventInfoViewModelImpl(eventsRepository) + } } diff --git a/common/constants.gradle b/common/constants.gradle index 31ca308..0f63c57 100644 --- a/common/constants.gradle +++ b/common/constants.gradle @@ -52,8 +52,8 @@ project.ext { releaseRepoName = "maven" releaseUserOrg = "arthurlabs" releaseGroupId = "com.arthurivanets.mvvm" - releaseVersion = "1.0.0" - releaseVersionCode = 1 + releaseVersion = "1.1.0" + releaseVersionCode = 2 releaseWebsite = "https://github.com/arthur3486/android-mvvm" releaseLicense = ["Apache-2.0"] diff --git a/mvvm/build.gradle b/mvvm/build.gradle index 820fe00..56ecd1a 100644 --- a/mvvm/build.gradle +++ b/mvvm/build.gradle @@ -56,6 +56,7 @@ dependencies { implementation kotlinDependencies implementation androidCoreDependencies + implementation lifecycleDependencies implementation rxJavaDependencies implementation rxBusDependencies diff --git a/mvvm/src/main/java/com/arthurivanets/mvvm/ViewModelProviderFactory.kt b/mvvm/src/main/java/com/arthurivanets/mvvm/ViewModelProviderFactory.kt index 5e48d63..9bb2c53 100644 --- a/mvvm/src/main/java/com/arthurivanets/mvvm/ViewModelProviderFactory.kt +++ b/mvvm/src/main/java/com/arthurivanets/mvvm/ViewModelProviderFactory.kt @@ -24,17 +24,20 @@ import androidx.lifecycle.ViewModelProvider *
* Allows you to construct the [ViewModel] that meets your requirements (e.g. perform the dependency injection into your [ViewModel]). */ -class ViewModelProviderFactory(private val viewModel : V) : ViewModelProvider.Factory { - - +class ViewModelProviderFactory( + private val viewModelClass : Class, + private val creator : () -> V +) : ViewModelProvider.Factory { + + @SuppressWarnings("unchecked") override fun create(modelClass : Class) : T { - if(!modelClass.isAssignableFrom(viewModel::class.java)) { + if(!modelClass.isAssignableFrom(viewModelClass)) { throw IllegalArgumentException("Unsupported class name.") } - - return (viewModel as T) + + return (creator() as T) } - - + + } \ No newline at end of file diff --git a/mvvm/src/main/java/com/arthurivanets/mvvm/util/ViewModelProviderExtensions.kt b/mvvm/src/main/java/com/arthurivanets/mvvm/util/ViewModelProviderExtensions.kt new file mode 100644 index 0000000..fb75406 --- /dev/null +++ b/mvvm/src/main/java/com/arthurivanets/mvvm/util/ViewModelProviderExtensions.kt @@ -0,0 +1,49 @@ +/* + * Copyright 2018 Arthur Ivanets, arthur.ivanets.l@gmail.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@file:JvmName("ViewModelProviderUtils") + +package com.arthurivanets.mvvm.util + +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentActivity +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProviders +import com.arthurivanets.mvvm.ViewModelProviderFactory + + +/** + * Provides the [ViewModel] of the specified class using the [ViewModelProviders] and a custom [ViewModelProviderFactory], + * thus allowing you to construct a [ViewModel] according to your requirements (e.g. inject the dependencies into it, etc.). + * The creation of a new [ViewModel] instance using the [creator] will happen only if the [ViewModelProviders] doesn't have + * a retained version of the corresponding [ViewModel] instance, otherwise the existing (retained) [ViewModel] will be reused. + */ +fun FragmentActivity.provideViewModel(viewModelClass : Class, creator : () -> T) : T { + val viewModelFactory = ViewModelProviderFactory(viewModelClass, creator) + return ViewModelProviders.of(this, viewModelFactory).get(viewModelClass) +} + + +/** + * Provides the [ViewModel] of the specified class using the [ViewModelProviders] and a custom [ViewModelProviderFactory], + * thus allowing you to construct a [ViewModel] according to your requirements (e.g. inject the dependencies into it, etc.). + * The creation of a new [ViewModel] instance using the [creator] will happen only if the [ViewModelProviders] doesn't have + * a retained version of the corresponding [ViewModel] instance, otherwise the existing (retained) [ViewModel] will be reused. + */ +fun Fragment.provideViewModel(viewModelClass : Class, creator : () -> T) : T { + val viewModelFactory = ViewModelProviderFactory(viewModelClass, creator) + return ViewModelProviders.of(this, viewModelFactory).get(viewModelClass) +} \ No newline at end of file