我是 MVVM 新手,正在尝试弄清楚如何组织我的应用程序。我制作了一个简化的登录应用程序来帮助解决问题。
我将应用程序分为几层:Activity -> ViewModel -> Repository -> Database。
我有几个 Activity,每个 Activity 都有其特定的 ViewModel(即 LoginActivity 访问 LoginViewModel)。有一个存储库用于通过 Dao 接口访问 Room 数据库。所有 ViewModel 都包含一个 LocalRepository 变量。
我知道活动应该只处理用户界面作业。因此,LoginActivity 通过 EditText 视图从用户那里获取用户名,显示成功和失败 Toast,启动下一个活动等。但要实际登录用户,需要调用 loginViewModel.login(username),以便 ViewModel 可以处理所有业务逻辑。
LoginViewModel 需要检查用户是否已存在于数据库中。但是ViewModel应该如何等待数据库结果呢?
currentUser.observe(LoginActivity.this, new Observer<User>()
从 LoginActivity.this
返回错误:“'LoginActivity'不是封闭类”)。myViewModel.getUserByName(username).observe(this, new Observer<User>() {
@Override
public void onChanged(User userFromDatabase) {
// If userFromDatabase is not null, login as this user
}
});
也许我遗漏了架构的结构。
或者有没有一种从 ViewModel 访问数据库的技术?
或者我读到关于添加另一个“模型”层?还是服务?
这么多的技术和选择,希望有人能解决问题。
干杯。这是代码:
登录活动:
public void loginButtonClicked(View v){
// TODO: Stub. Login user
String msg = myViewModel.loginUser(splashUserInput.getText().toString());
if(msg.isEmpty()){
Intent intent = new Intent(this, DEFAULT_FIRST_ACTIVITY);
startActivity(intent);
} else {
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
}
}
LoginViewModel:newUser 返回 null,即使它确实存在于数据库中。
public String loginUser(String username) {
if(username.isEmpty()) {
// If empty user input
return "Username field missing";
} else if(currentUser.getValue() != null && username.equals(currentUser.getValue().getUsername())) {
// If user is already logged on
return null; // No errors
} else {
// Else if logging on to a different user
User oldUser = currentUser.getValue();
User newUser;
newUser = localRepository.getUserByName(username).getValue();
// newUser is null since the repository's background thread is still accessing the database.
if(newUser == null) {
return "Login Failed. Username does not exist.";
} else {
updateCurrentUser(oldUser, newUser);
return null; // No errors
}
}
}
存储库:
public LiveData<User> getUserByName(String name) {
return myDao.getUserByName(name);
}
我的道:
@Query("SELECT * FROM user_table WHERE username = :username")
LiveData<User> getUserByName(String username);
编辑 2022 年 4 月 22 日:以下是我尝试过的一些选项,但仍然没有解决方案:
在存储库中:
// getAllMarks returns LiveData<List<Mark>> from myDao
// Just grabbing the value returns null since the background thread is still grabbing the data
List<Mark> localMarks = repositoryLocal.getAllMarks.getValue(); // null
// So I tried to wait for the data, but I can't find the LifecycleOwner from within the repository.
// "this" should be the activity that called the viewModel that called the repository. (Required type: LifecycleOwner)
// Is it appropriate to just pass Activity down the line through viewModel to repository?
repositoryLocal.getAllMarks.observe(this, new Observer<List<Mark>>() {
@Override
public void onChanged(List<Mark> marks) {
localMarks = marks;
}
});
// getAllMarksValue returns List<Mark> from myDao
// IllegalStateException: will lock main thread
List<Mark> localMarks = repositoryLocal.getAllMarksValue();
您的用例不需要 LiveData,您只需从 Dao 返回 User 即可。喜欢, 我的道:
@Query("SELECT * FROM user_table WHERE username = :username")
User getUserByName(String username);
存储库:
public User getUserByName(String name) {
return myDao.getUserByName(name);
}
LiveData 仅应在底层数据频繁更改并且我们想要相应地进行一些更改时使用。
当用户点击登录时,用户名和密码被发送到服务器,服务器响应用户信息以及令牌,始终使用您的应用程序存储库将服务器的响应存储到数据库中,服务器的响应是您的新用户,当您注销时,您需要清除数据库以存储新用户信息,如果您有所有用户的数据库表,您可以通过在您的数据库中添加另一列来获取登录用户名为“token”的数据库,因为只有登录的用户才能拥有令牌,所有其他用户都不能拥有令牌,然后在您的 DAO 中执行此操作
@Query("SELECT * FROM users WHERE token IS NOT NULL ")
User getLoggedInUser();
要获得新用户,您可以这样做
if(appDatabase.getLoggedInUser() != null){
// do something
}