Wait until complete onChildAdded in firebase querys, Android - java

I'm new to Firebase and working on simple project. I have a method to get a list of teachers from Firebase and add their emails to an ArrayList and return.
public ArrayList<String> getTeacherList() {
temp = new ArrayList();
Firebase node = ref.child("teachers");
Query query = node.orderByChild("subject").equalTo(selected_subject);
query.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
Map<String,Object> value = (Map<String, Object>) dataSnapshot.getValue();
Log.d("TEACHER"," "+value.toString());
Iterator entries = value.entrySet().iterator();
while (entries.hasNext()) {
Map.Entry thisEntry = (Map.Entry) entries.next();
String key = (String) thisEntry.getKey();
Object data = thisEntry.getValue();
if(key.equals("email")) {
temp.add(data.toString());
Log.d("ENTRY",": DATA "+data.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(FirebaseError firebaseError) {
}
});
Log.d("RETURN", temp.toString());
return temp;
}
This method is working fine. But the problem is this returns the temp variable before completing adding child.
For example my Android Monitor is like that :
D/RETURN: []
D/TEACHER: {number=1, name=Teacher, email=teacher#new.lk, subject=Physics}
D/ENTRY: : DATA teacher#new.lk
Is there any way to wait until complete thatonChildAdded method..?
=========================================================================
I tried in this way also. But then it stopped at inside method.
Even it didn't go to Log.d("TEACHER", " " + value.toString()); tag here.
public ArrayList getTeacherList() throws InterruptedException {
temp = new ArrayList();
Firebase node = ref.child("teachers");
Query query = node.orderByChild("subject").equalTo(selected_subject);
semaphore = new Semaphore(0);
query.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
Map<String, Object> value = (Map<String, Object>) dataSnapshot.getValue();
Log.d("TEACHER", " " + value.toString());
Iterator entries = value.entrySet().iterator();
while (entries.hasNext()) {
Map.Entry thisEntry = (Map.Entry) entries.next();
String key = (String) thisEntry.getKey();
Object data = thisEntry.getValue();
if (key.equals("email")) {
setArrayList(data.toString());
Log.d("ENTRY", ": DATA " + data.toString());
}
}
semaphore.release();
}
#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(FirebaseError firebaseError) {
}
});
semaphore.acquire();
return temp;
}

public class MainActivity extends AppCompatActivity{
public static Bus bus;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
bus = new Bus(ThreadEnforcer.MAIN);
bus.register(this);
//CALL TO LOAD DATA FROM FIREBASE
loadData();
}
private void loadData(){
DatabaseReference mDatabase = FirebaseDatabase.getInstance().getReference();
mDatabase.child("my-data").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
//CREATE YOUR OWN OBJECT USING RECEIVED DATA
MyObject o = new MyObject();
//CALL FOR METHOD nextMethod()
bus.post(o);
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.d("Error", databaseError.toString());
}
});
}
#Subscribe
private void nextMethod(MyObject o){
}
}
This is a very basic example for a usage of otto - EventBus. From bus.post(o), you can call to relavant Subscribed method.

Solved :
I used Otto EventBus Library from http://square.github.io/otto/ and it worked for me

Related

how to fetch data in range using timestamp - Firebase, Android, Real-time Database

I am using firebase real-time database to store and retrieve data but for some reason, I am unable to achieve what I want.
I want to get 20 records at a time on swipe refresh layout.
My data looks like:
Initially, I load 50 records:
chat.limitToLast(default_num_of_messages).addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
Map map = dataSnapshot.getValue(Map.class);
String user_id = map.get("user_id").toString();
String message = map.get("message").toString();
String timestamp = map.get("timestamp").toString();
Log.d("MAP", map.toString());
ChatMessage chat_message = new ChatMessage();
chat_message.setUser_id(user_id);
chat_message.setMessage(message);
chat_message.setTimestamp(Long.parseLong(timestamp));
Log.d("CHAT MSG", chat_message.toString());
if(Integer.parseInt(user_id) == Utils.getInstance().getLoggedInUser().getId()) {
addMessageBox(chat_message, 1);
}
else{
addMessageBox(chat_message, 2);
}
}
after these on refresh, I want to load 20 more. I am trying to do something like these. but it does not seem to return.
#Override
public void onRefresh() {
if(refresh_toggle) {
swipe_view.setRefreshing(true);
Log.d("CHAT", "REFRESHED!!!");
//reload new data
chat.orderByChild("timestamp").endAt(10).addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
Map map = dataSnapshot.getValue(Map.class);
String user_id = map.get("user_id").toString();
String message = map.get("message").toString();
String timestamp = map.get("timestamp").toString();
Log.d("CHATS", map.toString());
ChatMessage chat_message = new ChatMessage();
chat_message.setUser_id(user_id);
chat_message.setMessage(message);
chat_message.setTimestamp(Long.parseLong(timestamp));
swipe_view.setRefreshing(false);
if(Integer.parseInt(user_id) == Utils.getInstance().getLoggedInUser().getId()) {
addMessageBox(chat_message, 1);
}
else{
addMessageBox(chat_message, 2);
}
}
#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(FirebaseError firebaseError) {}
});
}
}
As you can see I am trying to orderByChild "timestamp" and then tried different combination of startAt(), endAt() but nothing seem to work.
Try this:
int number_item_load = 10; //changed number if you want
public void loadMore(int offset, String theLastValue) {
// theLastValue: depend on your orderByChild() 's value
mDatabase = FirebaseDatabase.getInstance().getReference().child(Utils.FB_ROOT);
mEventListener = new ChildEventListener() {
#Override
public void onChildAdded(#NonNull DataSnapshot dataSnapshot, #Nullable String s) {
if (isAlreadyAttach()) {
// Get your item and callback to view and insert to listView
}
} ...
};
mDatabase.orderByChild("yourvalue").startAt(theLastValue).limitToFirst(number_item_load ).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
// TODO Finished loaded number_item_load item
OnLoadedFinish(); //
mDatabase.removeEventListener(mEventListener); // Remove listener after load
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
mDatabase.orderByChild("yourvalue").startAt(theLastValue).limitToFirst(number_item_load ).addChildEventListener(mEventListener);
}
It worked perfectly in my case. Goodluck

Firebase - Getting a child value under unique key

I am using Google Firebase to store database of user heartbeat from Android Wear.
Now I'm trying to retrieve the heartbeat value on child "tmpHR" but the value keeps on showing null.
Here is the structure of my database :
I want to retrieve int tmpHR child value under the random key
Here's my lines of code :
Integer hrValue;
ArrayList<Integer> array1;
array1 = new ArrayList<>();
DatabaseReference userdatabase = FirebaseDatabase.getInstance().getReference().child("Users");
DatabaseReference ref = userdatabase.child(user.getUid()).child("hrvalue").child("nilaihr");`
`ref.addChildEventListener(new ChildEventListener() {`
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
for (DataSnapshot uniqueKeySnapshot : dataSnapshot.getChildren()) {
//Loop 1 to go through all the child nodes of nilaihr
//store random key in uniqueKeySnapshot
Integer hrValue = uniqueKeySnapshot.child("tmpHR").getValue(Integer.class);
// random key -> child tmpHR -> store in Integer hrValue
Log.i("heart rate value", "hrvalue " + hrValue);
showData(dataSnapshot);
}
}
#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) {
}
});
}
private void showData(DataSnapshot dataSnapshot) {array1.add(hrValue);}
And here's the logcat :
05-15 19:01:55.824 27624-27624/com.example.amiramaulina.retrievedataheartbeat I/database array: array database [null, null, null,....., null]
05-15 19:01:55.824 27624-27624/com.example.amiramaulina.retrievedataheartbeat I/heart rate value: hrvalue null
Before, there was only one child (which is Integer "tmpHR") under the random key, and I have succeeded to retrieve the value. Since I added another child (String Date), the value of tmpHR keeps on returning null.
Any solutions would be appreciated.
Thank you!
Try the following:
ref.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
Integer hrValue = dataSnapshot.child("tmpHR").getValue(Integer.class);
showData(dataSnapshot);
}
#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) {
}
});
Remove the for loop, since you do not need to loop to be able to retrieve tmpHR in this case.

addChildEventListener is not working

In my app I want to add data to my Firebase database and read the data simultaneously. So I am using addChildEventListener() method to retrieve data. But this method is not working and the app is getting crashed everytime.
The code is given below:
public class ChatActivity extends AppCompatActivity {
LinearLayout layout;
RelativeLayout layout_2;
ImageView sendButton;
EditText messageArea;
ScrollView scrollView;
//Firebase reference1, reference2;
private DatabaseReference reference1, reference2,reference3;
private FirebaseAuth firebaseAuth;
private FirebaseUser firebaseUser;
private String username,userId;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chat);
layout = (LinearLayout) findViewById(R.id.layout1);
layout_2 = (RelativeLayout)findViewById(R.id.layout2);
sendButton = (ImageView)findViewById(R.id.sendButton);
messageArea = (EditText)findViewById(R.id.messageArea);
scrollView = (ScrollView)findViewById(R.id.scrollView);
username=getIntent().getStringExtra("abc");
userId=getIntent().getStringExtra("abcd");
reference1= FirebaseDatabase.getInstance().getReferenceFromUrl("https://career-dna-bec2e.firebaseio.com/CHAT/user/" +username);
reference2=FirebaseDatabase.getInstance().getReferenceFromUrl("https://career-dna-bec2e.firebaseio.com/CHAT/admin/"+username);
sendButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String messageText = messageArea.getText().toString();
if(!messageText.equals("")){
Map<String, String> map = new HashMap<String, String>();
map.put("message", messageText);
map.put("user",userId);
reference1.push().setValue(map);
reference2.push().setValue(map);
messageArea.setText("");
}
}
});
try
{
reference1.addChildEventListener(new com.google.firebase.database.ChildEventListener() {
#Override
public void onChildAdded(com.google.firebase.database.DataSnapshot dataSnapshot, String s) {
Map<String, String> map = dataSnapshot.getValue(Map.class);
String message = map.get("message").toString();
String userName = map.get("user").toString();
if(userName.equals(userId)){
addMessageBox("You:-\n" + message, 1);
}
else{
addMessageBox("admin"+ ":-\n" + message, 2);
}
}
#Override
public void onChildChanged(com.google.firebase.database.DataSnapshot dataSnapshot, String s) {
}
#Override
public void onChildRemoved(com.google.firebase.database.DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(com.google.firebase.database.DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
catch (Exception e)
{
Toast.makeText(getApplicationContext(),"error:"+e,Toast.LENGTH_SHORT).show();
}
}
The app is crashing with this error:
02-23 20:43:15.832 12583-12583/com.ankush.anthroplace.careerdna
E/AndroidRuntime: FATAL EXCEPTION: main Process:
com.ankush.anthroplace.careerdna, PID: 12583
com.google.firebase.database.DatabaseException: Class java.util.Map
has generic type parameters, please use GenericTypeIndicator instead
Try using :
Map<String, Object> map = new HashMap<String, Object>();
While retrieving data use this :
Map<String, Object> map = dataSnapshot.getValue(Map.class);
But, if you really want to use Map class with 2 Strings as parameters, use this inside onChildAdded method :-
GenericTypeIndicator<Map<String, String>> genericTypeIndicator = new GenericTypeIndicator<Map<String, String>>() {};
Map<String, String> map = dataSnapshot.getValue(genericTypeIndicator);
There's no need to enter the full URL, use this instead
DatabaseReference reference1 = FirebaseDatabase.getInstance().getReference().child("CHAT").child("user").child(username);
DatabaseReference reference2 = FirebaseDatabase.getInstance().getReference().child("CHAT").child("admin").child(username);
And, one more thing you are doing wrong is, you are adding ChildEventListener everytime the sendButton is clicked, which is not neccesary, just create a ChildEventListener inside onCreate() and not in onClick() like this:-
ChildEventListener childEventListener = new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
Map<String, String> map = dataSnapshot.getValue(Map.class);
String message = map.get("message").toString();
String userName = map.get("user").toString();
if(userName.equals(userId)){
addMessageBox("You:-\n" + message, 1);
}
else{
addMessageBox("admin"+ ":-\n" + message, 2);
}
}
#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.e("TAG", databaseError.toString());
}
};
//attaching the ChildEventListener to the DatabaseReference
reference1.addChildEventListener(childEventListener);
Don't forget to remove the listener in onStop()
#Override
protected void onStop() {
super.onStop();
reference1.removeEventListener(childEventListener);
}
I have solved the problem myself by using an object to store the data instead of hashmap. I am not sure why map is not working,but use of object is doing fine.
the code is given below:
sendButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String messageText = messageArea.getText().toString();
if(!messageText.equals("")){
map=new messageModel(messageText,userId);
reference1.child(""+n).setValue(map).addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
if(task.isSuccessful())
{
reference2.child(""+n2).setValue(map).addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
if(task.isSuccessful())
{
messageArea.setText("");
}
}
});
}
else
{
Toast.makeText(getApplicationContext(),"server problem try again",Toast.LENGTH_SHORT).show();
}
}
});
}
}
});
try
{
reference1.addChildEventListener(new com.google.firebase.database.ChildEventListener() {
#Override
public void onChildAdded(com.google.firebase.database.DataSnapshot dataSnapshot, String s) {
map = dataSnapshot.getValue(messageModel.class);
String message = map.myMessage;
String userName = map.user;
if(userName.equals(userId)){
addMessageBox("You:\n" + message, 1);
}
else{
addMessageBox("USER:\n" + message, 2);
}
}
#Override
public void onChildChanged(com.google.firebase.database.DataSnapshot dataSnapshot, String s) {
}
#Override
public void onChildRemoved(com.google.firebase.database.DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(com.google.firebase.database.DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
catch (Exception e)
{
Toast.makeText(getApplicationContext(),"error:"+e,Toast.LENGTH_SHORT).show();
}
as u can see no part of the code has been changed except the map portion.The code of messageModel class is as following:
public class messageModel {
public String myMessage;
public String user;
public messageModel()
{
}
public messageModel(String a,String b)
{
myMessage=a;
user=b;
}
}

Android stored values in string returning null

I am trying to store some data retrieved "receiver" and "sender" from my firebase database in a string "room_type_1" and "room_type_2" in my code. The data is being retrieved alright but it isn't being passed to the string. It keeps on returning null. What might be the problem?
databaseReference3.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
receiver = (String) dataSnapshot.child(user_id).child("username").getValue();
Log.v("postID2", receiver);
sender = (String) dataSnapshot.child(user_id).child("username1").getValue();
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
room_type_1 = sender + "_" + receiver;
room_type_2 = receiver + "_" + sender;
databaseReference6 = databaseReference5.child(room_type_1);
databaseReference7 = databaseReference5.child(room_type_2);
sendButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String message = messageArea.getText().toString();
if (!message.equals("")) {
databaseReference6.push().setValue(new Messages(message, sender));
databaseReference7.push().setValue(new Messages(message, sender));
}
}
});
databaseReference6.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
//Messages messages = dataSnapshot.getValue(Messages.class);
String message = (String) dataSnapshot.child("message").getValue();
String userName = (String) dataSnapshot.child("sender").getValue();
if(userName.equals(sender)){
addMessageBox("You:-\n" + message, 1);
}
else{
addMessageBox(receiver + ":-\n" + message, 2);
}
}
#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) {
}
});
}
The code given only creates objects with callbacks. The callback methods will be called only after all of these lines are executed, including the assignments. For example, at the moment the line
room_type_1 = sender + "_" + receiver;
executes, neither sender nor receiver have yet been set to the values from your database.
I suggest you learn more about how callback methods work.
To fix this, just move the assignment into the callback method after you assign sender and receiver.
Note that variable names ending in numbers indicate that you should use an array or List.

How to retrieve specific list of data from firebase

I am new to firebase and nosql database
I am trying to retrieve some specific data from a Firebase. I have a node universities and that node has many unique identifier as node which has so more data into it. I want to retrieve name from each node.
Please have a look at this.
What I have tried so far: I tried using addChildEventListener but this only listens first child. I've debugged it and it was only showing the values of the first child of universities node.
myRef.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
List<University> university = new ArrayList<>();
Map<String, Object> td = (HashMap<String, Object>) dataSnapshot.getValue();
Log.d("TAG",dataSnapshot.getValue().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 have also tried addValueEventListener. This listens the whole node and return whole data, but I am unable to extract "name" for it since it contains unique identifiers.
Please guide me into the right directions.
myRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
University university = dataSnapshot.getValue(University.class);
List<University> universities = (List<University>) dataSnapshot.getValue(University.class);
*//*List<String> lst = new ArrayList<String>();
for(DataSnapshot dsp : dataSnapshot.getChildren()){
lst.add(dsp.getKey());
}
Log.d("TAG",lst.toString());*//*
Map<String, Object> td = (HashMap<String, Object>) dataSnapshot.getValue();
*//*List<Object> values = new ArrayList<Object>(td.values());
List<String> list=new ArrayList<String>();
for(int i=0; i<values.size(); i++){
list.add((String) values.get(i));
}*//*
Log.d("TAG", university.toString());
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
If you using addValueEventListener for retrieve all university from Firebase
List<University> universityList = new ArrayList<>();
myRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
universityList.clear();
for (DataSnapshot postSnapshot: snapshot.getChildren()) {
University university = postSnapshot.getValue(University.class);
universityList.add(university);
// here you can access to name property like university.name
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
System.out.println("The read failed: " + firebaseError.getMessage());
}
});
Or if you using addChildEventListener
List<University> universityList = new ArrayList<>();
myRef.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
University university = dataSnapshot.getValue(University.class);
universityList.add(university);
Log.i(TAG,"add university name = " + university.name);
...
}
...
}
Regarding Linh's answer about using the addEventListener method. You can use GenericTypeIndicator in order to directly create the list of objects you'll be needing. Here's a quick example:
List<University> universityList = new ArrayList<>();
myRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
GenericTypeIndicator<List<University>> t = new GenericTypeIndicator<List<University>>() {};
universityList = snapshot.getValue(t);
}
#Override
public void onCancelled(DatabaseError databaseError) {
System.out.println("The read failed: " + firebaseError.getMessage());
}
});
Hope this answer helps.
You can use equalTo() of Query in which you can pass your custom string to match with firebase database.
like this way you can create your Query and set listener with that
Query queryRef = myFirebaseRef.orderByChild("name").equalTo("some name");
refer this thread for more.
Here is nice example from firebase officials, refer that and you will get clear idea
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot postSnapshot : dataSnapshot.getChildren()) {
User user = postSnapshot.getValue(User.class);
list.add(user);
}
}
class User
class User{
String name;
String phone;
public String getname() {
return name;
}
public void setname(String name) {
this.name = name;
}
public String getphone() {
return phone;
}
public void setphone(String phone) {
this.phone = phone;
}
List bind with class
List<User> list== new ArrayList <>();
100% it will work
I had the same problem .You can just change the access specifier i.e public to private in the Model class you problem will be solved

Categories