Android ViewModel调用Activity方法 [英] Android ViewModel call Activity methods

查看:1417
本文介绍了Android ViewModel调用Activity方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在我的项目中使用了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 通信的新方法时,必须在ViewModelActivityListener界面中进行更改.
  • The View should take care of subscribing/unsubscribing to/from ViewModel, as it's lifetime most likely shorter than ViewModel's
  • The first drawback also leads to a situation where something happened and the ViewModel should call View's method but the View is in between subscribing/unsubscribing; ViewModel also should aware of empty listener situation as it can be null
  • When adding new methods of ViewModel-Activity communication you will have to make changes in ViewModel, Activity and Listener 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.postValueMutableLiveData.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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆