android - 如何在 mvvm 模式中登录成功时从 viewmodel 启动 Activity

标签 android mvvm kotlin android-viewmodel

嗨,我有一项 Activity LoginActivity.ktLoginViewModel 。我正在调用login中的登录API方法LoginViewModel 。一旦成功,我想开始家庭 Activity 。在 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
    }

}

登录 Activity .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来显示相关信息,例如“您的用户名和密码不正确”。您需要的代码是

Toast(必需上下文)

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

信息栏(必需的 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();
      });
    }

您将以相反的方式找到 Activity 的引用,这会使代码更加复杂并且难以维护,因为每次您需要获取 Activity 或上下文的引用来执行与 Activity 相关的操作时 View 模型中的 View 或 Activity 而不是 Activity

来自google sample ,您可以看到输入完成后调用 doSearch() 函数。获取搜索结果后,绑定(bind)会将最新结果返回给该观察者,现在是更新适配器中结果的 Activity 工作。

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 to a NavigationController which holds the Context of the Activity. This is a common used pattern for handling context-bound stuff inside a ViewModel.

I highly decline this for several reasons. First: that usually means that you have to keep a reference to your NavigationController which fixes the context leak, but doesnt solve the architecture at all.

The best way (in my oppinion) is using LiveData which is lifecycle aware and can do all the wanted stuff.

如果您在 View 模型中实现 ui 操作,您可能会考虑另一个问题,如果您在 View 或上下文中收到 NullPointerException 或对其进行一些增强,您会首先找到哪个类? View 模型还是 Activity ?由于第一个操作是保存 UI 操作,第二个操作是保存数据绑定(bind)。在故障排除中这两种情况都可能发生。

关于android - 如何在 mvvm 模式中登录成功时从 viewmodel 启动 Activity ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47160241/

相关文章:

MVVM 与 Bloc 模式

java - 在 Kotlin 中使用 @EnableNeo4jRepositories(basePackageClasses = "myApp")

c# - 按照 MVVM 设计模式从 ViewModel 访问 this.content

c# - 组合框不会加载数据库值

java - 在 Android Java 中通过主键获取 Realm 对象的正确方法

javascript - 如何将网页不可用页面替换为自定义页面? (网络浏览)

android - 是否能感知observeForever的生命周期?

Kotlin 函数类型

android - Snackbar 多行文本填充

android - 如何更改支持工具栏菜单项的 onPress 行为?