Firebase + Android - Notifications ChildAdded - How to get only New Childs? - android

I'm trying to setup an Android Service in my app that listens for new Childs in a Firebase Ref, and throws a Notification when that happens.
I'm having issues because addChildEventListener onChildAdded apparently is called one time for every existent record and only then actually listens for new childs..
In this answer #kato states that if addChildEventListener is called like ref.endAt().limit(1).addChildEventListener(...) it would get only the newly added records.
It actually only gets one record at a time (I suppose with limit(1)) but it still gets an existant record before listening for added records.
Here's some code:
Initializing the Listener in onCreate():
#Override
public void onCreate() {
super.onCreate();
this.handler = new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
AllowedGroup ag = dataSnapshot.getValue(AllowedGroup.class);
postNotif("Group Added!", ag.getName());
}
...rest of needed overrides, not used...
I'm using the AllowedGroup.class to store the records, and postNotif to build and post the notification. This part is working as intended.
Then, onStartCommand():
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
this.f = new Firebase(FIREBASE_URL).child("users").child(this.currentUserUid).child("allowedGroups");
f.endAt().limit(1).addChildEventListener(handler);
return START_STICKY;
}
It still returns one existant record before actually listening for newly added childs.
I've also tried querying by timestamp, like so:
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
this.f = new Firebase(FIREBASE_URL).child("users").child(this.currentUserUid).child("allowedGroups");
f.startAt(System.currentTimeMillis()).addChildEventListener(handler);
return START_STICKY;
}
Hoping that it would only get records set after the service was started. It doesn't get existant records, but doesn't even get newly added childs.
EDIT:
I've also thought of something like getting into memory first all of the existant records, and conditionally post a notification if the record brought by onChildAdded does not exist on the previously gathered list, but that seems a bit like overkill, and thought that could be an easier (more API-friendly) way of doing this, am I right ?
Can anyone provide me with some insight on this ? I can't really find anything on the official docs or in any StackOverflow question or tutorial.
Thanks.

If you have a field modifiedOn, you can index that and setup a query like:
Query byModified = firebaseEndpoint.orderByChild("modifiedOn").startAt(lastInAppTime);

For me the logic was is to have value -"status" for example- which needs to be validated before deciding whether it is really new or was an old record then I set the "status" to a different value so I don't get it next time:
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String previousChildKey) {
if(dataSnapshot.hasChildren()) {
String Id = (String) dataSnapshot.child("user_id").getValue();
String status = (String) dataSnapshot.child("status").getValue();
if (Id != null && Id.equals(storedId) && status != null && status.equals("created")) {
Log.d("INCOMING_REQUEST", "This is for you!");
sessionsRef.child(dataSnapshot.getKey()).child("status").setValue("received");
}
}
}

I overcome this problem by keeping items of firebase database reference node in a List; and whenever onChildAdded(...) method is triggered, check if the incoming datasnapshot is in your list or not; if not, then it's new data
In order to achieve this you must meet below conditions:
Have a unique value that must not repeated from item to item. (this can be easily achieved when you add items into Firebase using .push() method.
Override .equals() method of your model class in order to fulfill the comparison based on this unique value.
Here code snippets based on your inputs
Model class
public class AllowedGroup {
private String id;
public static List<AllowedGroup> sGroups;
// reset of fields ...
public AllowedGroup() {
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
#Override
public boolean equals(#Nullable Object o) {
// If the object is compared with itself then return true
if (o == this) {
return true;
}
// Check if o is an instance of AllowedGroup or not
if (!(o instanceof AllowedGroup)) {
return false;
}
// typecast o to AllowedGroup so that we can compare data members
AllowedGroup group = (AllowedGroup) o;
// Compare data based on the unique id
return (group.id).equals(id);
}
}
Listening to firebase added nodes
#Override
public void onCreate() {
super.onCreate();
this.handler = new ChildEventListener() {
#Override
public void onChildAdded(#NonNull DataSnapshot dataSnapshot, #Nullable String s) {
AllowedGroup group = dataSnapshot.getValue(AllowedGroup.class);
if (!AllowedGroup.sGroups.contains(group)) {
// Here you'll receive only the new added values
}
}
// ...rest of needed overrides, not used...
}
}

Related

Cannot read country code selected in a spinner Android

I am working on an app in which users have to select a country code, i was successful in creating a spinner for the said purpose as shown in this link:
Creating a spinner for choosing country code
But i am getting problem in reading the value selected in the spinner.
{
String abc = onCountryPickerClick();//abc is always null
}
public String onCountryPickerClick (){
ccp.setOnCountryChangeListener(new CountryCodePicker.OnCountryChangeListener() {
#Override
public void onCountrySelected() {
selected_country_code = ccp.getSelectedCountryCodeWithPlus();
}
});
return selected_country_code;
}
When String abc = onCountryPickerClick(); is being invoked, the selected_country_code value will be assigned to abc.
When your CountryCodePicker.OnCountryChangeListener's onCountrySelected() method is being invoked, the ccp.getSelectedCountryCodeWithPlus();'s value gets assigned to selected_country_code. Since String is immutable, changing selected_country_code's value won't change the value of abc, nor the return selected_country_code; will be invoked.
One of possible solutions would be to change your CountryCodePicker.OnCountryChangeListener anonymous implementation to assign the selected country value to abc e.g.
#Override
public void onCountrySelected() {
selected_country_code = ccp.getSelectedCountryCodeWithPlus();
abc = selected_country_code
}
Callbacks are not synchronous. Unfortunately, you cannot simply do String abc = onCountryPickerClick(); because what you are returning is something that is not yet set. Let's go through your code:
ccp.setOnCountryChangeListener(
new CountryCodePicker.OnCountryChangeListener() {
#Override
public void onCountrySelected() {
selected_country_code = ccp.getSelectedCountryCodeWithPlus();
}
});
The code seems to say that when the country is selected in the spinner, you assign the value of selected_country_code. Assuming this is an action triggered by the user, when you call String abc = onCountryPickerClick();, how can you be sure the user has selected anything? This is the issue. You cannot be sure that the user has already selected the option and returning the value is not enough.
You can solve this in many ways. You can for example keep propagating the callback:
public void onCountryPickerClick(OnCountryChangeListener listener){
ccp.setOnCountryChangeListener(listener);
}
// Anywhere you call this
onCountryPickerClick(new CountryCodePicker.OnCountryChangeListener() {
#Override
public void onCountrySelected() {
// Here do whatever you want with the selected country
}
});
The above approach is not very different than what you have now. There are other options. You could use java observables i.e.:
class CountryCodeObservable extends Observable {
private String value;
public CountryCodeObservable(String value) {
this.value = value;
}
public void setCountryCode(String countryCode) {
value = countryCode;
setChanged();
notifyObservers(value);
}
}
public CountryCodeObservable onCountryPickerClick(){
CountryCodeObservable retValue = new CountryCodeObservable("");
ccp.setOnCountryChangeListener(
new CountryCodePicker.OnCountryChangeListener() {
#Override
public void onCountrySelected() {
retValue.setCountryCode(ccp.getSelectedCountryCodeWithPlus());
}
});
return retValue;
}
// Then when calling this method you can do something like:
CountryCodeObservable observable = onCountryPickerClick();
observable.addObserver((obj, arg) -> {
// arg is the value that changed. You'll probably need to cast it to
// a string
});
The above example lets you add more than one observable. It might be too much for your use case, I just thought it illustrates another approach and also the asynchronicity of this situation.
Again, there are even more ways to solve this, the key is that you can't simply return a string and hope it changes when the user selects anything.

How can I increase the value of a Firestore subcollection-document-field?

So, I am working on an Android app and what I try to do is to increase the value of a field by 1, by pressing a button. Basically, I have a collection of users, each of them having a subcollection of posts, each post having a numeric field called 'applicants' which I use to count how many persons applied to a job.
The problem is I can't find a way to query the exact document field because I can't obtain the documents ID programmatically. I have been trying to identify the post by searching for its title but no luck. When I run the code it doesn't throw any error, the app doesn't crash, but still, nothing changes in my database.
This is my database structure:
public void executeTransaction(int position){
db.runTransaction(new Transaction.Function<Void>() {
#Nullable
#Override
public Void apply(#NonNull Transaction transaction) throws FirebaseFirestoreException {
db.collectionGroup("posts").whereEqualTo("title", list.get(position).getTheTitle()).get()
.addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
#Override
public void onSuccess(QuerySnapshot queryDocumentSnapshots) {
DocumentReference postRef = usersRef.document("posts");
DocumentSnapshot postSnapshot = null;
try {
postSnapshot = transaction.get(postRef);
} catch (FirebaseFirestoreException e) {
e.printStackTrace();
}
long newApplicants = postSnapshot.getLong("applicants") + 1;
transaction.update(postRef, "applicants", newApplicants);
}
});
return null;
}
}).addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Toast.makeText(context, "You have applied to this job!", Toast.LENGTH_SHORT).show();
}
});
This is the method that runs when you tap the "Apply" button. The argument 'position' is just the position the post is stored in a local ArrayList<Post> list.
How can I simply increase the applicants value of a post?
The correct way to do this is to use a Transaction (click this to go to the documentation). Basically, how they work is detailed in the documentation:
Using the Cloud Firestore client libraries, you can group multiple operations into a single transaction. Transactions are useful when you want to update a field's value based on its current value, or the value of some other field.
And you are using a transaction the correct way, but not in the best case. As you want to update only one value transactions could be an overload of work for your database. A simple increment method will do the work using FieldValue.increment():
DocumentReference postRef = db.collection("users").document(userId).collection("posts").document(postId);
// Atomically increment the population of the city by 50.
washingtonRef.update("applicants", FieldValue.increment(50));
You need to have the id of the post you want to update.
To solve the problem you are having just edit your Post class and add this:
string id;
public void setId(string id) {
this.id = id;
}
public string getId() {
return id;
}
And when you get the posts just do this (aka. how to get the post id):
for (DocumentSnapshot documentSnapshot : documentSnapshots.getDocuments()) {
Post post = new Post();
post.setId(documentSnapshot.getId());
post.setTitle(documentSnapshot.getString("title"));
post.setApplicants(documentSnapshot.getLong("applicants")); ... list.add(post);
}
notifyDataSetChanged();
And when updating the applicants field, to obtain the documents ID just do:
string postId = post.getId();
then continue with your normal code.

MediatorLiveData onChanged not getting called

In my app I am trying to use MediatorLiveData to listen to the changes to a livedata. Since DB operations are involved I use an executor service like this.
MediatorLiveData<Content> mediatorLiveData = new MediatorLiveData<>();
appExecutors.diskIO().execute(() -> {
long id = contentDao.insert(content);
Log.i("LIVE", id + "");
LiveData<Content> content = contentDao.getContentById(id);
mediatorLiveData.addSource(content, new Observer<Content>() {
#Override
public void onChanged(#Nullable Content content) {
Log.i("LIVE", "FIRED");
}
});
});
First I try to insert a new content object into the db. I get the id of the inserted object which I log in the next line. I get some id which is good. After this, I use the id to query for the same object. The query returns a LiveData. (If I use content.getValue() at this time, I get null.)
Then I listen to changes in this liveData using a MediatorLiveData. Unfortunately, the onChange method of the mediatorLiveData is never fired. Thus the Log is not printed too.
This is my content dao class
#Dao
public interface ContentDao {
#Insert
long insert(Content content);
#Query("SELECT * FROM my_table WHERE id = :id")
LiveData<Content> getContentById(long id);
}
I can't understand what I am doing wrong. Can someone please help. Thanks!!
Edit: To clarify, this is how the code looks.
return new NetworkBoundResource<Content, CreateContent>(appExecutors) {
#Override
protected void saveCallResult(#NonNull CreateContent item) {
//Something
}
#Override
protected boolean shouldCall(#Nullable Content data) {
//Something;
}
#Override
protected LiveData<Content> createDbCall() {
MediatorLiveData<Content> mediatorLiveData = new MediatorLiveData<>();
appExecutors.diskIO().execute(() -> {
long id = contentDao.insert(content);
Log.i("LIVE", id + "");
LiveData<Content> content = contentDao.getContentById(id);
mediatorLiveData.addSource(content, new Observer<Content>() {
#Override
public void onChanged(#Nullable Content c) {
Log.i("LIVE", "FIRED");
mediatorLiveData.removeSource(content);
mediatorLiveData.postValue(c);
}
});
});
return mediatorLiveData;
}
#NonNull
#Override
protected LiveData<ApiResponse<CreateContent>> createCall() {
//Something
}
}.asLiveData();
The value is returned to the constructor.
#MainThread
public NetworkBoundResource(AppExecutors appExecutors) {
this.appExecutors = appExecutors;
result.setValue(Resource.loading(null));
//TODO:: Add method to check if data should be saved. This should apply for search data.
LiveData<ResultType> dbSource = createDbCall();
result.addSource(dbSource, data -> {
result.removeSource(dbSource);
if (shouldCall(data)) {
fetchFromNetwork(dbSource);
} else {
result.addSource(dbSource, newData -> setValue(Resource.success(newData)));
}
});
}
As discussed you need to make sure the mediatorLiveData has an active observer attached.
If you take a look at the addSource method it checks whether any active observers are attached before subscribing to the source.
https://github.com/aosp-mirror/platform_frameworks_support/blob/d79202da157cdd94c2d0c0b6ee57170a97d12c93/lifecycle/livedata/src/main/java/androidx/lifecycle/MediatorLiveData.java#L95
In case anyone is re initializing a mediator live data, the old object only will be observed, new object will not be observed.
That is , dont do this:
Observe
myViewModel.observe(....)
Trying to allocate new memory to mediator
myMediatorObj = new MediatorLiveData<>(); //this can be the issue. Try removing if you have any lines like this.
//after this point,anything set to the object myMediatorObj will not be observed
In case you are trying to reset the data, pass in some data that signals null/empty/rest.

LiveData List doesn't update when updating database

I'm currently refactoring legacy code to use Android Architecture Components and set up a room db and volley requests within a kind of repository pattern.
So the presentation/domain layer asks the repository to get LiveData-Objects to observe or tell him to synchronize with the server, after which old db entries are deleted and all current ones refetched from the server.
I've written tests for the synchronization part, so I'm sure, that the objects get fetched and inserted to the database correctly. But when writing a test to observe the entries of that db table (and test if the objects were saved correctly with everything there needs to be done before putting them into db) the LiveData> I'm observing, doesn't get triggered.
In the following snippet you can assume, that the synchronizeFormsWithServer(...) method does work correctly and is performing database operations asynchronously. It contains operations which deletes all Form-Objects from the db which are not present in the list of Forms fetched from the server and inserts all new ones. Since at the start of the test the database is empty this shouldn't matter that much
The test in which the observer doesn't get triggered:
#Test
public void shouldSaveFormsFromServerIntoDb() throws Exception
{
Lifecycle lifecycle = Mockito.mock(Lifecycle.class);
when(lifecycle.getCurrentState()).thenReturn(Lifecycle.State.RESUMED);
LifecycleOwner owner = Mockito.mock(LifecycleOwner.class);
when(owner.getLifecycle()).thenReturn(lifecycle);
final CountDownLatch l = new CountDownLatch(19);
formRepository.allForms().observe(owner, formList ->
{
if (formList != null && formList.isEmpty())
{
for (Form form : formList)
{
testForm(form);
l.countDown();
}
}
});
formRepository.synchronizeFormsWithServer(owner);
l.await(2, TimeUnit.MINUTES);
assertEquals(0, l.getCount());
}
The FormRepository code:
#Override
public LiveData<List<Form>> allForms()
{
return formDatastore.getAllForms();
}
The datastore:
#Override
public LiveData<List<Form>> getAllForms()
{
return database.formDao().getAllForms();
}
The formDao code (database is implemented how you'd expect it from room):
#Query("SELECT * FROM form")
LiveData<List<Form>> getAllForms();
It may very well be, that I didn't understand something about the LiveData-Components, because this is my first time using them, so maybe I got something fundamentally wrong.
Every bit of help is very much appreciated :)
PS: I stumbled across THIS post, which discusses a similar issue, but since I'm currently not using DI at all and just use a single instance of the formrepository (which has only one instance of formDao associated) I don't think it's the same problem.
Ok, so I found the solution, although I don't know, why it behaves that way.
Remember when I said "don't worry about the synchronize method"? Well... turns out there were a couple of things wrong with it, which delayed the solution further.
I think the most important error there was the method to update the objects in the database when the network response came in.
I used to call
#Update
void update(Form form)
in the dao, which for unknown reasons doesn't trigger the LiveData-Observer. So I changed it to
#Insert(onConflict = OnConflictStrategy.REPLACE)
void insert(Form form);
After doing this I could get the Form-LiveData from my repository as easy as
LiveData<List<Form>> liveData = formRepository.allForms();
Then subscribe to it as usual.
The previously failed test looks like this now:
#Test
public void shouldSaveFormsFromServerIntoDb() throws Exception
{
Lifecycle lifecycle = Mockito.mock(Lifecycle.class);
when(lifecycle.getCurrentState()).thenReturn(Lifecycle.State.RESUMED);
LifecycleOwner owner = Mockito.mock(LifecycleOwner.class);
when(owner.getLifecycle()).thenReturn(lifecycle);
final CountDownLatch l = new CountDownLatch(19);
final SortedList<Form> sortedForms = new SortedList<Form>(Form.class, new SortedList.Callback<Form>()
{
#Override
public int compare(Form o1, Form o2)
{
return o1.getUniqueId().compareTo(o2.getUniqueId());
}
#Override
public void onChanged(int position, int count)
{
Log.d(LOG_TAG, "onChanged: Form at position " + position + " has changed. Count is " + count);
for (int i = 0; i < count; i++)
{
l.countDown();
}
}
#Override
public boolean areContentsTheSame(Form oldItem, Form newItem)
{
return (oldItem.getContent() != null && newItem.getContent() != null && oldItem.getContent().equals(newItem.getContent())) || oldItem.getContent() == null && newItem.getContent() == null;
}
#Override
public boolean areItemsTheSame(Form item1, Form item2)
{
return item1.getUniqueId().equals(item2.getUniqueId());
}
#Override
public void onInserted(int position, int count)
{
}
#Override
public void onRemoved(int position, int count)
{
}
#Override
public void onMoved(int fromPosition, int toPosition)
{
}
});
LiveData<List<Form>> ld = formRepository.allForms();
ld.observe(owner, formList ->
{
if (formList != null && !formList.isEmpty())
{
Log.d(LOG_TAG, "shouldSaveFormsFromServerIntoDb: List contains " + sortedForms.size() + " Forms");
sortedForms.addAll(formList);
}
});
formRepository.synchronizeFormsWithServer(owner);
l.await(2, TimeUnit.MINUTES);
assertEquals(0, l.getCount());
}
I know that exactly 19 Forms will get fetched from the server and then every Form will get changed once (first time I load a list containing all Forms with reduced data, and the second time I load every item from the server again replacing the old value in the db with the new value with more data).
I don't know if this will help you #joao86 but maybe you have a similar issue. If so, please make sure to comment here :)
You have to use the same database instance at all places.
=> Use a singleton for that
I had a similar issue with yours --> LiveData is not updating its value after first call
Instead of using LiveData use MutableLiveData and pass the MutableLiveData<List<Form>> object to the Repository and do setValue or postValue of the new content of the list.
From my experience with this, which is not much, apparently the observer is connected to object you first assign it too, and every change must be done to that object.

return an object Android

I want to return an object with some things in them.
Here is the declaration;
Object user_det = get_user_det();
Here is the function code:
private Object get_user_det() {
Firebase f_user = new Firebase("https://myapp.firebaseio.com/User/");
f_user.addListenerForSingleValueEvent(new ValueEventListener(){
#Override
public void onDataChange(DataSnapshot snap_user) {
// TODO Auto-generated method stub
Iterable<DataSnapshot> rs = snap_user.getChildren();
Iterator<DataSnapshot> irs = rs.iterator();
long allNum2 = snap_user.getChildrenCount();
int maxNum2 = (int)allNum2;
int count_user = 1;
while(irs.hasNext())
{
if(count_user <= maxNum2)
{
Firebase user_data = new Firebase("https://myapp.firebaseio.com/");
AuthData authData = user_data.getAuth();
Map<String, Object> nPost = (Map<String, Object>) irs.next().getValue();
String db_email = nPost.get("email_addr").toString();
if (authData != null) {
String usr_email = authData.getProviderData().get("email").toString();
if(usr_email.equals(db_email))
{
//NB: I WANT TO ADD THE FOLLOWING INTO THE OBJECT
String disp_name = nPost.get("disp_name").toString();
String real_name = nPost.get("real_name").toString();
}
} else {
System.out.println("Failed");
}
}
count_user++;
}
}
#Override
public void onCancelled(FirebaseError arg0) {
// TODO Auto-generated method stub
}
});
return null; //NB: I NEED TO RETURN THE OBJECT HERE.
}
I want to return the string disp_name and real_name but they are inside the addListenerForSingleValueEvent, so how do I get them out and return it to the function.
I have wrote "NB" in the code where I need help with.
Thanks for your time.
If you want to return an object from your method in java, do it like this:
The Object class:
This contains the structure of your Object, and defines what data will be in it. Also includes methods to easily get the data.
private class myObject {
private String name;
private String realName;
//The constructor, so you can set the data when creating the Object.
public myObject (String disp_name, String real_name) {
name = disp_name;
realName = real_name;
}
//Getter methods, to get the data.
public String getRealName() {return realName;}
public String getDisplayName() {return name;}
}
Your code:
private Object get_user_det() {
myObject o; //Declare it, so it can be returned.
...
String disp_name = nPost.get("disp_name").toString();
String real_name = nPost.get("real_name").toString();
o = new myObject(disp_name, real_name); //create it and set the data.
...
return myobject; //return the new Object with the data.
}
To get the data from the Object:
myObject o = get_user_det(); //Call the metod which return our Object.
String realName = o.getRealName(); //Get the data from the Object.
String displayName = o.getDisplayName;
In your case, it would be much easier to use a String array.
Hope this helps.
It's probably easiest to see what's going on, if you add some printlns to your code:
private Object get_user_det() {
Firebase f_user = new Firebase("https://myapp.firebaseio.com/User/");
System.out.println("Adding listener");
f_user.addListenerForSingleValueEvent(new ValueEventListener(){
#Override
public void onDataChange(DataSnapshot snap_user) {
System.out.println("Data received");
}
#Override
public void onCancelled(FirebaseError arg0) {
// TODO Auto-generated method stub
}
});
System.out.println("Returning");
return null; //NB: I NEED TO RETURN THE OBJECT HERE.
}
If you execute this code, you will see that it logs:
Adding listener
Returning
Data received
Most likely, this is not what you expected. But hopefully, it makes sense if you read my explanation below.
Asynchronous loading
When you register your listener:
f_user.addListenerForSingleValueEvent(new ValueEventListener(){
You tell Firebase to start listening for events. It goes off and starts retrieving the data from the server.
Since retrieving the data may take some time, it does this retrieval asynchronously so that your thread isn't blocked. Once the data is completely retrieved, Firebase calls the onDataChange method in your listener.
Between the time you start listening and the time onDataChange is called, your code continues executing. So there is no way to return data that is loaded asynchronously, because by the time your function returns, the data isn't loaded yet.
Solutions
Disclaimer: I am not an expert at solving this problem in Java, so there may be problems with my solutions. If^H^HWhen you find any, please report them in the comments.
I know of three possible solutions to the problem:
force the code to wait for the data to be returned
return a Future that at some point will contain the data
pass a callback into get_user_det and call that function once the data is available
You will probably be tempted to selected option 1, since it matches most closely with your mental modal of loading data. While this is not necessarily wrong, keep in mind that there is a good reason that the loading is done asynchronously. It might be worth taking the "learning how to deal with asynchronicity" penalty now.
Instead of writing up examples for all solutions, I'll instead refer to some relevant questions:
Retrieving data from firebase returning NULL (an answer that uses approach 3)
Is waiting for return, ok?
Java wait() & notify() vs Android wait() & notify() (a question from a user taking approach 1)
How it works:
Firebase uses reflection to build a JSON tree object to save to the database. When you retrieve this JSON tree, you can cast it back to your original object. Just like serializing and deserializing. This means you do not need to handle the keys and values when trying to "rebuild" your object like you are. It can all be done like so:
YourObject object = (YourObject) dataSnapshot.getValue(YourObject.class);
Notice the YourObject.class in the getValue(). This tells firebase to reflect through this class and find the appropriate accessors with the dataSnapshot.
How to do it
Be sure that your object has:
Accessors Appropriate getters and setters for ALL fields - (or annotated with #JsonIgnore if you wish to not save a particular field)
Empty constructor. Your object must provide a constructor that does not modify itself at all.
What your object should look like:
public class YourObject {
private String displayName;
private String realName;
public YourObject() { /*Empty constructor needed for Firebase */ }
// Accessors
public void setRealName(String realName){
this.realName = realName;
}
public String getRealName(){
return this.realName;
}
public String getDisplayName(){
return this.displayName;
}
public void setDisplayName(String displayName){
this.displayName = displayName;
}
}
Then, in any of the firebase callbacks, you can just cast your DataSnapshot in to your object:
public void onDataChange(DataSnapshot snap_user) {
YourObject object = new Object;
if(snap_user.getValue() != null) {
try {
object = (YourObject) snap_user.getValue(YourObject.class); <-- Improtant!!!
} catch(ClassCastException ex) {
ex.printStackTrace();
}
}
return object;
}
Also
It seems you are retrieving many objects. When doing this, I find it best to use the onChildEventListener then for each of the YourObjects in that node, onChildAdded(DataSnapshot ds, String previousChild); will be called.

Categories

Resources