Search code examples
javaandroidfirebasefirebase-realtime-databasefirebase-console

DataSnapshot.getValue() doesn't retrieve object properly


I'm new to firebase database and firebase in general and I encountered a little issue while trying to work with it.

I am using the following JSON code I imported from firebase console, originally I just created a database structure in firebase console then downloaded the JSON code, and it is as follows:

{
  "rides" : {
    "arrive" : "kech",
    "depart" : "agadir",
    "detail" : "0,1",
    "msg" : false,
    "phone" : true,
    "price" : 15,
    "profile_id" : 0,
    "spots" : 3,
    "timedate" : "11/12/2017 11:15",
    "vocal_id" : 0
  }
}

The object i want to fetch is named Ride.java:

public class Ride {
    private String arrive;
    private String depart;
    private String detail;
    private boolean msg;
    private boolean phone;
    private int price;
    private int profile_id;
    private int spots;
    private String timedate;
    private int vocal_id;

    public Ride() {}

    // public getters (I created them I just didn't post it all to make the post shorter)
}

To listen to my database reference I'm using the following code:

FirebaseDatabase database = FirebaseDatabase.getInstance();
        DatabaseReference ref = database.getReference("rides").child("0"); // 0 is the ride id

        ref.addListenerForSingleValueEvent(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                for(DataSnapshot singleSnapshot : dataSnapshot.getChildren()){
                    Ride ride = singleSnapshot.getValue(Ride.class);
                    rides.add(ride); // list of rides used later in a listadapter
                }
            }
            @Override
            public void onCancelled(DatabaseError databaseError) {
                Log.e(TAG, "onCancelled", databaseError.toException());
            }
        });

I'm trying to fetch a single ride from firebase db and show it in my app. In firebase console it has the url: https://myapp.firebaseio.com/rides/0 (the second ride has id 1 and so on). What is the best practice to grab all rides and from each ride access its attributes?

Edit: When I remove .child("0") I do not get any errors but nothing appears in my listView. Apparently rides.add(ride) doesn't take place for some reason.

I tested it by adding a simple instance of Ride outside the addListenerForSingleValueEvent() method, it works fine.

I also tried changing the listener to addValueEventListener(), same result..

Edit2: I get an error message when I try to retrieve Integers from my database.

I tried this in my database listener:

public void onDataChange(DataSnapshot dataSnapshot) {
                for (DataSnapshot singleSnapshot: dataSnapshot.getChildren()) {
                    int price = (int) singleSnapshot.child("price").getValue(); //  error takes place here
                    Log.d("FirebaseDb", String.valueOf(price));
                }
            }

The result is this error: (with all ints)

java.lang.ClassCastException: java.lang.Long cannot be cast to java.lang.Integer
    at com.ride.ive.anas.iveride.Fragments.RidesFragment$1.onDataChange(RidesFragment.java:96)

But when I retrieve a String or a Boolean it works fine, as in:

String arrive = (String) singleSnapshot.child("arrive").getValue();

Or Boolean usePhone = (Boolean) singleSnapshot.child("phone").getValue();

Edit3: Here is the RidesFragment.java file upon request by @Prodigy. In which I have a ListView where I try to show all Ride instances stored in my firebase database.

public class RidesFragment extends Fragment {

    ListView list;
    RidesAdapter2 ridesAdapter;
    List<Ride> rides;
    Ride ride;

    List<RidePojo> filteredRides;
    LinearLayout layoutFilter;
    boolean filtersVisible = false;
    boolean searchDone = false;

    String userNumber;

    public RidesFragment() {
        // Required empty public constructor
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_rides, container, false);
        getActivity().setTitle("Search for a ride:");

        // filter action button stuff
        setHasOptionsMenu(true);
        layoutFilter = rootView.findViewById(R.id.ll_filter_screen);

        /*FragmentManager fm = getActivity().getSupportFragmentManager();
        fm.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);*/

        ridesAdapter = new RidesAdapter2(getContext(), rides);


        Bundle arguments = getArguments();
        list = rootView.findViewById(R.id.rides_list);
        rides = new ArrayList<>();

        FirebaseDatabase database = FirebaseDatabase.getInstance();
        DatabaseReference ref = database.getReference("rides");

        ref.addListenerForSingleValueEvent(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                for (DataSnapshot singleSnapshot: dataSnapshot.getChildren()) {
                    Ride ride = singleSnapshot.getValue(Ride.class);
                    rides.add(ride);
                    ridesAdapter.notifyDataSetChanged();
                    Log.d("FbDatabase", singleSnapshot.toString());
                }

            }
            @Override
            public void onCancelled(DatabaseError databaseError) {
                Log.e(TAG, "onCancelled", databaseError.toException());
            }
        });

        rides.add(ride);

        // In case the rides ArrayList is not empty another fragment appears for no rides criteria
        if(rides.isEmpty()) {
            rootView = inflater.inflate(R.layout.fragment_empty, container, false);
            Button rideButton = rootView.findViewById(R.id.btn_post_ride);
            Button requestButton = rootView.findViewById(R.id.btn_post_request);

            rideButton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    launchFragment(new OfferRideFragment(), getActivity());
                }
            });

            requestButton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    launchFragment(new RequestFragment(), getActivity());
                }
            });

        }
        else {
            list.setAdapter(ridesAdapter);

        }
        return rootView;
    }

    protected void launchFragment(Fragment f, FragmentActivity a) {
        android.support.v4.app.FragmentTransaction fragmentTransaction =
                a.getSupportFragmentManager().beginTransaction();
        fragmentTransaction.replace(R.id.fragment_container, f);
        fragmentTransaction.commit();
    }
// ....
}

Solution

  • Firebase database naming convention is more like naming file path for example to access rides you use .child("rides") while for arrive it will be .child("rides").child("arrive") or (rides/arrives)

    From the firebase link you posted I can see you have a node rides with a child node 0 etc.... But from the json you posted cant see node "0"and this will surely throw an error.

    The correct Json should be 
    {
      "rides" : {
       "0": {
        "arrive" : "kech",
        "depart" : "agadir",
        "detail" : "0,1",
        "msg" : false,
        "phone" : true,
        "price" : 15,
        "profile_id" : 0,
        "spots" : 3,
        "timedate" : "11/12/2017 11:15",
        "vocal_id" : 0
    }
      }
    }
    

    This will pull out the data you need and don't forget to add notifydata change listener on tour list adapter when data is added.

    See edited code below

    public class RidesFragment extends Fragment {
    
        ListView list;
        RidesAdapter2 ridesAdapter;
        List<Ride> rides;
        Ride ride;
    
        List<RidePojo> filteredRides;
        LinearLayout layoutFilter;
        boolean filtersVisible = false;
        boolean searchDone = false;
    
        String userNumber;
    
        public RidesFragment() {
            // Required empty public constructor
        }
    
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                 Bundle savedInstanceState) {
            View rootView = inflater.inflate(R.layout.fragment_rides, container, false);
            getActivity().setTitle("Search for a ride:");
    
            // filter action button stuff
            setHasOptionsMenu(true);
            layoutFilter = rootView.findViewById(R.id.ll_filter_screen);
    
            /*FragmentManager fm = getActivity().getSupportFragmentManager();
            fm.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);*/
    
    
    
    
            Bundle arguments = getArguments();
            list = rootView.findViewById(R.id.rides_list);
            rides = new ArrayList<>();
            ridesAdapter = new RidesAdapter2(getContext(), rides);
            list.setAdapter(ridesAdapter);
    
            FirebaseDatabase database = FirebaseDatabase.getInstance();
            DatabaseReference ref = database.getReference("rides");
    
            ref.addListenerForSingleValueEvent(new ValueEventListener() {
                @Override
                public void onDataChange(DataSnapshot dataSnapshot) {
                    for (DataSnapshot singleSnapshot: dataSnapshot.getChildren()) {
                        Ride ride = singleSnapshot.getValue(Ride.class);
                        rides.add(ride);
                        ridesAdapter.notifyDataSetChanged();
                        Log.d("FbDatabase", singleSnapshot.toString());
                    }
    
                }
                @Override
                public void onCancelled(DatabaseError databaseError) {
                    Log.e(TAG, "onCancelled", databaseError.toException());
                }
            });
    
            //rides.add(ride);
    
            // In case the rides ArrayList is not empty another fragment appears for no rides criteria
            if(rides.isEmpty()) {
                rootView = inflater.inflate(R.layout.fragment_empty, container, false);
                Button rideButton = rootView.findViewById(R.id.btn_post_ride);
                Button requestButton = rootView.findViewById(R.id.btn_post_request);
    
                rideButton.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View view) {
                        launchFragment(new OfferRideFragment(), getActivity());
                    }
                });
    
                requestButton.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View view) {
                        launchFragment(new RequestFragment(), getActivity());
                    }
                });
    
            }
    
            //else {
                //list.setAdapter(ridesAdapter);
    
            //}
            return rootView;
        }
    
        protected void launchFragment(Fragment f, FragmentActivity a) {
            android.support.v4.app.FragmentTransaction fragmentTransaction =
                    a.getSupportFragmentManager().beginTransaction();
            fragmentTransaction.replace(R.id.fragment_container, f);
            fragmentTransaction.commit();
        }
    // ....
    }