I am implementing Firebase Recyclerview UI in my application. I have implemented a recyclerview adapter and it shows me following exception.
Following is my adapter code :
FirebaseRecyclerAdapter<DataSnapshot, MyHolder> recyclerAdapter = new FirebaseRecyclerAdapter<DataSnapshot, MyHolder>(
DataSnapshot.class,
R.layout.row_slots,
MyHolder.class,
databaseReference.child("slots").child(uid).child(dayOfWeek).child("time")
) {
#Override
protected void populateViewHolder(MyHolder viewHolder, DataSnapshot model, int position) {
System.out.println("Key : "+model.getKey());
}
};
It is showing following exception :
How can I get a snapshot value using FirebaseRecyclerAdapter?
The problem is DataSnapShot is missing a no Argument Constructor so you can not use it directly like this.
Use some other model class instead.
Like this :
Create your own model called FriendlyMessage :
public class FriendlyMessage {
private String text;
private String name;
// Created constructor with no Arguments
public FriendlyMessage() {
}
public FriendlyMessage(String text, String name) {
this.text = text;
this.name = name;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Most important thing we have created constructor with no Arguments
which was missing in DataSnapShot
NOTE : The above model class is just an example for you as you are new in firebase. You can have your own model class with your own type of parameters and all.
The use it like this for your Firebase Recyclerview :
FirebaseRecyclerAdapter mFirebaseAdapter = new FirebaseRecyclerAdapter<FriendlyMessage, MyHolder>(
FriendlyMessage.class,
R.layout.row_slots,
MyHolder.class,
databaseReference.child("slots").child(uid).child(dayOfWeek).child("time")) {
#Override
protected void populateViewHolder(MyHolder viewHolder, FriendlyMessage friendlyMessage, int position)
{
}
};
EDIT :
It also matter how you are pushing the data on Database. Do it
directly with your model class.
For example for above model FriendlyMessage push it like this :
FriendlyMessage friendlyMessage = new FriendlyMessage("message", "Username");
databaseReference.child(MESSAGES_CHILD).push().setValue(friendlyMessage);
Here your child() will be somewhat different from my implementation it is just an example.
For Listening to a particular DataSnapShot :
databaseReference.child("users").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot postSnapshot : dataSnapshot.getChildren()) {
//The PUSH ID OP WANTED
System.out.println("Push Id ---"+postSnapshot.getKey());
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
})
Explaining the above listener. It will give you the DataSnapShot
Object for your every user who is falling inside your child "users". You can access Push id by getKey() method.
firebaser (and author of the FirebaseUI adapters) here
Lots of good answers already. I'd indeed typically recommend creating a Java class that represents your data objects.
But if you're intent on using a DataSnapshot, you can override FirebaseRecyclerAdapter.parseSnapshot to handle the "conversion":
#Override
protected ChatMessage parseSnapshot(DataSnapshot snapshot) {
return snapshot;
}
When initializing your recyclerAdapter, your passing a DataSnapshot.class as your modelClass, wherein a DataSnapshot doesn't really have a Constructor with no parameters which I think is causing the error, also I think that makes a DataSnapshot an invalid modelClass.
As stated in the FirebaseRecyclerViewAdapter for the modelClass:
modelClass - Firebase will marshall the data at a location into an instance of a class that you provide
You should define your own modelClass by creating an object that suites your needs. There's a simple example on the FirebaseRecyclerViewAdapter link that you can check out. Cheers!
EDIT:
As per the comments, I suggested to have your own modelClass object have a DataSnapshot as a parameter. For a simple example of what I'm saying, refer below.
public class ModelClass {
String sampleString;
ModelClass() {
}
ModelClass(DataSnapshot dataSnapshot) {
// Do what you need from the dataSnapshot here
sampleString = dataSnapshot.child("").toString();
}
}
I think from the example above, you would get what I mean. BUT, I'm not sure if this is recommended nor if it's even acceptable. I haven't tried this out before so I'm also not sure if it'll work.
Since this concern is actually different from the one posted. I suggest posting a different one if ever it doesn't work. Happy Coding. Cheers!
Related
I need to return a data from Firebase to a Java Object, but even using an Interface, it always returns me null. Some help?
I created a "Coordinate" object inside the method and I'm trying to access the data like this: readData(value -> coordinates = value , offer);
public interface CoordinatesCallback {
void onCallBack(Coordinates value);
}
public void readData(CoordinatesCallback callback, Offer offer) {
String storeId = Integer.toString(offer.getStore().getId());
mDatabase.child("stores").child(storeId).child("coordinates").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
Coordinates coordinates = dataSnapshot.getValue(Coordinates.class);
callback.onCallBack(coordinates);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
That's not how getValue() works. You have to pass it the Class object of a Java POJO with getters and setters for each field of the database you want to read. From the linked Javadoc:
This method is used to marshall the data contained in this snapshot
into a class of your choosing. The class must fit 2 simple
constraints:
The class must have a default constructor that takes no arguments
The class must define public getters for the properties to be assigned. Properties without a public getter will be set to their
default value when an instance is deserialized
I'm trying to implement firebase database in my app, but I'm having problems in retrieving data from multiple nodes at same time and updating the ui.
I'm storing all the Firebase methods in a separate class.
here is an example of one of the methods to retrieve one datum from a node.
public static void getTrainerCity(final Trainer trainer) {
dbref.child(USER_CITY).child(trainer.getId()).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
trainer.setCity(dataSnapshot.getValue(String.class));
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
Then, to get for example one complete trainer, I do
public static void getTrainer(final boolean isAthlete, final String trainerId, Trainer trainer) {
trainer.setId(trainerId);
getTrainerCity(trainer);
getTrainerName(trainer);
getTrainerAge(trainer);
getTrainerNumber(trainer);
getTrainerEmail(trainer);
getTrainerGym(trainer);
getTrainerPhoto(trainer);
getTrainerStudyDegree(trainer);
getTrainerDescription(trainer);
if(!isAthlete) {
getTrainerAthletes(trainer);
}
}
when I call this method in UI, I'm not getting the entire Trainer object in one shot as those methods don't run on the main thread. This means that the trainer is constantly updated. However, I want to notify the UI only when all fields of the object related to the method have been set (I have to populate lists and initialize user). How would you do this?
I thought about attaching listeners, but it seems like I need a tremendous amount of them. Thank you guys
EDIT: This is how Db is structured
It's scenarios like this where RxJava shines.....what you could do is wrap firebase queries in Observable.create() and then use something like RxJava zip operator to combine all the queries. The alternative (to be avoided if possible) is to nest all the queries which leads to dreaded callback hell
For example you might have something like following for getTrainerCity
public static Observable<String> getTrainerCity(int trainerId) {
return Observable.create(subscriber -> {
dbref.child(USER_CITY).child(trainerId).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String city = dataSnapshot.getValue(String.class);
subscriber.onNext(city);
subscriber.onCompleted();
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
});
}
You should get trainer node first and then parse the fetched data.
I assume you have a Trainer node which contains all the info (city, name, age, ...)
public static void getTrainer(final Trainer trainer) {
dbref.child(trainer_node).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Trainer trainer = dataSnapshot.getValue(Trainer.class);
String name = trainer.getName();
String city = trainer.getCity();
int age = trainer.getAge();
.
.
.
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
EDIT:
Data structure maybe like this
-users_node
--user1
---id:userid
---name: username
---city:usercity
---email:useremail
---workouts
----workoutid1:true
----workoutid2:true
--user2
---id:..
-athletes_node
--user1:true
--user2:true
--userX:true
-workouts_node
--workoutid1
---workout details here
--workoutid2
---workout details here
I am trying to query items in my firebase database based on an attribute value.
Here is my structure:
Here is my POJO object:
public class LapTime {
private int vehicleType;
private String track;
private String user;
private int time;
}
And here is my query code:
mDatabase.child("lap-time").orderByChild("track").equalTo(mTrackUuid).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
time = dataSnapshot.getValue(LapTime.class).getTime() + "ms");
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
My problem is that the datasnapshot I get back has for root lap-time and not the UUID, thus the getValue(LapTime.class) give me an empty object...
Here is the datasnapshot I get:
DataSnapshot { key = lap-time, value = {-KXwSxD0GYVC6awV8abw={track=-KXwSxCeV2Ib4Gt4pvjy, time=105000, vehicle-type=0, user=ZQKwPfRF0mVL5jAO7FaSPcsF7t42}} }
And this is what I would like to get:
DataSnapshot { key = -KXwSxD0GYVC6awV8abw, value ={track=-KXwSxCeV2Ib4Gt4pvjy, time=105000, vehicle-type=0, user=ZQKwPfRF0mVL5jAO7FaSPcsF7t42}} }
How can I do that?
Since your query code can actually have more than one result you should be calling dataSnapshot.getChildren() and iterating through the children to get the result you're looking for. Particularly, mDatabase.child("lap-time").orderByChild("track").equalTo(mTrackUuid) is a query that could have multiple results since there could be a situation where multiple LapTime objects could have a track that points to the same mTrackUuid. Now, to get the result of the log you posted you need to construct a the query like so: mDatabase.child("lap-time").child("-KXwSxD0GYVC6awV8abw"). Notice how the orderByChild/equalTo queries are gone since those result in a conditional query that may return a list of data instead of a singular value.
I am trying to pull a list of friend IDs from my firebase database into my app, and then look up any additional data associated with the player (such as username, online status etc..) by looking up their entry under "users" using their unique ID
My schema looks as follows:
{friends
{
"Ko2D1of4KxXHzX0OqEZEAKDfw4r2" : {
"-KR0aTQGT6pfRfB5qIUz" : {
"friend_id" : "6vFVAAQfwiYERl03C3lzxdPjnEp2"
},
"-KR0aaMAOS3FWOAosBmo" : {
"friend_id" : "kxrQFVjGv0XUHyV5N764Nq50Q3J3"
}
}
}
}
The first unique ID is the ID of the player, which enables querying their friends list. The child objects of that represent the ID of the relationship, and the friend_id under that shows the other players (the friends) ID
The user schema looks as follows:
{
"6vFVAAQfwiYERl03C3lzxdPjnEp2" : {
"emailAddress" : "b#b.com",
"level" : 1,
"userName" : "steve"
},
"Ko2D1of4KxXHzX0OqEZEAKDfw4r2" : {
"emailAddress" : "a#a.com",
"level" : 1,
"userName" : "bob"
},
"kxrQFVjGv0XUHyV5N764Nq50Q3J3" : {
"emailAddress" : "bg#b.com",
"level" : 1,
"userName" : "tim"
},
"rNtYvwF8LBhTRM1Wk8ybBJyrFIg2" : {
"emailAddress" : "c#c.com",
"level" : 1,
"userName" : "test account"
}
}
Now, in my app, I can successfully pull all of the friend_id entries, but am not sure how to then turn around and pull additional information on the friend by using this ID. Ideally I would be able to query each friend one by one by their unique player ID, and populate the friends list fragment I have using a firebaseListAdapter.
This is how I am pulling the friend IDs and populating the list object.
ValueEventListener friendListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
friendIdList.clear();
for (DataSnapshot messageSnapshot: dataSnapshot.getChildren()) {
String friend_id = (String) messageSnapshot.child("friend_id").getValue();
friendIdList.add(friend_id);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {}
};
mDatabase.addValueEventListener(friendListener);
Could anybody help me figure out the best way to pull all of this additional information on the friends once the IDs are received from the initial query?
You can use ChildEventListener to get all friends then on your populateview or onBindViewHolder use ValueEventListener to get all data of a friend. also try to use FirebaseRecyclerView instead of FirebaseListView
Yes agreed even I have same confusion.
What are we struggling is with Contains[*, *, *] query.
Even I have a similar problem
/Users{
"uuid1":{
data1:"value1"
data2:"value2"
}
"uuid2":{
data1:"value1"
data2:"value2"
}
}
/Friends:{
uuid1:{
"uuid2":true
....
}
uuid2:{
"uuid1":true
...
}
}
My Query is how to Query list of only my friends and present their data("/User") with FirebaseRecyclerViewAdapter.
I'd suggest a different data structure. Here it is based on your data:
{
friends : {
"Ko2D1of4KxXHzX0OqEZEAKDfw4r2" : {
"6vFVAAQfwiYERl03C3lzxdPjnEp2" : true,
"kxrQFVjGv0XUHyV5N764Nq50Q3J3" : true
}
}
}
Then loop over each one of them and pull up their individual profiles.
So I'm just going to connect the answers given by Mathew Berg and pastillas. Based on your question and comments I think combined they provide the solution you are looking for.
Data Structure
The structure you are using for your users location looks good to me so I'd say you can leave that as is:
{
users: {
"6vFVAAQfwiYERl03C3lzxdPjnEp2" : {
"emailAddress" : "b#b.com",
"level" : 1,
"userName" : "steve"
},
"Ko2D1of4KxXHzX0OqEZEAKDfw4r2" : {
"emailAddress" : "a#a.com",
"level" : 1,
"userName" : "bob"
}
}
}
For you friends location I agree with the structure given by Mathew Berg:
{
friends : {
"Ko2D1of4KxXHzX0OqEZEAKDfw4r2" : {
"6vFVAAQfwiYERl03C3lzxdPjnEp2" : true,
"kxrQFVjGv0XUHyV5N764Nq50Q3J3" : true
}
}
}
Just a quick FYI, you don't need to use a Boolean value with this structure. You can use any allowed data type as the value for each key. In this instance the key is what's important, the value is just a place holder because Firebase doesn't allow keys with null values. That said if you find a reason to use a value that is more useful you could do that. In my opinion using a Boolean value makes the structure a little more readable by team members or someone who may follow behind you.
Retrieving Your Data
There are multiple ways you can query a friends list and get the data for each user. Since you referred to using a FirebaseListAdapter in your question I'll go with that. The same pattern can be used with a FirebaseRecyclerAdapter if you want to use choose to use a RecyclerView with FirebaseUI instead. The basic steps are:
Create a DatabaseReference that points to the location of the user
who's friends list you want.
Create a DatabaseReference that points to your users location
Create your FirebaseListAdapter.
When you override populateView get the key for each item
Use a addListenersingleValueEvent() to retrieve each friends user information and populate your listview row items with the data from
this query.
Within onDataChange of value event listener use the info for each user to populate views for each list item.
DatabaseReference mFriendsRef = FirebaseDatabase.getInstance().getReference().child("friends").child(someUserID;
DatabaseReference mUsersRef = FirebaseDatabase.getInstance().getReference().child("users");
...
mAdapter = new FirebaseListAdapter<YourModel>(this, YourModel.class, android.R.your_layout, mFriendsRef) {
#Override
protected void populateView(final View view, YourModel model, int position) {
String key = getRef(position).getKey();
mUsersRef.child(key).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
//get data from dataSnapshot and bind to views for each list item
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
};
messagesView.setAdapter(mAdapter);
Check out the comment made on this question for concerns about performance when using this approach: Android Firebase - Denormalized Queries using FirebaseUI
Sorry sir, I dont know if it will work on FirebaseUI but here is my code that I think has the same problem and also same solution, it will display the list of a user followers then if a follower clicked it will open and pass the id of item that is clicked to other activity. I did not use FirebaseUI.
public class FollowersAdapter extends RecyclerView.Adapter<FollowersAdapter.FollowersHolder>{
public ArrayList<Follower> followers;
public void addFollower(Follower follower) {
followers.add(follower);
notifyItemInserted(getItemCount());
}
public FollowersAdapter(Context context) {
this.followers = new ArrayList<>();
mDatabase = FirebaseDatabase.getInstance().getReference();
this.fUser = FirebaseAuth.getInstance().getCurrentUser();
mDatabase.child("follower").child(fUser.getUid()).addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
Follower follower = dataSnapshot.getValue(Follower.class);
addFollower(follower);
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
Follower follower = dataSnapshot.getValue(Follower.class);
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
#Override
public FollowersHolder onCreateViewHolder(ViewGroup parent, int position) {
View view = inflater.inflate(R.layout.followers_model, parent, false);
FollowersHolder holder = new FollowersHolder(view);
return holder;
}
#Override
public void onBindViewHolder(final FollowersHolder holder, final int position) {
Query userQuery = mDatabase.child("users").child(followers.get(position).getId());
userQuery.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
user = dataSnapshot.getValue(User.class);
holder.name.setText(user.getName());
final Bundle extras = new Bundle();
extras.putString(EXTRAS_POSTER_ID, user.getId());
holder.cont.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (!fUser.getUid().equals(user.getId())) {
Intent intent = new Intent(context, ViewUserProfile.class);
intent.putExtra(EXTRAS_BUNDLE, extras);
context.startActivity(intent);
}
}
});
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
#Override
public int getItemCount() {
return followers.size();
}
class FollowersHolder extends RecyclerView.ViewHolder {
View cont;
TextView name;
public FollowersHolder(View itemView) {
super(itemView);
cont = itemView.findViewById(R.id.cont_followers);
name = (TextView) itemView.findViewById(R.id.tv_followers_name);
}
}
}
you get the data using childEventListener then on the onBindViewHolder or populateView add onClickListener and pass the id when it clicked to other activity. sorry sir im a beginner lol :)
I know how to parse a simple DataSnapshot object to any Java class using public T getValue (Class<T> valueType). But after the Firebase 3.0 I am not able to parse the following data to my Java Class, as it contains a custom type instance for which I'm receiving NULL.
NOTE: The same logic was working fine before Firebase 3.0. I suppose its because now Firebase is using GSON instead of JACKSON. Please correct me if I'm wrong
DATA:
{
"address" : "DHA karachi",
"addresstitle" : "DHA karachi",
"logoimage" : {
"bucketname" : "test2pwow",
"id" : "zubairgroup",
"mediaType" : "image/png",
"source" : 1,
"url" : "https://pwowgroupimg.s3.amazonaws.com/zubairgroup1173.png?random=727"
},
"title" : "zubairghori"
}
Group.java
public class Group {
public String address;
public String addresstitle;
public LogoImage logoimage;
public Group(){}
}
LogoImage.java
public class LogoImage {
public String bucketname;
public String id;
public LogoImage(){}
}
Code that read:
Group group = datasnapshot.getValue(Group.class);
It doesn't cast LogoImage part of the database into the logoimage object. We always retrieve null in the logoimage object.
public T getValue(Class valueType)
1.The class must have a default constructor that takes no arguments
2.The class must define public getters for the properties to be assigned. Properties without a public getter will be set to their default value when an instance is deserialized
Check it from:
this source It'll help you
detail
I'm not sure why that is causing problem for you. This code works fine for me with the data you provided:
DatabaseReference ref = database.getReference("37830692");
ref.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Group group = dataSnapshot.getValue(Group.class);
System.out.println(group);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
I had the same problem. I solved it by providing not only getters for all the values, but setter too. Hope this helps.
I had the same problem and solved it by making sure that the arguments of the constructor are spelled the same than the elements saved at Firebase. My mistake was that I was setting the key of Firebase with Uppercase letters and object arguments with lowercase letters.