我正在学习ViewModel
和LiveData
,并且在此过程中出现了疑问。
如果我需要开始使用Activity
,我该怎么办?
是否可以将上下文作为参数传递给ViewModel
(上下文不会存储在ViewModel中)?
ActivityAViewModel : ViewModel() {
// ...
fun openActivityB(context: Context) {
context.startActivity(...)
}
// ...
}
ActivityA {
// ...
fun onSomethingHappened() {
viewModel.openActivityB(this)
}
// ...
}
如果没有,那么在这种情况下最正确的做法是什么?
我喜欢开事件。 :d
正如大家所说ViewModel
不应该包含Context
或引用包含Context
的类。所以从startActivity
做ViewModel
不是一个好主意。
我要做的是有一个包含事件数据的LiveData。此事件将根据您的业务逻辑从您的ViewModel中触发(也许您正在显示一个CountDown,并在结束时移动到下一个Activity?)。这是一个LiveData
,你可以观察它。根据此活动的数据,您可以开始您的活动。
你可能想看看SingleLiveEvent
您应该从活动调用startActivity,而不是从viewmodel调用。如果要从viewmodel打开它,则需要在viewmodel中使用一些导航参数创建livingata,并观察活动内的livingata。
恕我直言,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作为存储库或业务模型或业务流程层的包装器,提供反应式样式数据流并发挥可观察的作用。这意味着,作为观察者的几个活动或片段可以收听一个视图模型。
因此,通过不将一个特定活动收紧到一个视图模型来保持louse耦合更好,但移动开发人员普遍认为他们更喜欢为一个活动/片段创建一个视图模型。
如果您有改造或okhttp或其他需要上下文的库,请通过dagger2或Koin DI库传递它们的上下文。这将是一个干净的架构。
你可以使用由Application
提供的AndroidViewModel
上下文,你应该扩展AndroidViewModel
,它只是一个包含ViewModel
参考的Application
。
ViewModels
旨在以生命周期意识的方式持有和管理单个Activity
相关数据
您不应该将应用程序上下文,活动或视图对象保存到ViewModel
中;因为ViewModels
旨在保存与这些内容相关的数据(上下文,视图......),以便在配置更改(例如屏幕旋转)时生存这些数据(所以ViewModels
不是为了在内容本身(上下文,活动或视图)中存活而设计的)但与他们相关的数据。
一般规则:单个ViewModel
是针对单个活动及其相关片段而设计的。因此,从父活动的ViewModel
开始新活动是没有意义的。
开始一项新活动应该来自当前的活动而不是它的ViewModel
;父活动将相应停止,因此无法从新活动访问其ViewModel
。
请查看this讨论了解更多信息