如何从内部 class 修改外部 class 的对象,以便从 firebase 检索 class 对象
How to modify objects of outer class from an inner class so as to retrieve class object from firebase
我目前正在使用 Firebase 集成开发一个 Android 项目,但是由于 Firebase 侦听器,我正在努力从 Firebase 实时数据库中检索 class 对象。
让我详细说明一下我的项目,以便您了解它的主要思想。我使用 MVVM 架构,我有 2 个 Activity,其中一个用于身份验证,另一个用于主要用途。
第一个 activity 包含注册、登录、密码重置片段
第二个 activity 包含 4 个片段,用于程序的主要功能
第一个 activity 中的片段使用共享视图模型“AuthViewModel”,其中包含调用 AuthRepository 对象方法的方法和向片段提供数据的 LiveData 变量
而我所有的Firebase Auth操作都是在这个AuthRepository的方法中处理的,例如这是AuthRepository中的注册方法,它使用FireBase Auth方法注册用户,同时通过创建用户对象将用户添加到实时数据库中。
public void register(String email , String pass, String username, String department){
auth.createUserWithEmailAndPassword(email , pass).addOnCompleteListener(new OnCompleteListener<AuthResult>() {
@Override
public void onComplete(@NonNull Task<AuthResult> task) {
if (task.isSuccessful()){
firebaseUserMutableLiveData.postValue(auth.getCurrentUser());
// Add user to realtime database
FirebaseUser user = auth.getCurrentUser();
dbRef.child("Users").child(user.getUid()).setValue(new User(email, username, department));
}
else{
Toast.makeText(application, task.getException().getMessage(), Toast.LENGTH_SHORT).show();
}
}
});
}
既然你已经掌握了主要思想,那么让我们继续解决我的问题。就像 AuthRepository 一样,我有一个 MainRepository 作为变量添加到 MainViewModel 中,它是负责第二个 activity.
中 4 个片段的 ViewModel
无论我尝试过什么,我都无法检索相应用户的用户对象并将此对象分配给 MainRepository 中的用户变量 class。
问题不在于检索数据,因为 "IN LISTENER: "Toast 在实时数据库中显示正确的用户对象
然而 "OUT LISTENER: " and "FINAL: " Toast 显示 User 对象已初始化出侦听器。我不知道为什么,但监听器中的用户对象似乎是 class 中用户对象的副本,即使如果我不将用户对象分配给 firebase 中的用户对象,它们也共享相同的哈希码.
尽管进行了长时间的搜索,但我无法从内部 class 访问外部 class 的“真实”对象 “不是副本”。即使我似乎正在访问和调用外部对象的方法,它们也不会持续存在于侦听器之外。
这里有什么问题,我能做些什么来解决它?如果无法解决,我应该怎么做?
不同的输出
Toast.makeText(application, "IN LISTENER: "+ user.hashCode() + user.getDepartment(), Toast.LENGTH_SHORT).show();
相同的输出
Toast.makeText(application, "OUT LISTENER: "+ user.hashCode() + user.getDepartment(), Toast.LENGTH_SHORT).show();
Toast.makeText(申请, "最终: "+ user.hashCode() + user.getDepartment(), Toast.LENGTH_SHORT).show();
public class MainRepository {
private Application application;
private FirebaseAuth auth;
private DatabaseReference dbRef;
private MutableLiveData<User> userMutableLiveData;
private User user;
public MainRepository(Application application) {
this.application = application;
auth = FirebaseAuth.getInstance();
dbRef = FirebaseDatabase.getInstance().getReference();
getUser();
Toast.makeText(application, "FINAL: " + user.hashCode() + user.getDepartment(), Toast.LENGTH_SHORT).show();
}
class ListenerInner implements ValueEventListener {
MainRepository mainRepository;
ListenerInner(MainRepository mainRepository) {
this.mainRepository = mainRepository;
}
@Override
public void onDataChange(@NonNull DataSnapshot snapshot) {
mainRepository.setUser(snapshot.getValue(User.class));
Toast.makeText(application, "IN LISTENER: "+ user.hashCode() + user.getDepartment(), Toast.LENGTH_SHORT).show();
}
@Override
public void onCancelled(@NonNull DatabaseError error) {
Toast.makeText(application, "Error: " + error.getMessage(), Toast.LENGTH_SHORT).show();
}
}
public void getUser() {
setUser(new User("aaa", "bbb", "ccc"));
dbRef.child("Users").child(auth.getCurrentUser().getUid()).addListenerForSingleValueEvent(new ListenerInner(this));
Toast.makeText(application, "OUT LISTENER: " + user.hashCode() + user.getDepartment(), Toast.LENGTH_SHORT).show();
}
private void setUser(User user) {
this.user = user;
}
}
问题不在于您在何处访问数据库中的数据,而是何时访问它。
数据是从 Firebase(和大多数云 API)异步加载的,因为获取它可能需要一些时间。在加载数据时,您 main code/thread 继续执行。然后当代码可用时,将使用该数据调用侦听器上的 onDataChange
。
这意味着主线程上的任何代码(例如 Toast.makeText(application, "OUT LISTENER: " + user.hashCode() + user.getDepartment(), Toast.LENGTH_SHORT).show()
)都将在 onDataChange
被调用并完成其 mainRepository.setUser(snapshot.getValue(User.class))
之前 运行。验证这一点的最简单方法是在这些行上设置断点并 运行 在调试器中调试代码,或者记录一些输出并在 logcat.
中检查其输出顺序
这个问题的解决方案总是相同的:任何需要数据库数据的代码都需要在 onDataChange
中,从那里调用,或者以其他方式同步。
有关此问题和使用这些方法的一个很好的例子,请参阅:
- Android firebase getting null
我目前正在使用 Firebase 集成开发一个 Android 项目,但是由于 Firebase 侦听器,我正在努力从 Firebase 实时数据库中检索 class 对象。
让我详细说明一下我的项目,以便您了解它的主要思想。我使用 MVVM 架构,我有 2 个 Activity,其中一个用于身份验证,另一个用于主要用途。
第一个 activity 包含注册、登录、密码重置片段 第二个 activity 包含 4 个片段,用于程序的主要功能
第一个 activity 中的片段使用共享视图模型“AuthViewModel”,其中包含调用 AuthRepository 对象方法的方法和向片段提供数据的 LiveData 变量
而我所有的Firebase Auth操作都是在这个AuthRepository的方法中处理的,例如这是AuthRepository中的注册方法,它使用FireBase Auth方法注册用户,同时通过创建用户对象将用户添加到实时数据库中。
public void register(String email , String pass, String username, String department){
auth.createUserWithEmailAndPassword(email , pass).addOnCompleteListener(new OnCompleteListener<AuthResult>() {
@Override
public void onComplete(@NonNull Task<AuthResult> task) {
if (task.isSuccessful()){
firebaseUserMutableLiveData.postValue(auth.getCurrentUser());
// Add user to realtime database
FirebaseUser user = auth.getCurrentUser();
dbRef.child("Users").child(user.getUid()).setValue(new User(email, username, department));
}
else{
Toast.makeText(application, task.getException().getMessage(), Toast.LENGTH_SHORT).show();
}
}
});
}
既然你已经掌握了主要思想,那么让我们继续解决我的问题。就像 AuthRepository 一样,我有一个 MainRepository 作为变量添加到 MainViewModel 中,它是负责第二个 activity.
中 4 个片段的 ViewModel无论我尝试过什么,我都无法检索相应用户的用户对象并将此对象分配给 MainRepository 中的用户变量 class。
问题不在于检索数据,因为 "IN LISTENER: "Toast 在实时数据库中显示正确的用户对象
然而 "OUT LISTENER: " and "FINAL: " Toast 显示 User 对象已初始化出侦听器。我不知道为什么,但监听器中的用户对象似乎是 class 中用户对象的副本,即使如果我不将用户对象分配给 firebase 中的用户对象,它们也共享相同的哈希码.
尽管进行了长时间的搜索,但我无法从内部 class 访问外部 class 的“真实”对象 “不是副本”。即使我似乎正在访问和调用外部对象的方法,它们也不会持续存在于侦听器之外。
这里有什么问题,我能做些什么来解决它?如果无法解决,我应该怎么做?
不同的输出
Toast.makeText(application, "IN LISTENER: "+ user.hashCode() + user.getDepartment(), Toast.LENGTH_SHORT).show();
相同的输出
Toast.makeText(application, "OUT LISTENER: "+ user.hashCode() + user.getDepartment(), Toast.LENGTH_SHORT).show();
Toast.makeText(申请, "最终: "+ user.hashCode() + user.getDepartment(), Toast.LENGTH_SHORT).show();
public class MainRepository {
private Application application;
private FirebaseAuth auth;
private DatabaseReference dbRef;
private MutableLiveData<User> userMutableLiveData;
private User user;
public MainRepository(Application application) {
this.application = application;
auth = FirebaseAuth.getInstance();
dbRef = FirebaseDatabase.getInstance().getReference();
getUser();
Toast.makeText(application, "FINAL: " + user.hashCode() + user.getDepartment(), Toast.LENGTH_SHORT).show();
}
class ListenerInner implements ValueEventListener {
MainRepository mainRepository;
ListenerInner(MainRepository mainRepository) {
this.mainRepository = mainRepository;
}
@Override
public void onDataChange(@NonNull DataSnapshot snapshot) {
mainRepository.setUser(snapshot.getValue(User.class));
Toast.makeText(application, "IN LISTENER: "+ user.hashCode() + user.getDepartment(), Toast.LENGTH_SHORT).show();
}
@Override
public void onCancelled(@NonNull DatabaseError error) {
Toast.makeText(application, "Error: " + error.getMessage(), Toast.LENGTH_SHORT).show();
}
}
public void getUser() {
setUser(new User("aaa", "bbb", "ccc"));
dbRef.child("Users").child(auth.getCurrentUser().getUid()).addListenerForSingleValueEvent(new ListenerInner(this));
Toast.makeText(application, "OUT LISTENER: " + user.hashCode() + user.getDepartment(), Toast.LENGTH_SHORT).show();
}
private void setUser(User user) {
this.user = user;
}
}
问题不在于您在何处访问数据库中的数据,而是何时访问它。
数据是从 Firebase(和大多数云 API)异步加载的,因为获取它可能需要一些时间。在加载数据时,您 main code/thread 继续执行。然后当代码可用时,将使用该数据调用侦听器上的 onDataChange
。
这意味着主线程上的任何代码(例如 Toast.makeText(application, "OUT LISTENER: " + user.hashCode() + user.getDepartment(), Toast.LENGTH_SHORT).show()
)都将在 onDataChange
被调用并完成其 mainRepository.setUser(snapshot.getValue(User.class))
之前 运行。验证这一点的最简单方法是在这些行上设置断点并 运行 在调试器中调试代码,或者记录一些输出并在 logcat.
这个问题的解决方案总是相同的:任何需要数据库数据的代码都需要在 onDataChange
中,从那里调用,或者以其他方式同步。
有关此问题和使用这些方法的一个很好的例子,请参阅:
- Android firebase getting null