当前位置: 首页 > 知识库问答 >
问题:

如何从内部类修改外部类的对象,以便从Firebase检索类对象

艾安和
2023-03-14

我目前正在进行一个与Firebase集成的Android项目,但是由于Firebase监听器的原因,我正在努力从Firebase实时数据库中检索类对象。

让我详细介绍一下我的项目,以便你能了解它的主要内容。我使用MVVM架构,我有两个活动,其中一个用于身份验证,另一个用于主要用途。

第一个活动包含注册、登录、密码重置片段第二个活动包含4个用于程序主要功能的片段

第一个活动中的片段使用共享的viewmodel“AuthViewModel”,其中包含调用AuthRepository对象的方法的方法和向片段提供数据的LiveData变量

我所有的Firebase Auth操作都在这个AuthRepository的方法中处理,例如这是AuthRepository中的注册方法,它使用FireBase Auth方法注册用户,同时通过创建User对象将User添加到实时数据库中。

    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中,它是负责第二个活动中4个片段的ViewModel。

无论我尝试了什么,我都无法检索相应用户的User对象并将该对象分配给Main Repository类中的user变量。

问题不在于检索数据,因为"IN LISTENER:"Toast在实时数据库中显示正确的User对象

但是,“OUT LISTENER:”和“FINAL:”Toast显示在侦听器之外初始化的用户对象。我不知道为什么,但侦听器中的用户对象似乎是类中用户对象的副本,即使它们共享相同的哈希代码,如果我不将用户对象分配给firebase中的用户对象。

尽管进行了长时间的搜索,但我无法从内部类访问“真实”对象“而不是外部类的副本”。即使是我似乎也在访问和调用外部对象的方法,但它们不会持久存在于侦听器之外。

这里有什么问题,我能做些什么来解决?如果无法解决,我该怎么办?

不同输出

Toast.make文本(应用程序,收听者:user.hash代码()user.get部(),祝酒词。LENGTH_SHORT). show();

相同输出

干杯makeText(应用程序,“OUT LISTENER:”user.hashCode()user.getDepartment(),Toast.LENGTH\u SHORT)。show();

干杯makeText(应用程序,“FINAL:”user.hashCode()user.getDepartment(),Toast.LENGTH\u 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;
}

}

共有1个答案

张兴旺
2023-03-14

问题不在于从哪里访问数据库中的数据,而在于何时访问。

数据是从Firebase(和大多数云API)异步加载的,因为获取数据可能需要一些时间。加载数据时,主代码/线程将继续执行。然后,当代码可用时,将使用该数据调用侦听器上的onDataChange

这意味着主线程上的任何代码(例如Toast.makeText(应用程序,“OUT LISTENER:”user.hashCode()user.getDepartment(),Toast.LENGTH\u SHORT.).show())都将在调用onDataChange并完成其mainRepository之前运行。setUser(snapshot.getValue(User.class))。验证这一点的最简单方法是在这些行上设置断点并在调试器中运行代码,或者记录一些输出并在logcat中检查其输出顺序。

解决方案总是一样的:任何需要数据库数据的代码都需要在onDataChange中,从那里调用,或者以其他方式进行同步。

有关此问题的一个很好的示例,并使用这些方法,请参阅:

  • getConrectsFromFirebase()方法返回空列表
  • 在Firebase Listener中设置Singleton属性值
  • Android Firebase越来越空
 类似资料:
  • 问题内容: 我有以下代码。我想掌握用来创建内部类对象的外部类对象。我该怎么做? 编辑:好,你们中的一些人建议通过添加方法来修改内部类: 但是,如果我没有控制权来修改内部类,那(只是确认一下),我们还有其他方法可以从内部类对象中获取相应的外部类对象吗? 问题答案: 在内部类本身中,你可以使用。该表达式允许引用任何词法包围的实例,在JLS中被描述为。 我认为没有办法从内部类的代码之外获取实例。当然,你

  • 问题内容: 是否可以从Java内部类中获取对它的引用? 即 问题答案: 您可以像这样访问外部类的实例:

  • 问题内容: 考虑以下Python(在2.x或3.x中运行): 我想把手放在里面。但: 我不想成为的显式参数。 我想和成为一个类的对象,不是很奇怪像关闭。 您能建议我如何实现这一目标吗? 现在,我最好的主意是使用线程本地存储。在我的用例中,每当我构造一个时,我已经在某个地方的方法中,并且添加没什么大不了的 对我的代码。 这使您对我愿意考虑的堕落程度有所了解。 问题答案: 在Python 2.6中,也

  • 问题内容: 在下面显示的代码片段中,内部类本身继承了外部类。 方法中的唯一语句(最后一个片段)将值分配给类的私有字段,然后调用该方法。 该方法会导致另一个字符串- 要设置的私人领域中的类调用之前的方法延长。 因此,方法中的以下两个语句: 应该显示 另一个价值 另一个价值 但是他们显示 初始值 初始值 为什么会这样? 问题答案: 方法和领域都是。因此,其他子类(包括子类)均无法访问它们。它们不是继承

  • 问题内容: 如何从内部类访问外部类? 我正在重写一种使它在不同线程上运行的方法。从内联线程中,我需要调用原始方法,但是当然只要调用就会变成无限递归。 具体来说,我在扩展BufferedReader: 这个地方给了我我找不到的NullPointerException。 谢谢。 问题答案: 像这样: 上面的测试在执行时显示:

  • 问题内容: 这不是直截了当的问题。在我的情况下,外部类变量和内部类setter方法的参数名称相同。喜欢: 现在,我无法初始化外部类实例变量p,因为它指示内部类。再次,我做不到它得到一个错误。现在如何分配外部p,同时将内部Class方法的参数命名为p? 问题答案: 这是您可以/应该这样做的方式: