Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/392.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何作为方法的结果返回DataSnapshot值?_Java_Android_Firebase_Firebase Realtime Database - Fatal编程技术网

Java 如何作为方法的结果返回DataSnapshot值?

Java 如何作为方法的结果返回DataSnapshot值?,java,android,firebase,firebase-realtime-database,Java,Android,Firebase,Firebase Realtime Database,我对Java没有太多经验。我不确定这个问题是否愚蠢,但我需要从Firebase实时数据库中获取一个用户名,并返回此名称作为此方法的结果。所以,我知道了如何获得这个值,但我不知道如何将它作为这个方法的结果返回。最好的方法是什么 private String getUserName(String uid) { databaseReference.child(String.format("users/%s/name", uid)) .addListenerForSing

我对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) {}
    });
}

我相信我理解你的要求。尽管您说希望从fetch方法“返回”它(本身),但可以说您实际上只是希望能够在fetch完成后使用检索到的值。如果是这样,您需要做的就是:

  • 在类的顶部创建一个变量
  • 检索您的值(您基本上正确地完成了此操作)
  • 将类中的公共变量设置为检索到的值
  • 一旦获取成功,就可以使用变量执行许多操作。4a和4b是一些简单的例子:

    4a编辑: 作为一个使用示例,您可以在使用
    yournamerable
    的类中触发需要运行的任何其他命令(并且您可以确保它
    yournamerable
    不为null)

    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
    是否为空,因为
    onDataChange
    是异步的,并且在尝试在其他地方使用它时可能尚未完成。

    这是异步web API的经典问题。您现在无法返回尚未加载的内容。换句话说,您不能简单地创建一个全局变量并在
    onDataChange()
    方法之外使用它,因为它总是
    null
    。之所以发生这种情况,是因为
    onDataChange()
    方法被称为异步方法。根据您的连接速度和状态,数据可用可能需要几百毫秒到几秒钟的时间

    但不仅Firebase实时数据库以异步方式加载数据,而且几乎所有现代web API也以异步方式加载数据,因为这可能需要一些时间。因此,当数据加载到辅助线程时,主应用程序代码将继续,而不是等待数据(这可能会导致用户出现无响应的应用程序对话框)。然后,当数据可用时,将调用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
    的原因

    大多数开发人员的最初反应是尝试“修复”这一
    异步行为,我个人建议不要这样做。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) {}
    });
    
    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(new MyCallback() {
        @Override
        public void onCallback(String value) {
            Log.d("TAG", value);
        }
    });
    
    如果你想在外面使用,还有另一种方法。您需要创建自己的回调以等待Firebase返回数据。要实现这一点,首先需要创建一个
    接口,如下所示:

    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) {}
    });
    
    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(new MyCallback() {
        @Override
        public void onCallback(String value) {
            Log.d("TAG", value);
        }
    });
    
    然后,您需要创建一个从数据库中实际获取数据的方法。此方法应如下所示:

    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) {}
    });
    
    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(new MyCallback() {
        @Override
        public void onCallback(String value) {
            Log.d("TAG", value);
        }
    });
    
    最后,只需调用
    readData()
    方法,并在需要时将
    MyCallback
    接口的实例作为参数传递,如下所示:

    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) {}
    });
    
    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(new MyCallback() {
        @Override
        public void onCallback(String value) {
            Log.d("TAG", value);
        }
    });
    
    这是在
    onDataChange()
    方法之外使用该值的唯一方法。有关更多信息,您还可以查看此

    编辑:2021年2月26日

    有关更多信息,您可以查看以下文章:

    以及以下短片:


      • 这里有一个疯狂的想法,在onDataChange中,将其放在一个可视性消失的文本视图中
        textview.setVisiblity(消失)
        或其他什么, 然后做一些类似的事情

        textview.setText(dataSnapshot.getValue(String.class))
        
        然后稍后使用
        textview.getText().toString()获取它


        这只是一个疯狂的简单想法。

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

        // 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);
        

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

        使用
        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;
        }
        
        getUserName().observe(this, new Observer<String>() {
            @Override
            public void onChanged(String userName) {
                //here, do whatever you want on `userName`
            }
        });
        
        从版本开始,Firebase Realtime Database SDK包含一个名为get()的新方法,可以在DatabaseReference或查询对象上调用该方法:

        添加了DatabaseReference#get()和Query#get(),它们从服务器返回数据,即使本地缓存中有较旧的数据可用

        我们已经知道,Firebase API是异步的。所以我们需要为此创建一个回调。首先,让我们创建一个接口:

        public interface FirebaseCallback {
            void onResponse(String name);
        }
        
        和一种方法