2

Here's my code :

public ArrayList<Integer> getArray() {

    final FirebaseDatabase database = FirebaseDatabase.getInstance();

    final DatabaseReference myReff =database.getReference("server").child("user");


    myReff.addListenerForSingleValueEvent(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            boolean connected = dataSnapshot.getValue(Boolean.class);
            if (connected){
                Log.d("CONNECT","YES");
            }
            else {
                Log.d("CONNECT","NO");

            }

            GenericTypeIndicator<ArrayList<Integer>> genericTypeIndicator = new GenericTypeIndicator<ArrayList<Integer>>() {};
            array = dataSnapshot.getValue(genericTypeIndicator) ;
        }

        @Override
        public void onCancelled(DatabaseError databaseError) {

        }
    });


    return array;
}

Here's my database :

 "server" : {
  "user" : {
         1 : 0,
         2 : 0,
         3 : 0
 },
}

The purpose of this code is to retrieve an array in my database.

The problem is that OnDataChange is never called in this function

(I debug my code, put breakpoint in it but nothings happend. And CONNECT is not shown in Logcat).

I don't understand why because when a listener is added, ondatachange must be triggered at least once even if data has not changed.

Can you explain me why OnDataChange is not executed ?

Mxwan
  • 191
  • 13
  • Is onCancelled called? You need to check for that in case your read was rejected because of a security rule. – Doug Stevenson Feb 07 '18 at 21:48
  • Show us a representation of your database that you are trying to read. – Hasan Bou Taam Feb 07 '18 at 21:53
  • @DougStevenson Thanks for your reply. No onCancelled is neither called (Try to debug it with breakpoint and try to Log a message in it). My rules are : { "rules": { ".read": true, ".write": true } } – Mxwan Feb 07 '18 at 23:04
  • Where are you calling the `getArray()` method? – Rosário Pereira Fernandes Feb 07 '18 at 23:10
  • @RosárioPereiraFernandes I'm calling it in OnCreate inside another class. (like this : `activityObject.getArray().get(0);`)It's where the error appeared. It says `IndexOutOfBoundsException: Index: 0, Size: 0` because of my array which is return null by `getArray()`. – Mxwan Feb 07 '18 at 23:19
  • You can't simply return that array because the `onDataChange` method is asynchronous. Have a look at [this answer](https://stackoverflow.com/a/47853774/5861618) – Rosário Pereira Fernandes Feb 07 '18 at 23:21
  • @RosárioPereiraFernandes Thanks for the link, I've checked it. The final answer of your link is : `readData(new MyCallback() { Override public void onCallback(Arraylist array) { Log.d("TAG", array); } });`. But with this method, How can I use it to use array.get(0) outside "onCallback" ? – Mxwan Feb 07 '18 at 23:51
  • Is this the case when there is no data in database? you want to handle some event in such case? – Deep Patel Feb 08 '18 at 04:38
  • Possible duplicate of [How to return dataSnapshot value as a result of a method?](https://stackoverflow.com/questions/47847694/how-to-return-datasnapshot-value-as-a-result-of-a-method) – Alex Mamo Feb 08 '18 at 07:02
  • @AlexMamo Hi, I've seen your answer on this post. I understand that onDatachange is **asynchronous**. But I don't see how can I handle your answer in the purpose of returning an Array of my database and use it where in other functions. In your answer, readData is void type and cannot be change in other type (because if it was the case, readData will have the same problem than my method getArray() ). So I cannot use readData to have my array and manipulate it like I want (readData (....) .get(0) is impossible because it didn't return an array.) . Do you have another option ? Thanks a lot. – Mxwan Feb 08 '18 at 08:59
  • @AlexMamo Here's what I want to do. I have two activities. User enters an array in first activity. I send it to my database and go to my second activity. In my second activity I need to use this array. So I want to retrieve it from my database (here's the problem) and manipulate this array in my second activity. Then I send it to my database, which will be retrieved and used by my first activity. – Mxwan Feb 08 '18 at 09:05
  • But that's not the point regarding my answer from that post. You can add into your calback, everything you need, a single boolean value, an ArrayList and so on. And then you can use in which place you want an object of the class. – Alex Mamo Feb 08 '18 at 09:38
  • @AlexMamo Thanks for your answer but I'm not sure to fully understand you. Can you give me an example with my arrayList in onCallback? What's the point of onCallback method ? What's the use of onCallBack ? Let's say I'm reading my arrayList in onCallBack, how will I do to share it with other functions ? Thanks a lot – Mxwan Feb 10 '18 at 10:37
  • These are basically other questions and it's impossible for me to answer them in single comment. In order to follow the rules of this comunity, please post another fresh question, so me and other users can help you. – Alex Mamo Feb 10 '18 at 10:41

3 Answers3

1

When you add ListenerForSingleValueEvent or any other Firebase-listener, what happens is that your phone starts a service which connect to the internet and returns a result after he gets it. While your phone is connecting to the firebase server and returns the data, your function (public ArrayList<Integer> getArray()) already has finished. The function does not wait for the data to return, it continues.

Eventually, when your data returns from your Firebase-database, it calls the function public void onDataChange(DataSnapshot dataSnapshot) or to the function public void onCancelled(DatabaseError databaseError) which you have declared inside your new ValueEventListener(){}

So, in your case, if you want to return the array, you need to call to another function from the onDataChange(DataSnapshot dataSnapshot) function and pass the array as a parameter.

for example:

    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {
        boolean connected = dataSnapshot.getValue(Boolean.class);
        if (connected){
            Log.d("CONNECT","YES");
        }
        else {
            Log.d("CONNECT","NO");

        }

        GenericTypeIndicator<ArrayList<Integer>> genericTypeIndicator = new GenericTypeIndicator<ArrayList<Integer>>() {};
        array = dataSnapshot.getValue(genericTypeIndicator);

        handleArrayFunction(array);
    }

And outside your public ArrayList<Integer> getArray() function declare a new function like this:

    private void handleArrayFunction(ArrayList<Integer> array){ 
        //use your array for what you want
    }

Hope I was understandable! If something isn't clear enough feel free to say so :)

yotam ravits
  • 181
  • 10
  • Thanks for your reply :). You were fully understandable thanks. But my problem remains. My final purpose is to get my array from my database (with getArray method or another) and to use it with array.get(0) in onCreate of another activity. With handleArrayFunction, I can't do it (I think). Do you think it's possible ? If yes, how ? Thanks a lot. – Mxwan Feb 07 '18 at 23:57
  • It is not so elegant solution but you can pass a Context as another parameter on your getArray function and then, after you get the array, cast the context to tge other activity and call to another function in that activity (for example: ((MainActivity)context).useData(array.get (0))). But again, it's pretty ugly and limited solution. – yotam ravits Feb 08 '18 at 00:06
0

I wondered for the same issue yesterday, Look at this code snippet how the things got resolved.

The logic is, there is some data and its getting changed (update or remove) onDataChanged will get triggered. but when there is no data initially, it will go to onError.

Please check the snippet, i think you will find the answer.

                @Override
                public void onDataChanged() {
                    // If there are no chat messages, show a view that invites the user to add a message.
                    mBinder.progress.setVisibility(View.GONE); // tried to hide progressbar on data changed when there is no data found but didn't worked.
                    mBinder.rvChatUsersList.llNoData.setVisibility(getItemCount() == 0 ? View.VISIBLE : View.GONE);
                }

                @Override
                public void onError(@NonNull DatabaseError error) {
                    super.onError(error);
                    mBinder.progress.setVisibility(View.GONE); // tried to hide progressbar on error due to no data found and it worked.
                }
Deep Patel
  • 2,584
  • 2
  • 15
  • 28
  • Hi thanks for your help but it didn't work. onError is not recognize as a method from this superclass. And if I remove overide annotation, it didn't work also. – Mxwan Feb 08 '18 at 08:05
-1
Just write it.

public ArrayList<Integer> getArray() {

    final FirebaseDatabase database = FirebaseDatabase.getInstance();

    final DatabaseReference myReff =database.getReference("server").child("user");

    myReff.addValueEventListener
(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            boolean connected = dataSnapshot.getValue(Boolean.class);
            if (connected){
                Log.d("CONNECT","YES");
            }
            else {
                Log.d("CONNECT","NO");
            }
            GenericTypeIndicator<ArrayList<Integer>> genericTypeIndicator = new GenericTypeIndicator<ArrayList<Integer>>() {};
            array = dataSnapshot.getValue(genericTypeIndicator) ;
        }
        @Override
        public void onCancelled(DatabaseError databaseError) {

        }
    });
    return array;
}
Chetan Patel
  • 180
  • 8
  • Hi, I don't understand what's different from my code ? If it is `myReff.addValueEventListener` instead of `myReff.addListenerForSingleValueEvent`, it's the same, it didn't work also – Mxwan Feb 08 '18 at 08:07