Android ViewModel call Activity methods

2019-04-22 10:09发布

问题:

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,

Also i need to call sign in intent which requires Activity context too:

Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(googleApiClient);
startActivityForResult(signInIntent, GOOGLE_AUTH);

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();
}

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 ?

回答1:

the most difficult part of MVVM is View model must not know about view and reference them

This is quite strong restriction.

You have some options about that

1. View model methods receveing context argument

You can make methods receveing context from view(this method is called from view).

After you can instantiate context related variables.

If you are aware about memory leak, just destroy it when view is pause or stop using Lifecycle aware AAC and reinstatiate when resume or start of Activity or Fragment.

About onActivityResult, I think your solution is not bad because API support is like that.

2. get context from view with data binding

in layout xml, you can send view itself with event listener.

<Button
    ....
    android:onClick=“@{(view) -> vm.onClickFacebookLogin(view)}”

Then you can receive view and retrieve context from view in Viewmodel

3. Use AndroidViewModel

AndroidViewModel class is same with ViewModel class without that has Application context.

You can use Application Context with

gerApplication()

Thank you



回答2:

well your approach is quite good. But somehow your interface depends on the activity means if you are reusing your view these interface makes no use or may be for that scenario you have to create new interface to solve your problem.

But if you create an instance of Activity then you have control of it.