Judy T Raj
Judy T Raj

Reputation: 1844

Firebase/Android: Adding retrieved values from Firebase to arraylist returns null pointer exception

I'm trying the add the retrieved values from Firebase database to an Arraylist and from there to a String array. My retrieval method works fine. I can have all the values printed out in a toast. But apparently it doesn't get added to the arraylist.

Here's my code for retrieval in onActivityCreated() of fragment class.

ArrayList<String> allBrands = new ArrayList<>();
brandRef=FirebaseDatabase.getInstance().getReferenceFromUrl("https://stockmanager-142503.firebaseio.com/Brands");
        q=brandRef.orderByChild("brandName");
        q.addChildEventListener(new ChildEventListener() {
            @Override
            public void onChildAdded(DataSnapshot dataSnapshot, String s) {
               allBrands.add((dataSnapshot.getValue(Brand.class)).getBrandName());
                Toast.makeText(getActivity(),(dataSnapshot.getValue(Brand.class)).getBrandName(), Toast.LENGTH_SHORT).show();

            }

            @Override
            public void onChildChanged(DataSnapshot dataSnapshot, String s) {

            }

            @Override
            public void onChildRemoved(DataSnapshot dataSnapshot) {

            }

            @Override
            public void onChildMoved(DataSnapshot dataSnapshot, String s) {

            }

            @Override
            public void onCancelled(DatabaseError databaseError) {

            }
        });

And this is where I'm trying to use the arrayList in OnActivityResult() method of the Fragment class but the iterator loop is not executed I believe. The toast is not seen. I'm getting a null pointer exception when I try to work with the array. I assume the values do not get copied to the brands array.

count=allBrands.size();
                                String[] brands=new String[count];
                                Iterator<String> itemIterator = allBrands.iterator();
                                if(itemIterator.hasNext()){
                                    //brands[i] = itemIterator.next();
                                    Toast.makeText(getActivity(), itemIterator.next(), Toast.LENGTH_SHORT).show();
                                   // i++;

                                }
                               for( i=0;i<count;i++){
                                    if(brands[i].compareTo(Brand)==0){
                                        f=1;break;
                                    }
                                }

Here's my database in case that helps. But I can print out all the retrieved values in a Toast with no problem.

Database

Upvotes: 2

Views: 3222

Answers (2)

Frank van Puffelen
Frank van Puffelen

Reputation: 600120

It's hard to be certain from the code you shared, by I suspect you may be bitten by the fact that all data is loaded from Firebase asynchronously. Alternatively you may simply not have permission to read the data. I'll give an answer for both.

Data is loaded asynchronously

It's easiest to understand this behavior when you add a few log statements to a minimal snippet of your code:

System.out.println("Before attaching listener");
q.addChildEventListener(new ChildEventListener() {
    public void onChildAdded(DataSnapshot dataSnapshot, String s) {
        System.out.println("In onChildAdded");    
    }
    public void onChildChanged(DataSnapshot dataSnapshot, String s) { }
    public void onChildRemoved(DataSnapshot dataSnapshot) { }
    public void onChildMoved(DataSnapshot dataSnapshot, String s) { }
    public void onCancelled(DatabaseError databaseError) { }
});
System.out.println("After attaching listener");

The output of this snippet will be:

Before attaching listener

After attaching listener

In onChildAdded (likely multiple times)

This is probably not the order you expected the output in. This is because Firebase (like most cloud APIs) loads the data from the database asynchronously: instead of waiting for the data to return, it continues to run the code in the main thread and then calls back into your ChildEventListener.onChildAdded when the data is available.

There is no way to wait for the data on Android. If you'd do so, your users would get the daunted "Application Not Responding" dialog and your app would be killed.

So the only way to deal with the asynchronous nature of this API is to put the code that needs to have the new data into the onChildAdded() callback (and likely into the other callbacks too at some point):

q.addChildEventListener(new ChildEventListener() {
    public void onChildAdded(DataSnapshot dataSnapshot, String s) {
        allBrands.add((dataSnapshot.getValue(Brand.class)).getBrandName());  
        System.out.println(allBrands.length); 
    }

You need permission to read the data

You need permission to read the data from a location. If you don't have permission, Firebase will immediately cancel the listener. You need to handle this condition in your code, otherwise you'll never know.

public void onCancelled(DatabaseError databaseError) {
    throw databaseError.toException();
}

Upvotes: 3

sivi
sivi

Reputation: 11144

Try this (I'm writing this to future reference of myself .. too)

As you can see we implement a reresh before it's end. There is probably a nicer way to do it. However, it is not documented. Also all this event Listners should be add autmoatically and released automatically by firebase but they don't do it from some reason.

/**
   * @param uid  User's ID
   * @param Callable send as null just to implement a call to assure the callback is updapted before it's finished
   * @return returns ArrayList of all Games unique identification key enlisted in a User
   */
  private final ArrayList<String> mGamesPlaying = new ArrayList<>();
  public ArrayList<String> mGamesPlaying(final String uid, final Callable refresh) {


    final Firebase ref = FirebaseRef;
    Firebase usersRef = ref.child("users").child(uid).child("playing_games");

    usersRef.addValueEventListener(new ValueEventListener() {
      @Override
      public void onDataChange(DataSnapshot snapshot) {

        mGamesPlaying.clear();
        for (DataSnapshot child : snapshot.getChildren()) {
          Log.d(TAG, "Test Game" + child.getKey());
          mGamesPlaying.add(child.getKey());
        }

          try {

            refresh.call();
          } catch (Exception e) {
            e.printStackTrace();
          }

      }

      @Override
      public void onCancelled(FirebaseError firebaseError) {
      }
    });

    return mGamesPlaying;
  }

Upvotes: 0

Related Questions