Android ViewModel 和 startActivity

Android ViewModel and startActivity

我正在学习ViewModelLiveData,在这个过程中,产生了一个疑问。

如果我需要开始Activity怎么办?

是否可以将上下文作为参数传递给 ViewModel(上下文不会存储在 ViewModel 中)?

ActivityAViewModel : ViewModel() {
    // ...

    fun openActivityB(context: Context) {
        context.startActivity(...)
    }

    // ...
}

ActivityA {
    // ...

    fun onSomethingHappened() {
        viewModel.openActivityB(this)
    }

    // ...
}

如果不是,在这种情况下最正确的做法是什么?

您应该从 activity 调用 startActivity,而不是从视图模型。如果你想从 viewmodel 打开它,你需要在 viewmodel 中使用一些导航参数创建 livedata 并观察 activity 内的 livedata。

您可以使用 AndroidViewModel 提供的 Application 上下文,您应该扩展 AndroidViewModel,它只是一个包含 [=10= 的 ViewModel ]参考。

ViewModels 旨在保存和管理生命周期感知 (UI) 组件(即活动 and/or 片段)相关数据。

通常你不应该保存应用程序上下文,activity,或将对象查看到 ViewModel,因为 ViewModels 可以比 activity/fragment 活得更久;因为它们可以在配置更改时被销毁和重新创建;或者当 Android 系统决定这样做时,在这种情况下 ViewModel 可以保持已被摧毁的 activity/fragment 的停滞 context/views。因此,ViewModel 可以更新 UI 不再存在的视图。

当 UI 视图 (activities/fragments) 由于 user/configuration 更改“例如屏幕旋转”或系统操作而被破坏时,它们的 UI 相关数据将被丢失; ViewModel 通过在发生这些更改时保留这些数据解决了这个问题。

A ViewModel 可以绑定到 activity 的数据、具有片段的活动、父片段和子片段,或单个片段。但是不能绑定两个不同的数据activity;因此它不能从父 activity.

继承

开始一个新的 activity 应该从当前的 activity 而不是它的 ViewModel 开始;父 activity 将相应地停止,因此无法从新的 activity.

访问其 ViewModel

查看 this 讨论以获取更多信息

恕我直言,viewmodel 应该对视图及其如何向用户呈现信息一无所知。

/**
 * Activity (as view) responsible only for gathering actions and intentions from user and
 * show result state.
 * View must know "What user want". View knows meaning its interface.
 * Click on button 'login' means INTENTION to login somewhere.
 * This intention pass to ViewModel to process it and wait some changing state from LiveData.
 * For example implemented as Actions.
 */
public class LoginActivity extends AppCompatActivity {
    private LoginViewModel mLoginViewModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mLoginViewModel = ViewModelProviders.of(this).get(LoginViewModel.class);
        mLoginViewModel.getAction().observe(this, new Observer<Action>() {
            @Override
            public void onChanged(@Nullable final Action action) {
                if(action != null){
                    handleAction(action);
                }
            }
        });

        //Emulate user intention
        mLoginViewModel.userWantToLogin("0123456789", "admin");
    }

    private void handleAction(@NonNull final Action action) {
        switch (action.getValue()){
            case Action.SHOW_WELCOME:
                //show Activity. 
                break;
            case Action.SHOW_INVALID_PASSWARD_OR_LOGIN:
                //show Toast
                break;
        }
    }
}

    public class LoginViewModel extends ViewModel {
        //Stores actions for view.
        private MutableLiveData<Action> mAction = new MutableLiveData<>();

        public LiveData<Action> getAction() {
            return mAction;
        }

        /**
         * Takes intention to login from user and process it.
         *
         * @param password Dummy password.
         * @param login Dummy login.
         */
        public void userWantToLogin(String password, String login){
            if(validateInfo(password, login)){
                showWelcomeScreen();
            }else {
                showPasswordOrLoginInvalid();
            }
        }

        /*
         * Changes LiveData. Does not act directly with view.
         * View can implement any way to show info
          * to user (show new activity, alert or toast)
         */
        private void showPasswordOrLoginInvalid() {
            mAction.setValue(new Action(Action.SHOW_INVALID_PASSWARD_OR_LOGIN));
        }

        /*
         * Changes LiveData. Does not act directly with view.
         * View can implement any way to show info
         * to user (show new activity, alert or toast)
         */
        private void showWelcomeScreen() {
            mAction.setValue(new Action(Action.SHOW_WELCOME));
        }

        //As example of some logic.
        private boolean validateInfo(String password, String login) {
            return password.equals("0123456789") && login.equals("admin");
        }
    }

public class Action {
    public static final int SHOW_WELCOME = 0;
    public static final int SHOW_INVALID_PASSWARD_OR_LOGIN = 1;
    private final int mAction;

    public Action(int action) {
        mAction = action;
    }

    public int getValue() {
        return mAction;
    }
}

如果viewmodel对活动一无所知,那将是一个很好的设计选择。基本上,viewmodel 和活动扮演可观察者和观察者的角色。 ViewModel,作为存储库或业务模型或编排层的包装器,提供反应式数据流并扮演可观察的角色。这意味着,作为观察者的多个活动或片段可以收听一个视图模型。

因此,最好通过不将一个特定的 activity 紧缩到一个视图模型来保持虱子耦合,但移动开发人员之间的普遍惯例是他们更喜欢将一个视图模型创建到一个 activity/fragment.

如果您有改造或 okhttp 或其他需要上下文的库,请通过 dagger2 或 Koin DI 库将上下文传递给它们。这将是一个干净的架构。

我喜欢触发事件。 :D

正如大家所说,ViewModel 不应包含 Context 或引用包含 Context 的 类。所以从 ViewModel.

startActivity 不是一个好主意

我要做的是拥有一个包含事件数据的 LiveData。此事件将根据您的业务逻辑从您的 ViewModel 中触发(也许您正在显示倒计时并在它结束时移动到下一个 Activity?)。它是一个LiveData,你可以在上面观察。根据此事件的数据,您可以开始 activity.

你可能想看看SingleLiveEvent