Android ViewModel和startActivity

时间:2019-02-21 19:12:54

标签: android viewmodel android-architecture-components android-viewmodel

我正在学习ViewModelLiveData,在此过程中出现了疑问。

如果我需要启动Activity怎么办?

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

ActivityAViewModel : ViewModel() {
    // ...

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

    // ...
}

ActivityA {
    // ...

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

    // ...
}

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

6 个答案:

答案 0 :(得分:2)

您应该从活动而非视图模型调用startActivity。如果要从viewmodel打开它,则需要在viewmodel中使用一些导航参数创建livedata,并观察活动内部的livedata。

答案 1 :(得分:2)

我喜欢触发事件。 :D

每个人都说mimetypes.py不应包含ViewModel或对包含Context的类的引用。因此,从Context开始startActivity并不是一个好主意。

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

您可能想看看SingleLiveEvent

答案 2 :(得分:1)

恕我直言,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;
    }
}

答案 3 :(得分:1)

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

因此,最好不要通过将一个特定的活动限制在一个视图模型上来保持虱子耦合,但是移动开发人员之间的惯例是,他们倾向于为一个活动/片段创建一个视图模型。

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

答案 4 :(得分:0)

您可以使用Application提供的AndroidViewModel上下文,您应该扩展AndroidViewModel,它只是一个ViewModel,其中包含一个Application引用

答案 5 :(得分:0)

ViewModels旨在以生命周期感知的方式保存和管理单个 Activity相关数据

您不应将应用上下文,活动或视图对象保存到ViewModel中;因为ViewModels旨在保留与这些内容(上下文,视图...)相关的数据,以便在配置更改(例如屏幕旋转)时保留这些数据(因此ViewModels并非旨在保留这些内容本身(上下文,活动或视图),但与它们关联的数据。

一般规则:单个ViewModel是为单个活动及其相关片段设计的。因此,从父活动的ViewModel开始新的活动是没有意义的。

开始新活动应从当前活动开始,而不应从其ViewModel开始;父活动将因此停止,因此无法从新活动访问其ViewModel

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