I'm using firebase to create To-Do android application. I'm use recyclerview and cardview to populate data fro firebase. But some weird things happen when i watch on Android Monitor and see my debug log fires twice onChildChanged:
D/DiaryAdapter: onChildChanged:-KIafq035E-pc6uT3Ej7
D/DiaryAdapter: onChildChanged:-KIafq035E-pc6uT3Ej7
and caused item on recyclerview blink twice (blink efect on item when it's updated)
i put this on MainFragment.java onStart()
// Setup adapter
diaryAdapter = new DiaryAdapter(getContext(),mDiaryRef);
recyclerDiary.setAdapter(diaryAdapter);
On my adapter constructor
public DiaryAdapter(final Context context, DatabaseReference ref) {
mContex = context;
mDatabaseRefrence = ref;
mChildEventListener = new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
//Log.d(TAG, "onChildAdded:" + dataSnapshot.getKey());
Diary diary = dataSnapshot.getValue(Diary.class);
mDiary.add(diary);
mDiaryID.add(dataSnapshot.getKey());
notifyItemInserted(mDiary.size() - 1);
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
Log.d(TAG, "onChildChanged:" + dataSnapshot.getKey());
Diary newDiary = dataSnapshot.getValue(Diary.class);
int diaryIndex = mDiaryID.indexOf(dataSnapshot.getKey());
if (diaryIndex > -1) {
mDiary.set(diaryIndex, newDiary);
notifyItemChanged(diaryIndex);
} else {
Log.w(TAG, "onChildChanged:unknown_child:" + dataSnapshot.getKey());
}
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
Log.d(TAG, "onChildRemoved:" + dataSnapshot.getKey());
int diaryIndex = mDiaryID.indexOf(dataSnapshot.getKey());
if (diaryIndex > -1) {
mDiary.remove(diaryIndex);
mDiaryID.remove(diaryIndex);
notifyItemRemoved(diaryIndex);
} else {
Log.w(TAG, "onChildRemoved:unknown_child:" + dataSnapshot.getKey());
}
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
Log.d(TAG, "onChildMoved:" + dataSnapshot.getKey());
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.w(TAG, "postComments:onCancelled", databaseError.toException());
Toast.makeText(mContex, "Failed to load diary.",
Toast.LENGTH_SHORT).show();
}
};
ref.addChildEventListener(mChildEventListener);
}
I'm following from firebase android sample here
Edit: Database structure
{
"diary" : {
"2Y4XwraNoRdIFPBJY0XxwdPte0H2" : {
"1437" : {
"-KIahAPVU_WrmP5rDcAa" : {
"created" : 1464159484316,
"infaq" : {
"selesai" : false
},
"ngaji" : {
"ayatAkhir" : 0,
"ayatMulai" : 0,
"selesai" : true,
"surahAkhir" : 0,
"surahMulai" : 0
},
"puasa" : {
"sahur" : false,
"selesai" : true,
"summary" : ""
},
"sholat" : {
"ashar" : true,
"dhuhur" : true,
"done" : true,
"mIsya" : true,
"maghrib" : true,
"subuh" : true
},
"summary" : "Puasa kedua Tos",
"tanggal" : 2,
"tarawih" : {
"ringkasan" : "",
"selesai" : true
},
"updated" : 1464186194796
},
}
}
}
Related
i want to count the value number of this child date_time_doctor_active and if the count is more than 0 then toast can't insert and if the count is 0 then execute the method insert the data or toast a message
i have tried many solutions but i can't get one right
this is my json code
"appointment" : {
"-MHp2gC9Wwk_NlbpdJRY" : {
"active_appointment" : "true",
"active_appointment_patient_email" : "true_hanamoha075#gmail.com",
"date_appointment" : "23/Aug/2020",
"date_time_doctor_active" : "23/Aug/2020_10:15 AM_باسل الخالدي_true",
"doctor_number" : "باسل الخالدي",
"id_appointment" : "-MHp2gC9Wwk_NlbpdJRY",
"patient_email" : "hanamoha075#gmail.com",
"time_appointment" : "10:15 AM"
},
"-MHp2nWxB4jwx9K5NQGj" : {
"active_appointment" : "true",
"active_appointment_patient_email" : "true_hanamoha075#gmail.com",
"date_appointment" : "23/Aug/2020",
"date_time_doctor_active" : "23/Aug/2020_10:15 AM_باسل الخالدي_true",
"doctor_number" : "باسل الخالدي",
"id_appointment" : "-MHp2nWxB4jwx9K5NQGj",
"patient_email" : "hanamoha075#gmail.com",
"time_appointment" : "10:15 AM"
},
"-MHp2rOv3xiY_ylkIdyf" : {
"active_appointment" : "true",
"active_appointment_patient_email" : "true_hanamoha075#gmail.com",
"date_appointment" : "23/Aug/2020",
"date_time_doctor_active" : "23/Aug/2020_10:15 AM_باسل الخالدي_true",
"doctor_number" : "باسل الخالدي",
"id_appointment" : "-MHp2rOv3xiY_ylkIdyf",
"patient_email" : "hanamoha075#gmail.com",
"time_appointment" : "10:15 AM"
}
and this is my java code in my button click listener
submitButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
final String patienttDate = txt_date.getText().toString().trim();
final String patienttTime = txt_time.getText().toString().trim();
final String patienttDoctor = choose_doctor.getSelectedItem().toString().trim();
final String patienttActive = "true";
appointment_DataBase.child("appointment")
.orderByChild("date_time_doctor_active")
.equalTo(patienttDate + "_" + patienttTime + "_" + patienttDoctor + "_" + patienttActive)
.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
if (snapshot.exists()) {
for (DataSnapshot dataSnapshot : snapshot.getChildren()) {
Appointment appointment = dataSnapshot.getValue(Appointment.class);
appointment.getDate_time_doctor_active();
size = size + 1;
}
} else {
if (size > 0) {
Toast.makeText(booking.this, "cant inserrrrt", Toast.LENGTH_SHORT).show();
Toast.makeText(booking.this, "size is " + size, Toast.LENGTH_SHORT).show();
} else {
addAppointment();
}
}
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
}
});
}
});
i have figure it out .. i did some changes on the code and this is the result
Query query = FirebaseDatabase.getInstance().getReference("appointment")
.orderByChild("date_time_doctor_active")
.equalTo(patienttDate+"_"+patienttTime+"_"+patienttDoctor+"_"+patienttActive);
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
size = 0;
for (DataSnapshot dataSnapshot : snapshot.getChildren()){
Appointment appointment = dataSnapshot.getValue(Appointment.class);
appointment.getDate_time_doctor_active();
if (dataSnapshot.exists()){
size = size +1;
}
}
if (size > 0){
Toast.makeText(booking.this, "this appointment is not available" , Toast.LENGTH_LONG).show();
}else {
addAppointment();
}
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
}
});
Here is my db format:
{
"classes": {
"class1": {
"name":"C1"
},
"class2": {
"name":"C2"
}
},
"students": {
"student1": {
"name":"S1"
},
"student2": {
"name":"S2"
}
},
"classes_enrollments": {
"class1": {
"student1": true,
"student2": true
},
"class2": {
"student1": true
}
},
"students_enrollments": {
"student1": {
"class1": true,
"class2": true
},
"student2": {
"class1": true
}
}
}
I want to get data like : class1 named C1, has 2 students S1 and S2
I tried this way
database.child("classes_enrolments").addValueEventListener(eventListener);
but the output is just the data of classes_enrolments node: {class2={student1=true}, class1={student2=true, student1=true}}
How to achieve it, please?
Thank you
To solve this, please use the cpde below:
String class1 = "class1";
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
DatabaseReference nameRef = rootRef.child("classes").child(class1).child("name");
ValueEventListener nameValueEventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot nameDataSnapshot) {
String className = nameDataSnapshot.getValue(String.class);
Log.d(TAG, class1 + " named " + className);
DatabaseReference class1Ref = rootRef.child("classes_enrollments").child(class1);
ValueEventListener class1EventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
long numberOfStudent = dataSnapshot.getChildrenCount();
Log.d(TAG, "has " + numberOfStudent + " students");
for(DataSnapshot studentDataSnapshot : dataSnapshot.getChildren()) {
String student = studentDataSnapshot.getKey();
DatabaseReference studentsRef = rootRef.child("students");
DatabaseReference studentRef = studentsRef.child(student);
ValueEventListener studentsValueEventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot ds : dataSnapshot.getChildren()) {
String name = ds.child("name").getValue(String.class);
Log.d(TAG, name);
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
Log.d(TAG, databaseError.getMessage()); //Don't ignore errors!
}
};
studentRef.addListenerForSingleValueEvent(studentsValueEventListener);
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
Log.d(TAG, databaseError.getMessage()); //Don't ignore errors!
}
};
class1Ref.addListenerForSingleValueEvent(class1EventListener);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
Log.d(TAG, databaseError.getMessage()); //Don't ignore errors!
}
};
nameRef.addListenerForSingleValueEvent(nameValueEventListener);
In order to solve this, you should first get the name of the class, the query to get the number of student and in the end to get the name of the students. The result in the logcat will be:
class1 named C1
has 2 students
S1
S2
I'm trying to save GeoLocations with GeoFire in Firebase, the key of the GeoLocation must be the server timestamp. So the data model is as follows:
{
"geofire" : {
"1515755844766" : {
".priority" : "ez53wur36x",
"g" : "ez53wur36x",
"l" : [ 39.66227391798104, -6.372992321848869 ]
},
"1515755844962" : {
".priority" : "ez53wur36x",
"g" : "ez53wur36x",
"l" : [ 39.66227391798104, -6.372992321848869 ]
},
"1515755851938" : {
".priority" : "s6xhjeruen",
"g" : "s6xhjeruen",
"l" : [ 14.78428771229891, 21.346221640706066 ]
},
"1515755852148" : {
".priority" : "s6xhjeruen",
"g" : "s6xhjeruen",
"l" : [ 14.78428771229891, 21.346221640706066 ]
}
},
"serverTime" : 1515755852148
}
I just executed the following code twice to get those values (four values), so it saves key (timestamp) twice for each .setLocation(), with a small diference in miliseconds and seconds.
Why could this be happening?
//Get reference to Firebase DB
dbRef = FirebaseDatabase.getInstance().getReference();
// Get reference to Geofire inside FIrebaseDB
dbRefGeofire = FirebaseDatabase.getInstance().getReference("geofire");
geoFire = new GeoFire(dbRefGeofire);
dbRef.child("serverTime").setValue(ServerValue.TIMESTAMP); //Executes TIMESTAMP function in firebase server and stores that value
dbRef.child("serverTime").addValueEventListener(new ValueEventListener() { //Gets TIMESTAMP value from the server
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
serverTime = String.valueOf(dataSnapshot.getValue());
geoFire.setLocation(serverTime, new GeoLocation(latLng.latitude,latLng.longitude), new GeoFire.CompletionListener() {
#Override //Save geolocation with timestamp in seconds
public void onComplete(String key, DatabaseError error) {
if (error != null) {
Log.v("Informe","There was an error saving the location to GeoFire: " + error);
} else {
Log.v("Informe","Location saved on server successfully!");
}
}
});
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
To solve this, please use the following code:
ValueEventListener eventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String serverTime = String.valueOf(dataSnapshot.getValue(Long.class));
geoFire.setLocation(serverTime, new GeoLocation(latLng.latitude,latLng.longitude), new GeoFire.CompletionListener() {
#Override
public void onComplete(String key, DatabaseError error) {
if (error != null) {
Log.v("Informe","There was an error saving the location to GeoFire: " + error);
} else {
Log.v("Informe","Location saved on server successfully!");
}
}
});
}
#Override
public void onCancelled(DatabaseError databaseError) {}
};
dbRef.child("serverTime").addListenerForSingleValueEvent(eventListener);
addListenerForSingleValueEvent solves your problem.
The ValueEventListener excuted twice, if you will set breakpoint at serverTime = String.valueOf(dataSnapshot.getValue());
you will see it clearly,
Why that's happen?
because your dbRef.child("serverTime").setValue(ServerValue.TIMESTAMP); is async,
The application continue to run and set a new valueEventListener,
which will call immediately without any data in dataSnapshot
and then, when you async 'setValue' function will finish and 'onDataChange' will call again (because that the value has been changed)
You can just add 'datasnapshout.isExist()' to solve it,
or add 'onCompleteListener' to your setValue line like this:
dbRef.setValue(ServerValue.TIMESTAMP, new DatabaseReference.CompletionListener() {
#Override
public void onComplete(DatabaseError databaseError, DatabaseReference dataRef) {
dbRef.child("serverTime").addValueEventListener(new ValueEventListener() { //Gets TIMESTAMP value from the server
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
serverTime = String.valueOf(dataSnapshot.getValue());
geoFire.setLocation(serverTime, new GeoLocation(latLng.latitude,latLng.longitude), new GeoFire.CompletionListener() {
#Override //Save geolocation with timestamp in seconds
public void onComplete(String key, DatabaseError error) {
if (error != null) {
Log.v("Informe","There was an error saving the location to GeoFire: " + error);
} else {
Log.v("Informe","Location saved on server successfully!");
}
}
});
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
});
I created a helper class including the functions and processes I would like to perform over my data in FirebaseDatabase.
The following function was meant to get all Posts in posts Node in fireabseDataBase:
public static Jobs getAllJobs() {
Posts posts= new Posts();
postsDBRef= FirebaseDatabase.getInstance().getReference("posts").getRef();
postsDBRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot jobSnapshot: dataSnapshot.getChildren()) {
// TODO: handle the post
String key = jobSnapshot.getKey();
//here
Post post= jobSnapshot.child(key).getValue(Post.class);
// and here
posts.add(post);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.i(getClass().getName().toString(), ": " + databaseError.getMessage());
}
});
return posts;
}
in the onDataChange function I am trying to loop over the data and retrieve it, although while logging I do get data but the post object keeps giving Null
Here's How the data look like in firebase DataBase
{
"posts" : {
"-Kr9-ii-ArpC3fLuuGnb" : {
"address" : "sfhhfg",
"applied" : false,
"currency" : "le",
"description" : "ehdhyf",
"latitude" : 0,
"longitude" : 0,
"noOfWorkers" : 2,
"rating" : 0,
"reported" : false,
"salary" : "1250",
"title" : "rgchu",
"userId" : 0
},
"-Kr94BDkdEZrmxmjxv_Z" : {
/../
},
"-Kr9Dz3S0BQEYv4VO2l3" : {
/../
},
"-Kr9XqDPUvCRcv0lwFy_" : {
/../
}
}
}
and here’s what am getting from the Debugger in a single loop
DataSnapshot { key = -Kr9-ii-ArpC3fLuuGnb, value = {address=sfhhfg,
rating=0, title=rgchu, reported=false, description=ehdhyf, userId=0,
longitude=0, noOfWorkers=2, latitude=0, currency=le, applied=false,
salary=1250} }
Any idea what am I doing wrong?
After several trials
.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
Job job = snapshot.getValue(Job.class);
System.out.println(job.getTitle());
}
}
#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) {
Log.i(getClass().getName().toString(), ": " + databaseError.getMessage());
}
});
Using .addChildEventListener was the only Solution for my issue, Still investigation the reason.
i have edited your code and it should work fine now, i have used the same code snippets in several projects before. make sure your pojo matches the json data, you can use this tool to make it easier for yourself.
and the most important thing , Don't use Firebase as functions that return values - it goes against it's asynchronous nature.make Firebase perform it's task and then within the closure (block) go to the next step.
P.S: before using ValueEventListener or ChildEventListener read the difference between them from here.
public void getAllJobs() {
Posts posts= new Posts();
postsDBRef= FirebaseDatabase.getInstance();
postsDBRef.child("posts").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot jobSnapshot: dataSnapshot.getChildren()) {
Post post= jobSnapshot.getValue(Post.class);
posts.add(post);
}
// go to next step from here e.g handlePosts(posts);
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.i(getClass().getName().toString(), ": " + databaseError.getMessage());
}
});
}
This is my JSON Structure:
{
"users" : {
"userKey1" : {
"deviceToken" : "deviceTokenID",
"displayName" : "Name here",
"dob" : "28/6/2017",
"email" : "efg#wxy.com",
"gender" : "M"
},
"userKey2" : {
"deviceToken" : "deviceTokenID",
"displayName" : "Name here",
"dob" : "28/1/2017",
"email" : "abc#xyz.com",
"gender" : "M",
}
}
}
In my android Firebase file, I type this:
Query findUser = mDatabase.child("users").orderByChild("email").equalTo("abc#xyz.com");
findUser.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
Log.i("findUser","Snapshot: " + dataSnapshot.toString());
}
#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) {
}
});
I get the following log:
06-28 22:06:50.190 30962-30962/in.sample.project I/findUser: Snapshot: DataSnapshot { key = users, value = null }
I do not know why I am getting null when cleary I have a child who's email node matches "abc#xyz.com".
You need a foreach loop to get the user in your findUser query because your query contains a collection.
Query findUser = mDatabase.child("users").orderByChild("email").equalTo("abc#xyz.com");
put this part in your eventlistener
....
public Iterable<DataSnapshot> getChildren (){
for (DataSnapshot child : parent.getChildren()) {
....
}
}
Needed to add security rules.
rules: {
"users" : {
".indexOn": "email",
}
}
I guess, that did the trick. Hope it helps someone. :)