Android ViewModel调用Activity方法 [英] Android ViewModel call Activity methods
问题描述
我在我的项目中使用了android AAC库和Android数据绑定库.我有AuthActivity和AuthViewModel扩展了android的ViewModel类.在某些情况下,我需要请求Activity来调用ViewModel的某些方法. 例如,当用户单击在Activity类中初始化的Google Auth或Facebook Auth按钮时(由于初始化GoogleApiClient,我需要无法传递给ViewModel的Activity上下文,因此视图模型无法存储Activity字段). 通过Activity类实现的Google Api和Facebook API的所有逻辑:
I'm using android AAC library and Android databinding library in my project. I have AuthActivity and AuthViewModel extends android's ViewModel class. In some cases i need to ask for Activity to call some methods for ViewModel. For example when user click on Google Auth or Facebook Auth button, which initialized in Activity class (because to initialize GoogleApiClient i need Activity context which i can not pass to ViewModel, view model can not store Activity fields). All logic with Google Api and Facebook API implemented in Activity class:
//google api initialization
googleApiClient = new GoogleApiClient.Builder(this)
.enableAutoManage(this, this)
.addApi(Auth.GOOGLE_SIGN_IN_API, gso)
.build();
//facebook login button
loginButton.setReadPermissions(Arrays.asList("email", "public_profile"));
loginButton.registerCallback(callbackManager,
我还需要调用登录意图,这也需要Activity上下文:
Also i need to call sign in intent which requires Activity context too:
Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(googleApiClient);
startActivityForResult(signInIntent, GOOGLE_AUTH);
我无法从视图模型类请求Facebook登录名和google登录名或startActivity意图,因此我创建了类接口AuthActivityListener:
I can not request facebook login and google login, or startActivity intent from view model class, so i created class interface AuthActivityListener:
public interface AuthActivityListener {
void requestSignedIn();
void requestGoogleAuth();
void requestFacebookAuth();
void requestShowDialogFragment(int type);
}
在活动类中实现侦听器:
Implement listener in activity class:
AuthActivityRequester authRequestListener = new AuthActivityRequester() {
@Override
public void requestSignedIn() {
Intent intent = new Intent(AuthActivity.this, ScanActivity.class);
startActivity(intent);
AuthActivity.this.finish();
}
@Override
public void requestGoogleAuth() {
Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(googleApiClient);
startActivityForResult(signInIntent, GOOGLE_AUTH);
}
...
并在视图模型类中分配此侦听器以调用活动方法:
And assign this listener in view model class to call activity methods:
// in constructor
this.authRequester = listener;
// call activity method
public void onClickedAuthGoogle() {
authRequester.requestGoogleAuth();
}
通过google或facebook身份验证后,我从活动中调用视图模型方法:
After google or facebook authentication passed i call view model method from activity:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
callbackManager.onActivityResult(requestCode, resultCode, data);
if (requestCode == GOOGLE_AUTH) {
GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data);
if (result.isSuccess()) {
GoogleSignInAccount acct = result.getSignInAccount();
if (acct != null) {
viewModel.onGoogleUserLoaded(acct.getEmail(), acct.getId());
} else {
viewModel.onGoogleUserLoaded("", "");
}
}
}
}
任何人都可以向我解释一下这种视图模型与活动之间的交流方法是否正确,还是我需要找到另一种从视图模型中调用活动方法的方法?
Can anyone explain me is this approach of communication between view model and activity is right, or i need to find another way to call activity methods from view model ?
推荐答案
关于如何执行此操作,有几种不同的方法. 在这里,我想与您分享我的方法.我认为,这最适合MVVM模式思想.
There are several different approaches on how to do this. Here I want to share my approach with you. Which, in my opinion, is the most suitable for MVVM pattern ideology.
如前所述-"视图模型必须对视图一无所知,并对其进行引用".对于视图模型如何调用Activity方法,这没有太多选择.首先,想到的是 Listener 方法.但是我认为这种方法有几个缺点:
As was mentioned - "View Model must know nothing about the View and reference it". This leaves not many options on how a View Model will call an Activity method. First, what comes to mind is a Listener approach. But this approach has several drawbacks in my opinion:
-
View
应该注意订阅/取消订阅ViewModel
,因为它的生命周期很可能短于ViewModel
的时间 - 第一个缺点还导致发生某些情况,并且
ViewModel
应该调用View
的方法,但是View
在预订/取消预订之间;ViewModel
还应该意识到监听器为空的情况,因为它可能是null
- 添加 ViewModel-Activity 通信的新方法时,必须在
ViewModel
,Activity
和Listener
界面中进行更改.
- The
View
should take care of subscribing/unsubscribing to/fromViewModel
, as it's lifetime most likely shorter thanViewModel
's - The first drawback also leads to a situation where something happened and the
ViewModel
should callView
's method but theView
is in between subscribing/unsubscribing;ViewModel
also should aware of empty listener situation as it can benull
- When adding new methods of ViewModel-Activity communication you will have to make changes in
ViewModel
,Activity
andListener
interface.
所以 Listener 方法不太适合.它看起来更像是MVP方法.为了消除上述缺陷(或至少其中一些缺陷),我创建了我所说的 ViewModel Events 方法.在这种方法中,ViewModel
发出"(或生成)事件,并让View
对其进行观察.让我看看我在说什么.
So the Listener approach doesn't suite quite well. And it looks more like an MVP approach. To eliminate the above-mentioned drawbacks (or at least some of them), I've created, what I call, ViewModel Events approach. In this approach, ViewModel
"emits" (or generates) it's events and lets the View
to observe them. Let me show what I'm talking about.
首先,我们需要ViewModel
事件的某种表示形式.
At first, we will need some representation of the ViewModel
event.
abstract class ViewModelEvent {
var handled: Boolean = false
private set
open fun handle(activity: BaseActivity) {
handled = true
}
}
正如您已经看到的那样,handle()
方法将起到神奇作用.当Activity
处理接收到的事件时,它将把其实例作为参数传递给handle()
方法.在此方法内部,我们可以调用任何Activity
方法(或将其安全地强制转换为某些特定的Activity
). handled
属性旨在不让Activity
两次处理此ViewModelEvent
.
As you already can see, the handle()
method will do the magic. When the Activity
will handle received event it will pass its instance to handle()
method as a parameter. Inside this method, we can call any Activity
methods (or safe cast it to some specific Activity
). The handled
property is aimed to not let the Activity
to handle this ViewModelEvent
twice.
此外,我们需要为ViewModel
创建一些机制来发出其事件. LiveData
最适合这些需求.它将取消对生命周期事件的观察者订阅,并将存储最后发出的事件(这就是ViewModelEvent
应该具有上述handled
属性的原因).
Further, we need to create some mechanism for the ViewModel
to emit its events. LiveData
suits the most for these needs. It will cancel an observer subscription on lifecycle events and it will store last emitted event (that is why the ViewModelEvent
should have the above-mentioned handled
property).
abstract class BaseViewModel: ViewModel() {
private val observableEvents = MutableLiveData<ViewModelEvent>()
fun observeViewModelEvents(): LiveData<ViewModelEvent> = observableEvents
protected fun postViewModelEvent(event: ViewModelEvent) {
observableEvents.postValue(event)
}
}
这里没有什么复杂的.只是MutableLiveData
(公开为LiveData
)和发出事件的方法.顺便说一下,在postViewModelEvent
内部,我们可以检查调用此方法的线程,并使用MutableLiveData.postValue
或MutableLiveData.setValue
.
Nothing complex here. Just a MutableLiveData
(exposed as LiveData
) and a method to emit events. By the way, inside the postViewModelEvent
we can check the thread this method was called from and use MutableLiveData.postValue
or MutableLiveData.setValue
.
最后是Activity
本身.
abstract class BaseActivity: Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
// ...
viewModel.observeViewModelEvents().observe(this, Observer {
val event = it.takeUnless { it == null || it.handled } ?: return@Observer
handleViewModelAction(event)
})
}
protected open fun handleViewModelAction(event: ViewModelEvent) {
event.handle(this)
}
}
如您所见,一般事件可以在BaseActivity
中处理,而某些特定事件可以通过覆盖handleViewModelAction
方法来处理.
As you can see, general events can be handled in the BaseActivity
, while some specific events can be handled by overriding the handleViewModelAction
method.
可以针对特定需求更改此方法.例如,ViewModelEvent
不必与Activity
实例一起使用,可以用作标记"事件,也可以为所需的操作传递一些特定的参数,等等.
This approach can be changed for specific needs. For example, ViewModelEvent
doesn't have to work with Activity
instance and can be used as a "marker" event or it can pass some specific parameters for the required action, etc.
ViewModel事件方法使 ViewModel-Activity 通信功能强大且无缝. Activity
将必须订阅一次,并且不会错过最新的ViewModel
事件.
The ViewModel Events approach makes ViewModel-Activity communication robust and seamless. Activity
will have to subscribe once and it will not miss the latest ViewModel
's event.
这篇关于Android ViewModel调用Activity方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!