I am trying to wrap a Service around a (Firebase query)x(Perform some action) functionality. Namely, I would need to retrieve data from Firebase and then, for each item retrieved, perform a certain download-a-file-like action (which is itself can be an AsyncTask or a Service). I have a handler inside my service:
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
FirebaseUser user= FirebaseAuth.getInstance().getCurrentUser();
DatabaseReference ref= FirebaseDatabase.getInstance().getReference().child("users").child(user.getUid()).child("oj_goals");
ref.orderByChild("ojTag").addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
OjGoal goal= dataSnapshot.getValue(OjGoal.class);
Log.d("inside onChildAdded",goal.getMe());
data.add(goal);
}
#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) {
}
});
//FIXME: the below code continues to execute even if the Firebase-call has not yet completed
//
Intent intent= new Intent("com.example.sj.keymeasures.BAR_CHART_DATA_PREPARATION_FINISHED");
/**
* https://stackoverflow.com/questions/43800772/does-variable-order-matter-while-parcel-read-write-operation-in-parcelable
*/
intent.putParcelableArrayListExtra("oj_goals",data);
Log.d("[Handler]","sendingBroadcast with size "+data.size());
sendBroadcast(intent);
stopSelf(msg.arg1);
}
Now as you can see in the FIXME in the code, what I receive on the other end -- inside the dedicated receiver -- is an empty list. By now I know this whole thing was a bad idea, but how would you design what I am trying to accomplish (see first paragraph), without weird stuff like nested async-tasks?
It's pretty straightforward: the code that needs to execute after the child node was loaded, should be inside the onChildAdded function:
public void handleMessage(Message msg) {
super.handleMessage(msg);
FirebaseUser user= FirebaseAuth.getInstance().getCurrentUser();
DatabaseReference ref= FirebaseDatabase.getInstance().getReference().child("users").child(user.getUid()).child("oj_goals");
ref.orderByChild("ojTag").addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
OjGoal goal= dataSnapshot.getValue(OjGoal.class);
Log.d("inside onChildAdded",goal.getMe());
data.add(goal);
Intent intent= new Intent("com.example.sj.keymeasures.BAR_CHART_DATA_PREPARATION_FINISHED");
intent.putParcelableArrayListExtra("oj_goals",data);
Log.d("[Handler]","sendingBroadcast with size "+data.size());
sendBroadcast(intent);
stopSelf(msg.arg1);
}
#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) {
throw databaseError.toException(); // don't ignore errors
}
});
}
Related
I have 4 ChildEventListener in one activity. When activity launches 4th ChildEventListener executes 1st then 3rd, 2nd, 1st and some time it will be in reverse order 1,2,3,4. so it is not loading sequentially. ChildEventListener listeners example are mentioned below.
AfirebaseDatabaRef.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
}
#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) {
}
});
BfirebaseDatabaRef.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
}
#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 don't think you understand these listeners well, callback is being called for the node that is registered to it only this node no one else, so if you have multiple nodes and every node has callback registered to it, when change happens to any node the corresponding callback ONLY will be triggered, so the sequence should be changes in the nodes not the callbacks, i hope this answers your question.
I am facing a problem with adding older data to RecyclerView with SwipeRefreshLayout so, here is how i retrieve from Firebase and put data into RecyclerView
private void messageList() {
referenceMessages.orderByKey().limitToLast(10).addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
MessagesInfo messages = dataSnapshot.getValue(MessagesInfo.class);
result.add(messages);
adapter.notifyDataSetChanged();
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
MessagesInfo messages = dataSnapshot.getValue(MessagesInfo.class);
result.remove(messages);
adapter.notifyDataSetChanged();
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
as you can i see, I set limit of retrieved messages to last 10, what means the 10 newest messages will be displayed. So far everything works great, the messages are displayed like that
-20thmessage(the oldest)-
-21stmessage-
-...-
-29thmessage-
-30thmessage(the newest)-
now i use SwipeRefreshLayout like that
private void swipe(){
swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
#Override
public void onRefresh() {
referenceMessages.orderByKey().endAt("-LHUhkub4iaZNNzmtUcs").limitToLast(10).addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
MessagesInfo messages = dataSnapshot.getValue(MessagesInfo.class);
recyclerView.scrollToPosition(0);
result.add(0, messages);
adapter.notifyDataSetChanged();
}
#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) {
}
});
}
});
}
and it retrieves data properly, i mean previous 10 messages before hardcoded endAt() but after swiping places data not properly. If i dont use index 0
new data is placed at the botoom of latest message and it looks like that
-30thmessage(the newest)-
-10thmessage-
-...-
-19thmessage-
-20thmessage(the newest)-
and when i use index 0 data is added from the top of already loaded messages but like that
-20thmessage(the oldest)-
-19thmessage-
-...-
-11ththmessage-
-10th-
so it is added upside-down. Hope you understand me :) any inputs are highly appreciated
You might try clearing the list first and then add the new messages.
Android SwipeRefreshLayoutBasic
I want to be able to return random objects from the following image:
And not just the first object (-Kci1vM1dVBYBcPW7QA) which returns as is already. I have inserted the code from my app where it is currently reading from. The variables one, two and three are currently reading the first object but I want them to be able to retrieve random objects.
public void displayDeals(){
databaseReference.child("FruitDeals").child("bR4sR4flMkYFrw1SzuWA8hpOnY52").addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
deals_information = dataSnapshot.getValue(Deals_Information.class);
one = deals_information.getDeal();
two = deals_information.getPrice();
three = deals_information.getAisleNum();
}
#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) {
throw databaseError.toException();
}
});
}
I am trying to set my Firebase reference before my child listener kicks in and loads my data. I know onCreate is ahead of onResume but my code below seems to contradict it as my reference is still what I have set in my onResume(). It seems like what I set up in onCreate doesn't go before what I have in onCreate().
Why is that?
I am trying to read from a template in my database if another node doesn't have the user registered as a child yet.
Any hint would be appreciated.
p.s. I have logged out the references and the Log in OnResume() goes first as well.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.services_activity);
app = FirebaseApp.getInstance();
database = FirebaseDatabase.getInstance(app);
auth = FirebaseAuth.getInstance(app);
storage = FirebaseStorage.getInstance(app);
username = auth.getCurrentUser().getUid();
databaseRef = database.getReference("serv_hst");
servTempltRef = database.getReference("serv_tmplt");
databaseRef.child(username).addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
//I am trying to set my reference that I will use in OnResume() ..!!
if (dataSnapshot.hasChildren()) {
servTempltRef = database.getReference("serv_hst");
}
}
#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) {
}
});
}
#Override
protected void onResume() {
super.onResume();
servTempltRef.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
}
#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 inside the onChildAdded() will be executed asynchronously only when the data will be downloaded from Firebase Database.
So, after that the execution flow will execute the line in which there is the .addChildEventListener() method, it will go ahead and will continue to execute the next lines...
In your case, the onCreate() has no other lines to be executed. So, due to the Activity lifecicle, the onResume() method will be executed without the data you need.
My data on Firebase like this:
"node": {
"node1": {
"value1": value,
"value2": value,
"subSubNode":{
//....values....//
},
},
"node2": { ... },
"node3": { ... }
//...and so on ..//
}
First: I want to get all data from Firebase and render to my tree so I used addListenerForSingleValueEvent() function.
Then: I used addChildEventListener() function to listen when a node or child change, remove or added...
private void getData(Query ref){
ref.addListenerForSingleValueEvent(new ValueEventListener(){
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
**I render data on my tree here**
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
});
ref.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
Log.i(TAG, "onChildAdded");
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
Log.i(TAG, "onChildChanged");
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
Log.i(TAG, "onChildRemoved");
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
Log.i(TAG, "onChildMoved");
}
#Override
public void onCancelled(FirebaseError firebaseError) {
Log.i(TAG, "onCancelled");
}
});
}
But onChildAdded() method in addChildEventListener() function is always run first. Any idea or solution for me? Do I use addChildEventListener() function on that position correct?
The methods in ChildEventListener fire for both initial data and for subsequent changes to that data.
Once you call addChildEventListener(), you will get a call to onChildAdded() for each existing child node. You can use these event to build the initial tree. After the initial data has been added, you will receive a calls to all onChild... methods as the data changes.
So don't register for addListenerForSingleValueEvent() and simply build the tree from the methods of ChildEventListener.
Alternatively if you want to separately handle the initial data, you can make use of one of the event guarantees that Firebase has:
Value events are always triggered last and are guaranteed to contain updates from any other events which occurred before that snapshot was taken.
boolean isInitialValueLoaded = false;
ref.addListenerForSingleValueEvent(new ValueEventListener(){
public void onDataChange(DataSnapshot dataSnapshot) {
**I render data on my tree here**
isInitialValueLoaded = true;
}
public void onCancelled(FirebaseError firebaseError) {
}
});
ref.addChildEventListener(new ChildEventListener() {
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
if (!isInitialValueLoaded) return;
Log.i(TAG, "onChildAdded");
}
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
if (!isInitialValueLoaded) return;
Log.i(TAG, "onChildChanged");
}
public void onChildRemoved(DataSnapshot dataSnapshot) {
if (!isInitialValueLoaded) return;
Log.i(TAG, "onChildRemoved");
}
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
if (!isInitialValueLoaded) return;
Log.i(TAG, "onChildMoved");
}
public void onCancelled(FirebaseError firebaseError) {
Log.i(TAG, "onCancelled");
}
});