Vla*_*nov 11 android listener mvvm viewmodel android-activity
我正在我的项目中使用android AAC库和Android数据绑定库.我有AuthActivity和AuthViewModel扩展了android的ViewModel类.在某些情况下,我需要让Activity为ViewModel调用一些方法.例如,当用户点击Google Auth或Facebook Auth按钮时,它在Activity类中初始化(因为初始化GoogleApiClient我需要Activity上下文,我无法传递给ViewModel,视图模型无法存储Activity字段).在Activity类中实现了Google Api和Facebook API的所有逻辑:
//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,
Run Code Online (Sandbox Code Playgroud)
此外,我需要调用登录意图,这也需要活动上下文:
Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(googleApiClient);
startActivityForResult(signInIntent, GOOGLE_AUTH);
Run Code Online (Sandbox Code Playgroud)
我不能请求facebook登录和google登录,或者从视图模型类请求startActivity intent,所以我创建了类接口AuthActivityListener:
public interface AuthActivityListener {
void requestSignedIn();
void requestGoogleAuth();
void requestFacebookAuth();
void requestShowDialogFragment(int type);
}
Run Code Online (Sandbox Code Playgroud)
在活动类中实现监听器:
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);
}
...
Run Code Online (Sandbox Code Playgroud)
并在视图模型类中分配此侦听器以调用活动方法:
// in constructor
this.authRequester = listener;
// call activity method
public void onClickedAuthGoogle() {
authRequester.requestGoogleAuth();
}
Run Code Online (Sandbox Code Playgroud)
在google或facebook认证通过后,我从活动中调用视图模型方法:
@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("", "");
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
任何人都可以解释一下,视图模型和活动之间的这种沟通方式是对的,还是我需要找到另一种从视图模型中调用活动方法的方法?
关于如何做到这一点,有几种不同的方法。在这里,我想与您分享我的方法。在我看来,这最适合 MVVM 模式意识形态。
如前所述 - “视图模型必须对视图一无所知并引用它”。这对于 View Model 如何调用 Activity 方法的选择并不多。首先,想到的是侦听器方法。但我认为这种方法有几个缺点:
View应照顾订阅/退订/从ViewModel,因为它的寿命很可能比短ViewModel的ViewModel应该调用View的方法但View介于订阅/取消订阅之间的情况;ViewModel也应该意识到空听众的情况,因为它可以nullViewModel、Activity和Listener界面中进行更改。所以Listener方法不太适合。它看起来更像是一种 MVP 方法。为了消除上述缺点(或至少其中一些),我创建了我称之为ViewModel Events 的方法。在这种方法中,ViewModel“发出”(或生成)它的事件并让View观察它们。让我展示一下我在说什么。
首先,我们需要对ViewModel事件进行一些表示。
abstract class ViewModelEvent {
var handled: Boolean = false
private set
open fun handle(activity: BaseActivity) {
handled = true
}
}
Run Code Online (Sandbox Code Playgroud)
正如您已经看到的,该handle()方法将发挥神奇的作用。当Activity将处理接收到的事件时,它将其实例handle()作为参数传递给方法。在此方法中,我们可以调用任何Activity方法(或安全地将其强制转换为某个特定的Activity)。该handled属性旨在不让Activity处理此ViewModelEvent两次。
此外,我们需要创建一些机制ViewModel来发出它的事件。LiveData最适合这些需求。它将取消对生命周期事件的观察者订阅,并将存储上次发出的事件(这就是ViewModelEvent应该具有上述handled属性的原因)。
abstract class BaseViewModel: ViewModel() {
private val observableEvents = MutableLiveData<ViewModelEvent>()
fun observeViewModelEvents(): LiveData<ViewModelEvent> = observableEvents
protected fun postViewModelEvent(event: ViewModelEvent) {
observableEvents.postValue(event)
}
}
Run Code Online (Sandbox Code Playgroud)
这里没有什么复杂的。只是一个MutableLiveData(暴露为LiveData)和一个发出事件的方法。顺便说一下,在里面postViewModelEvent我们可以检查调用这个方法的线程并使用MutableLiveData.postValueor 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)
}
}
Run Code Online (Sandbox Code Playgroud)
如您所见,可以在 中处理一般事件BaseActivity,而可以通过覆盖该handleViewModelAction方法来处理某些特定事件。
可以根据特定需求更改此方法。例如,ViewModelEvent不必使用Activity实例,可以用作“标记”事件,或者它可以为所需的操作传递一些特定参数等。
该视图模型活动的方法使视图模型,活动通信稳健和无缝。Activity将不得不订阅一次,它不会错过最新ViewModel的事件。
MVVM 最困难的部分是视图模型必须不了解视图并引用它们
\n\n这是相当强的限制。
\n\n你对此有一些选择
\n\n1. 查看接收上下文参数的模型方法
\n\n您可以使方法从视图接收上下文(此方法从视图调用)。
\n\n之后您可以实例化上下文相关的变量。
\n\n如果您意识到内存泄漏,只需在视图暂停或停止时使用生命周期感知 AAC 销毁它,并在恢复或启动 Activity 或 Fragment 时恢复。
\n\n关于onActivityResult,我认为你的解决方案还不错,因为API支持就是这样。
\n\n2. 通过数据绑定从视图中获取上下文
\n\n在布局 xml 中,您可以使用事件侦听器发送视图本身。
\n\n<Button\n ....\n android:onClick=\xe2\x80\x9c@{(view) -> vm.onClickFacebookLogin(view)}\xe2\x80\x9d\nRun Code Online (Sandbox Code Playgroud)\n\n然后您可以接收视图并从 Viewmodel 中的视图检索上下文
\n\n3.使用AndroidViewModel
\n\nAndroidViewModel 类与 ViewModel 类相同,但没有应用程序上下文。
\n\n您可以使用应用程序上下文
\n\ngerApplication()\nRun Code Online (Sandbox Code Playgroud)\n\n谢谢
\n小智 0
嗯,你的方法很好。但不知何故,你的界面取决于活动,这意味着如果你重用你的视图,这些界面没有用处,或者可能对于这种情况,你必须创建新的界面来解决你的问题。
但是,如果您创建 Activity 的实例,那么您就可以控制它。
| 归档时间: |
|
| 查看次数: |
2685 次 |
| 最近记录: |