Android ViewModel 附加参数
Android ViewModel additional arguments
除了应用程序上下文之外,是否可以将其他参数传递给我的自定义 AndroidViewModel
构造函数。
示例:
public class MyViewModel extends AndroidViewModel {
private final LiveData<List<MyObject>> myObjectList;
private AppDatabase appDatabase;
public MyViewModel(Application application, String param) {
super(application);
appDatabase = AppDatabase.getDatabase(this.getApplication());
myObjectList = appDatabase.myOjectModel().getMyObjectByParam(param);
}
}
当我想使用我的自定义 ViewModel
class 我在我的片段中使用此代码:
MyViewModel myViewModel = ViewModelProvider.of(this).get(MyViewModel.class)
所以我不知道如何将附加参数 String param
传递到我的自定义 ViewModel
中。我只能传递应用程序上下文,但不能传递其他参数。我真的很感激任何帮助。谢谢你。
编辑:我添加了一些代码。希望现在好点了。
您的 ViewModel 需要一个工厂 class。
public class MyViewModelFactory implements ViewModelProvider.Factory {
private Application mApplication;
private String mParam;
public MyViewModelFactory(Application application, String param) {
mApplication = application;
mParam = param;
}
@Override
public <T extends ViewModel> T create(Class<T> modelClass) {
return (T) new MyViewModel(mApplication, mParam);
}
}
在实例化视图模型时,您可以这样做:
MyViewModel myViewModel = ViewModelProvider(this, new MyViewModelFactory(this.getApplication(), "my awesome param")).get(MyViewModel.class);
对于 kotlin,你可以使用委托 属性:
val viewModel: MyViewModel by viewModels { MyViewModelFactory(getApplication(), "my awesome param") }
还有另一个新选项 - 实现 HasDefaultViewModelProviderFactory
并用你的工厂实例化覆盖 getDefaultViewModelProviderFactory()
然后你可以在没有工厂的情况下调用 ViewModelProvider(this)
或 by viewModels()
.
对于在多个不同视图模型之间共享的一个工厂,我会像这样扩展 mlyko 的答案:
public class MyViewModelFactory extends ViewModelProvider.NewInstanceFactory {
private Application mApplication;
private Object[] mParams;
public MyViewModelFactory(Application application, Object... params) {
mApplication = application;
mParams = params;
}
@Override
public <T extends ViewModel> T create(Class<T> modelClass) {
if (modelClass == ViewModel1.class) {
return (T) new ViewModel1(mApplication, (String) mParams[0]);
} else if (modelClass == ViewModel2.class) {
return (T) new ViewModel2(mApplication, (Integer) mParams[0]);
} else if (modelClass == ViewModel3.class) {
return (T) new ViewModel3(mApplication, (Integer) mParams[0], (String) mParams[1]);
} else {
return super.create(modelClass);
}
}
}
并实例化视图模型:
ViewModel1 vm1 = ViewModelProviders.of(this, new MyViewModelFactory(getApplication(), "something")).get(ViewModel1.class);
ViewModel2 vm2 = ViewModelProviders.of(this, new MyViewModelFactory(getApplication(), 123)).get(ViewModel2.class);
ViewModel3 vm3 = ViewModelProviders.of(this, new MyViewModelFactory(getApplication(), 123, "something")).get(ViewModel3.class);
不同的视图模型具有不同的构造函数。
我写了一个库,它应该使这样做更直接、更清晰,不需要多重绑定或工厂样板,同时与 ViewModel 参数无缝工作,这些参数可以由 Dagger 作为依赖项提供:
https://github.com/radutopor/ViewModelFactory
@ViewModelFactory
class UserViewModel(@Provided repository: Repository, userId: Int) : ViewModel() {
val greeting = MutableLiveData<String>()
init {
val user = repository.getUser(userId)
greeting.value = "Hello, $user.name"
}
}
在视图中:
class UserActivity : AppCompatActivity() {
@Inject
lateinit var userViewModelFactory2: UserViewModelFactory2
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_user)
appComponent.inject(this)
val userId = intent.getIntExtra("USER_ID", -1)
val viewModel = ViewModelProviders.of(this, userViewModelFactory2.create(userId))
.get(UserViewModel::class.java)
viewModel.greeting.observe(this, Observer { greetingText ->
greetingTextView.text = greetingText
})
}
}
(KOTLIN) 我的解决方案使用了一点反射。
假设您不想在每次创建需要一些参数的新 ViewModel class 时都创建外观相同的工厂 class。您可以通过反射来完成此操作。
例如,您将有两个不同的活动:
class Activity1 : FragmentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val args = Bundle().apply { putString("NAME_KEY", "Vilpe89") }
val viewModel = ViewModelProviders
.of(this, ViewModelWithArgumentsFactory(args))
.get(ViewModel1::class.java)
}
}
class Activity2 : FragmentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val args = Bundle().apply { putInt("AGE_KEY", 29) }
val viewModel = ViewModelProviders
.of(this, ViewModelWithArgumentsFactory(args))
.get(ViewModel2::class.java)
}
}
以及这些活动的 ViewModel:
class ViewModel1(private val args: Bundle) : ViewModel()
class ViewModel2(private val args: Bundle) : ViewModel()
然后是神奇的部分,工厂 class 的实现:
class ViewModelWithArgumentsFactory(private val args: Bundle) : NewInstanceFactory() {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
try {
val constructor: Constructor<T> = modelClass.getDeclaredConstructor(Bundle::class.java)
return constructor.newInstance(args)
} catch (e: Exception) {
Timber.e(e, "Could not create new instance of class %s", modelClass.canonicalName)
throw e
}
}
}
为什么不这样做:
public class MyViewModel extends AndroidViewModel {
private final LiveData<List<MyObject>> myObjectList;
private AppDatabase appDatabase;
private boolean initialized = false;
public MyViewModel(Application application) {
super(application);
}
public initialize(String param){
synchronized ("justInCase") {
if(! initialized){
initialized = true;
appDatabase = AppDatabase.getDatabase(this.getApplication());
myObjectList = appDatabase.myOjectModel().getMyObjectByParam(param);
}
}
}
}
然后分两步使用:
MyViewModel myViewModel = ViewModelProvider.of(this).get(MyViewModel.class)
myViewModel.initialize(param)
我把它设为class,其中传递了已创建的对象。
private Map<String, ViewModel> viewModelMap;
public ViewModelFactory() {
this.viewModelMap = new HashMap<>();
}
public void add(ViewModel viewModel) {
viewModelMap.put(viewModel.getClass().getCanonicalName(), viewModel);
}
@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
for (Map.Entry<String, ViewModel> viewModel : viewModelMap.entrySet()) {
if (viewModel.getKey().equals(modelClass.getCanonicalName())) {
return (T) viewModel.getValue();
}
}
return null;
}
然后
ViewModelFactory viewModelFactory = new ViewModelFactory();
viewModelFactory.add(new SampleViewModel(arg1, arg2));
SampleViewModel sampleViewModel = ViewModelProviders.of(this, viewModelFactory).get(SampleViewModel.class);
使用依赖注入实现
这更高级,更适合生产代码。
Dagger2,方块AssistedInject offers a production-ready implementation for ViewModels that can inject necessary components such as a repository that handles network and database requests. It also allows for the manual injection of arguments/parameters in the activity/fragment. Here's a concise outline of the steps to implement with code Gists based on Gabor Varadi's detailed post, Dagger Tips.
Dagger Hilt 是下一代解决方案,自 2020 年 7 月 12 日起处于 alpha 状态,一旦库可用,便会提供相同的用例和更简单的设置处于发布状态。
在 Kotlin
中使用 Lifecycle 2.2.0 实现
路过Arguments/Parameters
// Override ViewModelProvider.NewInstanceFactory to create the ViewModel (VM).
class SomeViewModelFactory(private val someString: String): ViewModelProvider.NewInstanceFactory() {
override fun <T : ViewModel?> create(modelClass: Class<T>): T = SomeViewModel(someString) as T
}
class SomeViewModel(private val someString: String) : ViewModel() {
init {
//TODO: Use 'someString' to init process when VM is created. i.e. Get data request.
}
}
class Fragment: Fragment() {
// Create VM in activity/fragment with VM factory.
val someViewModel: SomeViewModel by viewModels { SomeViewModelFactory("someString") }
}
使用 Arguments/Parameters
启用 SavedState
class SomeViewModelFactory(
private val owner: SavedStateRegistryOwner,
private val someString: String) : AbstractSavedStateViewModelFactory(owner, null) {
override fun <T : ViewModel?> create(key: String, modelClass: Class<T>, state: SavedStateHandle) =
SomeViewModel(state, someString) as T
}
class SomeViewModel(private val state: SavedStateHandle, private val someString: String) : ViewModel() {
val feedPosition = state.get<Int>(FEED_POSITION_KEY).let { position ->
if (position == null) 0 else position
}
init {
//TODO: Use 'someString' to init process when VM is created. i.e. Get data request.
}
fun saveFeedPosition(position: Int) {
state.set(FEED_POSITION_KEY, position)
}
}
class Fragment: Fragment() {
// Create VM in activity/fragment with VM factory.
val someViewModel: SomeViewModel by viewModels { SomeViewModelFactory(this, "someString") }
private var feedPosition: Int = 0
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
someViewModel.saveFeedPosition((contentRecyclerView.layoutManager as LinearLayoutManager)
.findFirstVisibleItemPosition())
}
override fun onViewStateRestored(savedInstanceState: Bundle?) {
super.onViewStateRestored(savedInstanceState)
feedPosition = someViewModel.feedPosition
}
}
基于@vilpe89 AndroidViewModel案例的上述Kotlin解决方案
class ExtraParamsViewModelFactory(
private val application: Application,
private val myExtraParam: String
): ViewModelProvider.NewInstanceFactory() {
override fun <T : ViewModel?> create(modelClass: Class<T>): T =
SomeViewModel(application, myExtraParam) as T
}
然后片段可以启动 viewModel 为
class SomeFragment : Fragment() {
// ...
private val myViewModel: SomeViewModel by viewModels {
ExtraParamsViewModelFactory(this.requireActivity().application, "some string value")
}
// ...
}
然后是实际的 ViewModel class
class SomeViewModel(application: Application, val myExtraParam:String) : AndroidViewModel(application) {
// ...
}
或者以某种合适的方法...
override fun onActivityCreated(...){
// ...
val myViewModel = ViewModelProvider(this, ExtraParamsViewModelFactory(this.requireActivity().application, "some string value")).get(SomeViewModel::class.java)
// ...
}
class UserViewModelFactory(private val context: Context) : ViewModelProvider.NewInstanceFactory() {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return UserViewModel(context) as T
}
}
class UserViewModel(private val context: Context) : ViewModel() {
private var listData = MutableLiveData<ArrayList<User>>()
init{
val userRepository : UserRepository by lazy {
UserRepository
}
if(context.isInternetAvailable()) {
listData = userRepository.getMutableLiveData(context)
}
}
fun getData() : MutableLiveData<ArrayList<User>>{
return listData
}
在Activity
中调用Viewmodel
val userViewModel = ViewModelProviders.of(this,UserViewModelFactory(this)).get(UserViewModel::class.java)
在Kotlin中,由于ViewModel
的调用者和ViewModel
本身运行在不同的协程中,所以使用[=13在它们之间传递数据更自然方便=]:
class NewViewModel : ViewModel() {
private val newData: MutableLiveData<Service.DataEntry?> by lazy {
MutableLiveData<Service.DataEntry?>().also {
viewModelScope.launch {
val channel = Service.ParamChannel // type Channel<Params>
val params = channel.receive()
it.value = Service.postSomething(params)
}
}
}
fun getData(): LiveData<Service.DataEntry?> {
return newData
}
}
// Calling code:
val model: NewViewModel by viewModels()
model.getData().observe(this) { newData ->
if (newData != null) {
...
}
else
{
...
}
}
runBlocking {
Service.ParamChannel.send(theParams)
}
这是工作代码的一部分,出于演示目的我将其匿名化。
正确的方法是使用依赖注入框架,例如Dagger hilt。如果不使用 DI 框架,则使用 ViewModelFactory。
带匕首柄:
带参数的 ViewModel
@HiltViewModel
class MyViewModel @Inject constructor(
private val myRepository: MyRepository,
private val savedStateHandle: SavedStateHandle
) : ViewModel() { ... }
一个存储库
class MyRepository @Inject constructor(
private val myRemoteDataSource: MyDataSource,
private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO
) { ... }
一个提供 dependencies/parameters 的模块,因此可以将它们注入存储库和 ViewModel。
@InstallIn(ViewModelComponent::class)
@Module
object MyProvideModule {
@Provides
fun provideMyDataSource(@ApplicationContext context: Context): MyDataSource {
//code to create MyDataSource...
return MyDataSource(context)
}
@Provides
fun provideCoroutineDispatcher(): CoroutineDispatcher {
return Dispatchers.IO
}
}
用于绑定存储库的模块
@Module
@InstallIn(ViewModelComponent::class)
interface RepositoryModules {
@Binds
fun provideMyRepository(repository: MyRepository): MyRepository
}
使用带有@HiltAndroidApp 注释的应用程序启动 Dagger 刀柄。
@HiltAndroidApp
class MainApplication : Application() {
override fun onCreate() {
super.onCreate()
}
}
在活动中获取 ViewModel
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
private val myViewModel: MyViewModel by viewModels()
// Other code...
}
在片段中获取 ViewModel
@AndroidEntryPoint
class MyFragment : Fragment() {
private val myViewModel: MyViewModel by activityViewModels()
// Other code...
}
与 ViewModelFactory:
带有参数 messageDataStore 的 ViewModel,其中 MessageDataStore 是一个 DataStore class 或者它可以是您想要传递给 ViewModel 的任何其他内容。
class MyViewModel(
private val messageDataStore: MessageDataStore,
): ViewModel() { ... }
用于创建 ViewModel 的 ViewModel 工厂 class
/**
* Factory for all ViewModels.
*/
@Suppress("UNCHECKED_CAST")
class ViewModelFactory constructor(
private val messageDataStore: MessageDataStore,
owner: SavedStateRegistryOwner,
defaultArgs: Bundle? = null
) : AbstractSavedStateViewModelFactory(owner, defaultArgs) {
override fun <T : ViewModel> create(
key: String,
modelClass: Class<T>,
handle: SavedStateHandle
) = with(modelClass) {
when {
isAssignableFrom(MyViewModel::class.java) ->
MyViewModel(messageDataStore)
else ->
throw IllegalArgumentException("Unknown ViewModel class: ${modelClass.name}")
}
} as T
}
用于创建 dependencies/parameters
的应用程序 class
class MyApp : Application() {
val messageDataStore: MessageDataStore
get() = MessageDataStore.getInstance(this)
}
在活动和片段中获取工厂 class 的扩展函数,MyExt.kt
fun AppCompatActivity.getViewModelFactory(savedInstanceState: Bundle?): ViewModelFactory {
val messageDataStore = (applicationContext as MyApp).messageDataStore
return ViewModelFactory(messageDataStore, this, savedInstanceState)
}
fun Fragment.getViewModelFactory(savedInstanceState: Bundle?): ViewModelFactory {
val messageDataStore = (requireContext().applicationContext as MyApp).messageDataStore
return ViewModelFactory(messageDataStore, this.requireActivity(), savedInstanceState)
}
在活动中获取ViewMode
class MainActivity : AppCompatActivity() {
private lateinit var myViewModel: MyViewModel
// Other code...
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val vm by viewModels<MyViewModel> { getViewModelFactory(savedInstanceState) }
myViewModel = vm
// Other code...
}
}
在片段中获取 ViewModel。
class MyFragment : Fragment() {
private lateinit var myViewModel: MyViewModel
//Other code...
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val vm by activityViewModels<MyViewModel> { getViewModelFactory(savedInstanceState) }
myViewModel = vm
//Other code...
}
}
除了应用程序上下文之外,是否可以将其他参数传递给我的自定义 AndroidViewModel
构造函数。
示例:
public class MyViewModel extends AndroidViewModel {
private final LiveData<List<MyObject>> myObjectList;
private AppDatabase appDatabase;
public MyViewModel(Application application, String param) {
super(application);
appDatabase = AppDatabase.getDatabase(this.getApplication());
myObjectList = appDatabase.myOjectModel().getMyObjectByParam(param);
}
}
当我想使用我的自定义 ViewModel
class 我在我的片段中使用此代码:
MyViewModel myViewModel = ViewModelProvider.of(this).get(MyViewModel.class)
所以我不知道如何将附加参数 String param
传递到我的自定义 ViewModel
中。我只能传递应用程序上下文,但不能传递其他参数。我真的很感激任何帮助。谢谢你。
编辑:我添加了一些代码。希望现在好点了。
您的 ViewModel 需要一个工厂 class。
public class MyViewModelFactory implements ViewModelProvider.Factory {
private Application mApplication;
private String mParam;
public MyViewModelFactory(Application application, String param) {
mApplication = application;
mParam = param;
}
@Override
public <T extends ViewModel> T create(Class<T> modelClass) {
return (T) new MyViewModel(mApplication, mParam);
}
}
在实例化视图模型时,您可以这样做:
MyViewModel myViewModel = ViewModelProvider(this, new MyViewModelFactory(this.getApplication(), "my awesome param")).get(MyViewModel.class);
对于 kotlin,你可以使用委托 属性:
val viewModel: MyViewModel by viewModels { MyViewModelFactory(getApplication(), "my awesome param") }
还有另一个新选项 - 实现 HasDefaultViewModelProviderFactory
并用你的工厂实例化覆盖 getDefaultViewModelProviderFactory()
然后你可以在没有工厂的情况下调用 ViewModelProvider(this)
或 by viewModels()
.
对于在多个不同视图模型之间共享的一个工厂,我会像这样扩展 mlyko 的答案:
public class MyViewModelFactory extends ViewModelProvider.NewInstanceFactory {
private Application mApplication;
private Object[] mParams;
public MyViewModelFactory(Application application, Object... params) {
mApplication = application;
mParams = params;
}
@Override
public <T extends ViewModel> T create(Class<T> modelClass) {
if (modelClass == ViewModel1.class) {
return (T) new ViewModel1(mApplication, (String) mParams[0]);
} else if (modelClass == ViewModel2.class) {
return (T) new ViewModel2(mApplication, (Integer) mParams[0]);
} else if (modelClass == ViewModel3.class) {
return (T) new ViewModel3(mApplication, (Integer) mParams[0], (String) mParams[1]);
} else {
return super.create(modelClass);
}
}
}
并实例化视图模型:
ViewModel1 vm1 = ViewModelProviders.of(this, new MyViewModelFactory(getApplication(), "something")).get(ViewModel1.class);
ViewModel2 vm2 = ViewModelProviders.of(this, new MyViewModelFactory(getApplication(), 123)).get(ViewModel2.class);
ViewModel3 vm3 = ViewModelProviders.of(this, new MyViewModelFactory(getApplication(), 123, "something")).get(ViewModel3.class);
不同的视图模型具有不同的构造函数。
我写了一个库,它应该使这样做更直接、更清晰,不需要多重绑定或工厂样板,同时与 ViewModel 参数无缝工作,这些参数可以由 Dagger 作为依赖项提供: https://github.com/radutopor/ViewModelFactory
@ViewModelFactory
class UserViewModel(@Provided repository: Repository, userId: Int) : ViewModel() {
val greeting = MutableLiveData<String>()
init {
val user = repository.getUser(userId)
greeting.value = "Hello, $user.name"
}
}
在视图中:
class UserActivity : AppCompatActivity() {
@Inject
lateinit var userViewModelFactory2: UserViewModelFactory2
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_user)
appComponent.inject(this)
val userId = intent.getIntExtra("USER_ID", -1)
val viewModel = ViewModelProviders.of(this, userViewModelFactory2.create(userId))
.get(UserViewModel::class.java)
viewModel.greeting.observe(this, Observer { greetingText ->
greetingTextView.text = greetingText
})
}
}
(KOTLIN) 我的解决方案使用了一点反射。
假设您不想在每次创建需要一些参数的新 ViewModel class 时都创建外观相同的工厂 class。您可以通过反射来完成此操作。
例如,您将有两个不同的活动:
class Activity1 : FragmentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val args = Bundle().apply { putString("NAME_KEY", "Vilpe89") }
val viewModel = ViewModelProviders
.of(this, ViewModelWithArgumentsFactory(args))
.get(ViewModel1::class.java)
}
}
class Activity2 : FragmentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val args = Bundle().apply { putInt("AGE_KEY", 29) }
val viewModel = ViewModelProviders
.of(this, ViewModelWithArgumentsFactory(args))
.get(ViewModel2::class.java)
}
}
以及这些活动的 ViewModel:
class ViewModel1(private val args: Bundle) : ViewModel()
class ViewModel2(private val args: Bundle) : ViewModel()
然后是神奇的部分,工厂 class 的实现:
class ViewModelWithArgumentsFactory(private val args: Bundle) : NewInstanceFactory() {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
try {
val constructor: Constructor<T> = modelClass.getDeclaredConstructor(Bundle::class.java)
return constructor.newInstance(args)
} catch (e: Exception) {
Timber.e(e, "Could not create new instance of class %s", modelClass.canonicalName)
throw e
}
}
}
为什么不这样做:
public class MyViewModel extends AndroidViewModel {
private final LiveData<List<MyObject>> myObjectList;
private AppDatabase appDatabase;
private boolean initialized = false;
public MyViewModel(Application application) {
super(application);
}
public initialize(String param){
synchronized ("justInCase") {
if(! initialized){
initialized = true;
appDatabase = AppDatabase.getDatabase(this.getApplication());
myObjectList = appDatabase.myOjectModel().getMyObjectByParam(param);
}
}
}
}
然后分两步使用:
MyViewModel myViewModel = ViewModelProvider.of(this).get(MyViewModel.class)
myViewModel.initialize(param)
我把它设为class,其中传递了已创建的对象。
private Map<String, ViewModel> viewModelMap;
public ViewModelFactory() {
this.viewModelMap = new HashMap<>();
}
public void add(ViewModel viewModel) {
viewModelMap.put(viewModel.getClass().getCanonicalName(), viewModel);
}
@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
for (Map.Entry<String, ViewModel> viewModel : viewModelMap.entrySet()) {
if (viewModel.getKey().equals(modelClass.getCanonicalName())) {
return (T) viewModel.getValue();
}
}
return null;
}
然后
ViewModelFactory viewModelFactory = new ViewModelFactory();
viewModelFactory.add(new SampleViewModel(arg1, arg2));
SampleViewModel sampleViewModel = ViewModelProviders.of(this, viewModelFactory).get(SampleViewModel.class);
使用依赖注入实现
这更高级,更适合生产代码。
Dagger2,方块AssistedInject offers a production-ready implementation for ViewModels that can inject necessary components such as a repository that handles network and database requests. It also allows for the manual injection of arguments/parameters in the activity/fragment. Here's a concise outline of the steps to implement with code Gists based on Gabor Varadi's detailed post, Dagger Tips.
Dagger Hilt 是下一代解决方案,自 2020 年 7 月 12 日起处于 alpha 状态,一旦库可用,便会提供相同的用例和更简单的设置处于发布状态。
在 Kotlin
中使用 Lifecycle 2.2.0 实现路过Arguments/Parameters
// Override ViewModelProvider.NewInstanceFactory to create the ViewModel (VM).
class SomeViewModelFactory(private val someString: String): ViewModelProvider.NewInstanceFactory() {
override fun <T : ViewModel?> create(modelClass: Class<T>): T = SomeViewModel(someString) as T
}
class SomeViewModel(private val someString: String) : ViewModel() {
init {
//TODO: Use 'someString' to init process when VM is created. i.e. Get data request.
}
}
class Fragment: Fragment() {
// Create VM in activity/fragment with VM factory.
val someViewModel: SomeViewModel by viewModels { SomeViewModelFactory("someString") }
}
使用 Arguments/Parameters
启用 SavedStateclass SomeViewModelFactory(
private val owner: SavedStateRegistryOwner,
private val someString: String) : AbstractSavedStateViewModelFactory(owner, null) {
override fun <T : ViewModel?> create(key: String, modelClass: Class<T>, state: SavedStateHandle) =
SomeViewModel(state, someString) as T
}
class SomeViewModel(private val state: SavedStateHandle, private val someString: String) : ViewModel() {
val feedPosition = state.get<Int>(FEED_POSITION_KEY).let { position ->
if (position == null) 0 else position
}
init {
//TODO: Use 'someString' to init process when VM is created. i.e. Get data request.
}
fun saveFeedPosition(position: Int) {
state.set(FEED_POSITION_KEY, position)
}
}
class Fragment: Fragment() {
// Create VM in activity/fragment with VM factory.
val someViewModel: SomeViewModel by viewModels { SomeViewModelFactory(this, "someString") }
private var feedPosition: Int = 0
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
someViewModel.saveFeedPosition((contentRecyclerView.layoutManager as LinearLayoutManager)
.findFirstVisibleItemPosition())
}
override fun onViewStateRestored(savedInstanceState: Bundle?) {
super.onViewStateRestored(savedInstanceState)
feedPosition = someViewModel.feedPosition
}
}
基于@vilpe89 AndroidViewModel案例的上述Kotlin解决方案
class ExtraParamsViewModelFactory(
private val application: Application,
private val myExtraParam: String
): ViewModelProvider.NewInstanceFactory() {
override fun <T : ViewModel?> create(modelClass: Class<T>): T =
SomeViewModel(application, myExtraParam) as T
}
然后片段可以启动 viewModel 为
class SomeFragment : Fragment() {
// ...
private val myViewModel: SomeViewModel by viewModels {
ExtraParamsViewModelFactory(this.requireActivity().application, "some string value")
}
// ...
}
然后是实际的 ViewModel class
class SomeViewModel(application: Application, val myExtraParam:String) : AndroidViewModel(application) {
// ...
}
或者以某种合适的方法...
override fun onActivityCreated(...){
// ...
val myViewModel = ViewModelProvider(this, ExtraParamsViewModelFactory(this.requireActivity().application, "some string value")).get(SomeViewModel::class.java)
// ...
}
class UserViewModelFactory(private val context: Context) : ViewModelProvider.NewInstanceFactory() {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return UserViewModel(context) as T
}
}
class UserViewModel(private val context: Context) : ViewModel() {
private var listData = MutableLiveData<ArrayList<User>>()
init{
val userRepository : UserRepository by lazy {
UserRepository
}
if(context.isInternetAvailable()) {
listData = userRepository.getMutableLiveData(context)
}
}
fun getData() : MutableLiveData<ArrayList<User>>{
return listData
}
在Activity
中调用Viewmodelval userViewModel = ViewModelProviders.of(this,UserViewModelFactory(this)).get(UserViewModel::class.java)
在Kotlin中,由于ViewModel
的调用者和ViewModel
本身运行在不同的协程中,所以使用[=13在它们之间传递数据更自然方便=]:
class NewViewModel : ViewModel() {
private val newData: MutableLiveData<Service.DataEntry?> by lazy {
MutableLiveData<Service.DataEntry?>().also {
viewModelScope.launch {
val channel = Service.ParamChannel // type Channel<Params>
val params = channel.receive()
it.value = Service.postSomething(params)
}
}
}
fun getData(): LiveData<Service.DataEntry?> {
return newData
}
}
// Calling code:
val model: NewViewModel by viewModels()
model.getData().observe(this) { newData ->
if (newData != null) {
...
}
else
{
...
}
}
runBlocking {
Service.ParamChannel.send(theParams)
}
这是工作代码的一部分,出于演示目的我将其匿名化。
正确的方法是使用依赖注入框架,例如Dagger hilt。如果不使用 DI 框架,则使用 ViewModelFactory。
带匕首柄:
带参数的 ViewModel
@HiltViewModel
class MyViewModel @Inject constructor(
private val myRepository: MyRepository,
private val savedStateHandle: SavedStateHandle
) : ViewModel() { ... }
一个存储库
class MyRepository @Inject constructor(
private val myRemoteDataSource: MyDataSource,
private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO
) { ... }
一个提供 dependencies/parameters 的模块,因此可以将它们注入存储库和 ViewModel。
@InstallIn(ViewModelComponent::class)
@Module
object MyProvideModule {
@Provides
fun provideMyDataSource(@ApplicationContext context: Context): MyDataSource {
//code to create MyDataSource...
return MyDataSource(context)
}
@Provides
fun provideCoroutineDispatcher(): CoroutineDispatcher {
return Dispatchers.IO
}
}
用于绑定存储库的模块
@Module
@InstallIn(ViewModelComponent::class)
interface RepositoryModules {
@Binds
fun provideMyRepository(repository: MyRepository): MyRepository
}
使用带有@HiltAndroidApp 注释的应用程序启动 Dagger 刀柄。
@HiltAndroidApp
class MainApplication : Application() {
override fun onCreate() {
super.onCreate()
}
}
在活动中获取 ViewModel
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
private val myViewModel: MyViewModel by viewModels()
// Other code...
}
在片段中获取 ViewModel
@AndroidEntryPoint
class MyFragment : Fragment() {
private val myViewModel: MyViewModel by activityViewModels()
// Other code...
}
与 ViewModelFactory:
带有参数 messageDataStore 的 ViewModel,其中 MessageDataStore 是一个 DataStore class 或者它可以是您想要传递给 ViewModel 的任何其他内容。
class MyViewModel(
private val messageDataStore: MessageDataStore,
): ViewModel() { ... }
用于创建 ViewModel 的 ViewModel 工厂 class
/**
* Factory for all ViewModels.
*/
@Suppress("UNCHECKED_CAST")
class ViewModelFactory constructor(
private val messageDataStore: MessageDataStore,
owner: SavedStateRegistryOwner,
defaultArgs: Bundle? = null
) : AbstractSavedStateViewModelFactory(owner, defaultArgs) {
override fun <T : ViewModel> create(
key: String,
modelClass: Class<T>,
handle: SavedStateHandle
) = with(modelClass) {
when {
isAssignableFrom(MyViewModel::class.java) ->
MyViewModel(messageDataStore)
else ->
throw IllegalArgumentException("Unknown ViewModel class: ${modelClass.name}")
}
} as T
}
用于创建 dependencies/parameters
的应用程序 classclass MyApp : Application() {
val messageDataStore: MessageDataStore
get() = MessageDataStore.getInstance(this)
}
在活动和片段中获取工厂 class 的扩展函数,MyExt.kt
fun AppCompatActivity.getViewModelFactory(savedInstanceState: Bundle?): ViewModelFactory {
val messageDataStore = (applicationContext as MyApp).messageDataStore
return ViewModelFactory(messageDataStore, this, savedInstanceState)
}
fun Fragment.getViewModelFactory(savedInstanceState: Bundle?): ViewModelFactory {
val messageDataStore = (requireContext().applicationContext as MyApp).messageDataStore
return ViewModelFactory(messageDataStore, this.requireActivity(), savedInstanceState)
}
在活动中获取ViewMode
class MainActivity : AppCompatActivity() {
private lateinit var myViewModel: MyViewModel
// Other code...
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val vm by viewModels<MyViewModel> { getViewModelFactory(savedInstanceState) }
myViewModel = vm
// Other code...
}
}
在片段中获取 ViewModel。
class MyFragment : Fragment() {
private lateinit var myViewModel: MyViewModel
//Other code...
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val vm by activityViewModels<MyViewModel> { getViewModelFactory(savedInstanceState) }
myViewModel = vm
//Other code...
}
}