java—如何作为方法的结果返回datasnapshot值?

svmlkihl  于 2021-07-09  发布在  Java
关注(0)|答案(5)|浏览(281)

我对java没有太多经验。我不确定这个问题是否愚蠢,但我需要从firebase实时数据库中获取用户名,并将此名称作为此方法的结果返回。所以,我知道如何得到这个值,但我不知道如何返回它作为这个方法的结果。最好的办法是什么?

private String getUserName(String uid) {
    databaseReference.child(String.format("users/%s/name", uid))
            .addListenerForSingleValueEvent(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            // How to return this value?
            dataSnapshot.getValue(String.class);
        }

        @Override
        public void onCancelled(DatabaseError databaseError) {}
    });
}
of1yzvn4

of1yzvn41#

这里有一个疯狂的想法,在ondatachange中,把它放到一个没有可见性的文本视图中 textview.setVisiblity(Gone) 或者做些什么,然后像

textview.setText(dataSnapshot.getValue(String.class))

然后再拿回来 textview.getText().toString() 只是一个疯狂的简单想法。

bbmckpt7

bbmckpt72#

这是异步webapi的一个典型问题。您现在不能返回尚未加载的内容。换句话说,您不能简单地创建一个全局变量并在外部使用它 onDataChange() 方法,因为它总是 null . 这是因为 onDataChange() 方法称为异步。根据您的连接速度和状态,可能需要几百毫秒到几秒钟才能获得数据。
但是,不仅firebase实时数据库异步加载数据,而且几乎所有现代webapi都是这样,因为这可能需要一些时间。因此,不必等待数据(这可能导致用户的应用程序对话框没有响应),而是在将数据加载到辅助线程时继续执行主应用程序代码。当数据可用时,将调用ondatachange()方法,并可以使用数据。换句话说,到那时 onDataChange() 方法调用时,数据尚未加载。
让我们举一个例子,通过在代码中放置一些日志语句,来更清楚地看到发生了什么。

private String getUserName(String uid) {
    Log.d("TAG", "Before attaching the listener!");
    databaseReference.child(String.format("users/%s/name", uid)).addListenerForSingleValueEvent(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            // How to return this value?
            dataSnapshot.getValue(String.class);
            Log.d("TAG", "Inside onDataChange() method!");
        }

        @Override
        public void onCancelled(DatabaseError databaseError) {}
    });
    Log.d("TAG", "After attaching the listener!");
}

如果我们运行此代码,输出将是:
在连接侦听器之前!
连接侦听器之后!
ondatachange()方法内部!
这可能不是您所期望的,但它确切地解释了为什么您的数据 null 归还的时候。
对于大多数开发人员来说,最初的React是尝试“修复”这个问题 asynchronous behavior 我个人建议不要这样做。web是异步的,您越早接受这一点,就越早学会如何使用现代web API提高效率。
我发现为这个异步范例重新构造问题是最容易的。我没有说“首先获取数据,然后记录”,而是将问题定义为“开始获取数据”。加载数据时,记录它”。这意味着任何需要数据的代码都必须在 onDataChange() 方法或从内部调用,如下所示:

databaseReference.child(String.format("users/%s/name", uid)).addListenerForSingleValueEvent(new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {
        // How to return this value?
        if(dataSnapshot != null) {
            System.out.println(dataSnapshot.getValue(String.class));
        }
    }

    @Override
    public void onCancelled(DatabaseError databaseError) {}
});

如果你想在外面使用,还有另一种方法。您需要创建自己的回调来等待firebase返回数据。要实现这一点,首先,您需要创建 interface 这样地:

public interface MyCallback {
    void onCallback(String value);
}

然后您需要创建一个从数据库中实际获取数据的方法。此方法应如下所示:

public void readData(MyCallback myCallback) {
    databaseReference.child(String.format("users/%s/name", uid)).addListenerForSingleValueEvent(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            String value = dataSnapshot.getValue(String.class);
            myCallback.onCallback(value);
        }

        @Override
        public void onCancelled(DatabaseError databaseError) {}
    });
}

最后只要打个电话就行了 readData() 方法并传递 MyCallback 接口作为参数,在需要时如下所示:

readData(new MyCallback() {
    @Override
    public void onCallback(String value) {
        Log.d("TAG", value);
    }
});

这是您可以在外部使用该值的唯一方法 onDataChange() 方法。更多信息,你也可以看看这个视频。

lo8azlld

lo8azlld3#

使用 LiveData 作为返回类型并观察其值的变化以执行所需的操作。

private MutableLiveData<String> userNameMutableLiveData = new MutableLiveData<>();

public MutableLiveData<String> getUserName(String uid) {

    databaseReference.child(String.format("users/%s/name", uid))
            .addListenerForSingleValueEvent(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            // How to return this value?
            String userName = dataSnapshot.getValue(String.class);
            userNameMutableLiveData.setValue(userName);
        }

        @Override
        public void onCancelled(DatabaseError databaseError) {}
    });

    return userNameMutableLiveData;
}

然后从你的 Activity/Fragment 观察 LiveData 还有里面 onChanged 做你想要的手术。

getUserName().observe(this, new Observer<String>() {
    @Override
    public void onChanged(String userName) {
        //here, do whatever you want on `userName`
    }
});
3ks5zfa0

3ks5zfa04#

我相信我理解你的要求。尽管您说您想从fetch方法“返回”它(本身),但您实际上只想在fetch完成后使用检索到的值就足够了。如果是这样,您需要做的是:
在类的顶部创建一个变量
找回你的价值(你做得基本正确)
将类中的公共变量设置为检索到的值
一旦获取成功,就可以使用变量执行许多操作。4a和4b是一些简单的例子:
4a级。edit:作为一个使用示例,您可以触发在使用 yourNameVariable )你可以肯定 yourNameVariable (不为空)
第4b条。编辑:作为一个使用示例,您可以在由按钮的onclicklistener触发的函数中使用变量。
试试这个。

// 1. Create a variable at the top of your class
private String yourNameVariable;

// 2. Retrieve your value (which you have done mostly correctly)
private void getUserName(String uid) {
    databaseReference.child(String.format("users/%s/name", uid))
            .addListenerForSingleValueEvent(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            // 3. Set the public variable in your class equal to value retrieved
            yourNameVariable = dataSnapshot.getValue(String.class);
            // 4a. EDIT: now that your fetch succeeded, you can trigger whatever else you need to run in your class that uses `yourNameVariable`, and you can be sure `yourNameVariable` is not null.
            sayHiToMe();
        }

        @Override
        public void onCancelled(DatabaseError databaseError) {}
    });
}

// (part of step 4a)
public void sayHiToMe() {
  Log.d(TAG, "hi there, " + yourNameVariable);
}

// 4b. use the variable in a function triggered by the onClickListener of a button.
public void helloButtonWasPressed() {
  if (yourNameVariable != null) {
    Log.d(TAG, "hi there, " + yourNameVariable);
  }
}

然后,你可以使用 yourNameVariable 无论你想在课堂上什么地方。
注意:一定要检查一下 yourNameVariable 使用时不为null,因为 onDataChange 是异步的,在尝试在其他地方使用时可能尚未完成。

i86rm4rw

i86rm4rw5#

这不是解决方案,只是访问代码组织方法之外的数据的一种方法。

// Get Your Value
private void getValue() {

    fbDbRefRoot.child("fbValue").addListenerForSingleValueEvent(new ValueEventListener() {

        @Override
        public void onDataChange(@NonNull DataSnapshot dataSnapshot) {

            String yourValue = (String) dataSnapshot.getValue();
            useValue(yourValue);

        }

        @Override
        public void onCancelled(@NonNull DatabaseError databaseError) {

        }
    });

}

// Use Your Value
private void useValue(String yourValue) {

    Log.d(TAG, "countryNameCode: " + yourValue);

}

实现结果的另一种方式(但不一定是解决方案)
声明公共变量

public static String aPublicVariable;

在异步方法中设置此变量

aPublicVariable = (String) dataSnapshot.getValue();

在任何地方使用变量

Log.d(TAG, "Not Elegant: " + aPublicVariable);

在第二种方法中,如果异步调用不长,那么它几乎可以一直工作。

相关问题