如何从mvvm模式登录成功viewmodel启动活动

嗨,我有一个活动LoginActivity.ktLoginViewModel 。 我在LoginViewModellogin方法中调用登录API。 在成功之后,我想开始家庭活动。 在MVVM方法中,正确的方法是什么?

LoginViewModel.kt

 class LoginViewModel : BaseViewModel<LoginNavigator>(), AnkoLogger { val emailField = ObservableField<String>() private val email: String get() = emailField.get() val passwordField = ObservableField<String>() private val password: String get() = passwordField.get() val progressVisibility: ObservableInt = ObservableInt(View.GONE) @Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE") fun login(view: View) { // here I am calling API and on success } /** * Validate email and password. It checks email and password is empty or not * and validate email address is correct or not * @param email email address for login * @param password password for login * @return true if email and password pass all conditions else false */ private fun isEmailAndPasswordValid(email: String, password: String): Boolean { if (email.isEmpty()) return false if (!Patterns.EMAIL_ADDRESS.matcher(email).matches()) return false if (password.isEmpty()) return false return true } } 

LoginActivity.kt

 class LoginActivity : BaseActivity(), LoginNavigator { @Inject lateinit var loginViewModel: LoginViewModel override fun onCreate(savedInstanceState: Bundle?) { performDependencyInjection() super.onCreate(savedInstanceState) val activityLoginBinding: ActivityLoginBinding = DataBindingUtil.setContentView<ActivityLoginBinding>(this, R.layout.activity_login) activityLoginBinding.loginViewModel = loginViewModel loginViewModel.mNavigator = this } 

假设使用您的登录想法,用户登录失败和应用程序需要做一个简单的Toast或SnackBar一个简单的情况,以显示相关的信息,如“您的用户名和密码是不正确的”。 你需要的代码是

吐司(必需的Context

 Toast.makeText(context, text, duration).show(); 

Snackbar(必填View

 Snackbar.make(findViewById(R.id.myCoordinatorLayout), R.string.email_archived, Snackbar.LENGTH_SHORT); 

如果你想在你的ViewModel中使用它(我不熟悉Kotlin)

  @Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE") void function login(final View view) { // here I am calling API and on success repo.login(result -> { if(result.statusCode == 401) Toast.makeText(view.getContext(), "Login failed...", duration).show(); }); } 

您将以相反的方式找到活动的参考,这使得代码更加复杂,难以维护,因为每次需要获取活动或上下文的引用, 以便在视图中执行与视图或活动相关的内容模型而不是活动

从谷歌样本 ,你可以看到输入完成后调用doSearch()函数。 并且在获取搜索结果之后,绑定将把最新的结果返回给观察者,现在是更新适配器中结果的活动工作。

 private void initSearchInputListener() { binding.get().input.setOnEditorActionListener((v, actionId, event) -> { if (actionId == EditorInfo.IME_ACTION_SEARCH) { doSearch(v); return true; } return false; }); binding.get().input.setOnKeyListener((v, keyCode, event) -> { if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) { doSearch(v); return true; } return false; }); } private void doSearch(View v) { String query = binding.get().input.getText().toString(); // Dismiss keyboard dismissKeyboard(v.getWindowToken()); binding.get().setQuery(query); searchViewModel.setQuery(query); } private void initRecyclerView() { binding.get().repoList.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager(); int lastPosition = layoutManager .findLastVisibleItemPosition(); if (lastPosition == adapter.get().getItemCount() - 1) { searchViewModel.loadNextPage(); } } }); searchViewModel.getResults().observe(this, result -> { binding.get().setSearchResource(result); binding.get().setResultCount((result == null || result.data == null) ? 0 : result.data.size()); adapter.get().replace(result == null ? null : result.data); binding.get().executePendingBindings(); }); searchViewModel.getLoadMoreStatus().observe(this, loadingMore -> { if (loadingMore == null) { binding.get().setLoadingMore(false); } else { binding.get().setLoadingMore(loadingMore.isRunning()); String error = loadingMore.getErrorMessageIfNotHandled(); if (error != null) { Snackbar.make(binding.get().loadMoreBar, error, Snackbar.LENGTH_LONG).show(); } } binding.get().executePendingBindings(); }); } 

另外,根据@Emanuel S的回答,你会看到他的论点

WeakReference对包含活动的上下文的NavigationController。 这是处理ViewModel中的上下文绑定的东西的常用模式。

我高度拒绝这个原因有几个。 首先:这通常意味着你必须保留一个引用你的NavigationController来修复上下文泄漏,但是根本不解决这个架构。

最好的方式(在我的看法)是使用LiveData,它是生命周期感知,可以做所有想要的东西。

如果在视图模型中实现ui动作,如果在视图或上下文中出现NullPointerException,或者对其进行了一些改进,您会首先找到哪个类,那么您可能会考虑另一个问题: ViewModel还是Activity? 由于第一个持有UI操作,第二个持有数据绑定。 这两个可能在排除故障。