【问题标题】:Android ViewModel and startActivityAndroid ViewModel 和 startActivity
【发布时间】:2019-07-15 19:22:21
【问题描述】:

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

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

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

ActivityAViewModel : ViewModel() {
    // ...

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

    // ...
}

ActivityA {
    // ...

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

    // ...
}

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

【问题讨论】:

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


    【解决方案1】:

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

    【讨论】:

    • 非常感谢您的回答,亚历山大!作为示例的代码将非常受欢迎:)
    【解决方案2】:

    我喜欢触发事件。 :D

    正如大家所说,ViewModel 不应包含 Context 或引用包含 Context 的类。所以从ViewModelstartActivity不是一个好主意。

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

    你可能想看看SingleLiveEvent

    【讨论】:

      【解决方案3】:

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

      【讨论】:

      【解决方案4】:

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

      所以最好保持虱子耦合,不要将一个特定的活动收紧到一个视图模型,但移动开发人员的普遍约定是他们更喜欢为一个活动/片段创建一个视图模型>.

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

      【讨论】:

        【解决方案5】:

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

        【讨论】:

          【解决方案6】:

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

          通常您不应将应用上下文、活动或视图对象保存到ViewModel,因为ViewModels 的寿命可能比活动/片段长;因为它们可以在配置更改时被销毁和重新创建;或者当 Android 系统决定这样做时,在这种情况下,ViewModel 可以保存已被破坏的活动/片段的停滞上下文/视图。所以,ViewModel 可以更新不再存在的 UI 视图。

          当 UI 视图(活动/片段)由于用户/配置更改“例如屏幕旋转”或系统操作而被破坏时,其与 UI 相关的数据将丢失; ViewModel 解决了这个问题,方法是在发生这些变化时保留这些数据。

          ViewModel 可以绑定到活动的数据、带有片段的活动、父片段和子片段或单个片段。但它不能绑定两个不同活动的数据;因此它不能从父 Activity 继承。

          开始一个新的活动应该从当前活动而不是它的ViewModel开始;父活动将相应停止,因此无法从新活动中访问其ViewModel

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

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-12-23
            • 1970-01-01
            • 2017-06-17
            • 2017-04-02
            • 1970-01-01
            相关资源
            最近更新 更多