I use firebase real time database.
I have set : "FirebaseDatabase.getInstance().setPersistenceEnabled(true);"
So ok perfect, when I'am offline, all the data are being loaded just like I was online.
But I have a few questions.
In my app, I have chat group with few people.
Let's assume someone is in a group and is offline. He writes what he wants in the chat and the messages will be send when he will turn online.
But, while he is offline, someone kicks him out of the group. How do I tell firebase :
if (the user still belongs to the group)
{
sync messages
}
else
{
don't sync messages
}
I have tried to check if the users still belong to the group while he is offline but when it goes back online everything has already been checked in offline mode so the data are just sent without reckecking again.
Let's imagine I have 50 messages in my chat. While I'am offline, I get 5 messages. When I go back online, will it redownload the 55 messages or just the 5 extras message?
I am just trying to figure out if I the firebase sync is going to cost me a lot of data or not.
EDIT
Here is the way I retrieve data from chat :
public void retrievedata() {
UserMessageRef.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(#NonNull DataSnapshot dataSnapshot, #Nullable String s) /**** HERE IS THE PROBLEM***/
{
if (dataSnapshot.exists()) {
DisplayMessages(dataSnapshot);
}
}
#Override
public void onChildChanged(#NonNull DataSnapshot dataSnapshot, #Nullable String s) {
//Nothings
}
#Override
public void onChildRemoved(#NonNull DataSnapshot dataSnapshot) {
//Nothings
}
#Override
public void onChildMoved(#NonNull DataSnapshot dataSnapshot, #Nullable String s) { //Nothings
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
//Nothings
}
});
}
//function to retrieve messages
private void DisplayMessages(DataSnapshot dataSnapshot) { Iterator iterator = dataSnapshot.getChildren().iterator();
String chatImage = (String) ((DataSnapshot) iterator.next()).getValue();
String chatMessage = (String) ((DataSnapshot) iterator.next()).getValue();
String nID = (String) ((DataSnapshot) iterator.next()).getValue();
String chatTime = (String) ((DataSnapshot) iterator.next()).getValue().toString();
ListOfChats.add(new ChatClass(nID, chatMessage, chatTime, chatImage));
adapter.notifyDataSetChanged();
}
Now here is the problem.
If I'am offline and go into the chat, the messages are just retrieved from disk, ok works fine.
BUT If I go online and I got 2 new messages : instead of calling the onChildAdded for the 2 news messages, it download ALL of the messages and THEN call the onChildAdded with the old child and the 2 news child. What I want, is that the 2 news messages are added to the local json file in my phone and that everyting isnt downloaded everytime I open the conservation.
So with a firebase database which is about 30KB, I got 5-10 MB of downloads everyday even If I add 2KB in it per day... (It's for university project so we are like 2 users for now)
So is there a solution to this (firebase just update the json file into the local database without downloading eveything) or do I have to do my own SQL database and make a smart sync adapter?
I am sure that firebase must have done something smart about this.
Calling FirebaseDatabase.getInstance().setPersistenceEnabled(true) merely ensures that recently received data is cached on disk. It doesn't enable automatic synchronization of the data.
To enable automatic synchronization of a node, you can call keepSynced(true) on a reference to that node.
When the Firebase client finds it already has data for a reference that it is connecting a listener to, it hashes the local data, and sends the resulting hashes to the server. The server then sends back the data that is needed to update the client's version of the data. This is typically a lot less data than the full data set.
Also see my answer here: How does firebase client know what data to sync?
Related
I m developing taxi app like uber, I have developed android app to get location update every 5 seconds, currently I am using retrofit to sync location data to server. But we are expecting that thousands of drivers will be sending location data every five seconds and HTTP request where connection open than send data and close will cause problems. I am trying to use TCP/IP or sockets to send data, I want socket remain open and location data keep updating and when socket get closed it connect automatically. I have not found any help from any forum regarding this issue. Need help how can I achieve this and is this approach is better or not.
Rather Than socket updation you can go for the firebase also.
When you get the location you can push the changes to the Firebase in the following way
private FirebaseDatabase database = FirebaseDatabase.getInstance();
private DatabaseReference myRef = database.getReference();
When posting to the database you have to uniquely identify a row, just like in normal database (i.e ID, UUID), so you'll have to generate one and store into you SharedPreferences for example.
public void postToDatabase(UserLocation location) {
DatabaseReference usersRef = myRef.child(someUUID);
usersRef.setValue(location);
}
To listen for changes you have to implement a ValueEventListener. An example, but will have to adapt to it your needs
ValueEventListener valueEventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot snapshot : dataSnapshot.getChildren()) {
UserLocation location = new UserLocation();
location.setLongitude((String) snapshot.child("longitude").getValue());
location.setLatitude((String) snapshot.child("latitute").getValue());
//Dispatch your changes in application, i.e with and EventBus
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
};
Right now I'm developing an android app, and I just started to work with Firebase.
My question is: How can I retrieve data from the firebase database, without use listeners ?
In my game, I'm saving the high scores of all the users, and I need to take the data from the database when user go into "leader-boards" page.
I saw some solutions, which is not good for my case.
One of them is:
mRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String value = dataSnapshot.getValue(String.class);
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
});
This solution is not good for me, because I cant afford to my app to go into the server every time high score of user is changing, because if I would have 20,000 people who playing at the same time, then the game will stuck.
so I cant use this listener, because it will make the game very slow.
My goal is to to find a way to change the high score, without alerting the other people who is currently playing the game, I mean that I need to update every user score for his own, and when user go to the "leader boards" page, only then I will go to the server.
what is the right solution here ?
Or can I use this listener in another way?
If my question is not clear, then ask me in the comment please.
Thank you !!
my lines:
public static void setUserHighScoreToServer(Context context,boolean isClassic,int scoreNum)
{
com.firebase.client.Firebase mRef;
mRef= new com.firebase.client.Firebase("...");
String name = InternalStorage.getUserName(context);
String classic = "";
if(isClassic)classic="Classic";
else classic="Arcade";
com.firebase.client.Firebase mRefChild = mRef.child(name+classic);
mRefChild.setValue(String.valueOf(scoreNum));
}
This is the OFFICIAL way to retrieve data once without listening for data changes.
// Add all scores in ref as rows
scores.addListenerForSingleValueEvent( new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
for (DataSnapshot child : snapshot.getChildren()) {
...
}
}
}
more information here: https://firebase.google.com/docs/reference/android/com/google/firebase/database/DataSnapshot
If you donĀ“t need to update on real time, you can always do a Rest api call to your database.
Just do a GET call to retrieve your data
https://[PROJECT_ID].firebaseio/[path].json
and you are good to go
You can also update or create new keys using rest api calls.
My app is using Firebase and there are almost 200 users live at the a given time. Most of the users are complaining that the data doesn't load. I was using ChildEventListener for obtaining the data which keep the connection alive and reflects live changes. There is a limit of 100 connections in the free plan. I guess that is the reason my data is not loading at times. After reading the doc I found another way to read data using ValueEventListener. Below is the code I'm currently using
public void getImages() {
Query imagesQuery = FirebaseDatabase.getInstance().getReference().child("englishDps").child(mChildName).orderByKey().limitToLast(21);
ChildEventListener childEventListener = new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
Image image = dataSnapshot.getValue(Image.class);
image.setNodeKey(dataSnapshot.getKey());
mTempImages.add(image);
if (mTempImages.size() == 21) {
mLastKey = mTempImages.get(0).getNodeKey();
Collections.reverse(mTempImages);
mTempImages.remove(mTempImages.size() - 1);
mImages.addAll(mTempImages);
setAdapter();
}
}
#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) {
if (isAdded()) {
Toast.makeText(getActivity(), "Problem loading more images...", Toast.LENGTH_LONG).show();
}
}
};
ValueEventListener valueEventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot imageSnapshot : dataSnapshot.getChildren())
{
Image image = imageSnapshot.getValue(Image.class);
image.setNodeKey(imageSnapshot.getKey());
mTempImages.add(image);
if (mTempImages.size() == 21) {
mLastKey = mTempImages.get(0).getNodeKey();
Collections.reverse(mTempImages);
mTempImages.remove(mTempImages.size() - 1);
mImages.addAll(mTempImages);
setAdapter();
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
};
// imagesQuery.addChildEventListener(childEventListener);
// imagesQuery.addValueEventListener(valueEventListener);
imagesQuery.addListenerForSingleValueEvent(valueEventListener);
}
According to the docs
"While using a ChildEventListener is the recommended way to read lists
of data, there are situations where attaching a ValueEventListener to
a list reference is useful.
Attaching a ValueEventListener to a list of data will return the
entire list of data as a single DataSnapshot, which you can then loop
over to access individual children.
Even when there is only a single match for the query, the snapshot is
still a list; it just contains a single item. To access the item, you
need to loop over the result:.
This pattern can be useful when you want to fetch all children of a list in a single operation, rather than listening for additional
onChildAdded events."
I was thinking this will solve the data loading problem but my previous version of the app will still keep using live connection and I'm still seeing random success and failures for data load call in new version of the app with more than 150+ users live right now on old version of the app. What will happen if the old version of the app opens more than 100 connection and the new version of the app tries to load data ? i.e. if 100 connections in the free plan are used will a query with
imagesQuery.addListenerForSingleValueEvent(valueEventListener);
succeed or fail ?
When an Android app first uses the Firebase Database SDK, it makes a connection to the Firebase servers. If there are at that moment already as many connection as are allowed to your database, the new connection will be rejected. The type of listener has no influence on this.
For a lot of discussions covering this already, see this list. Some good ones:
Limitation of free plan in firebase
How the Connection is calculated in Firebase
When are new connections allowed after limit of 100 concurrent connection is reached in firebase?
How exactly are concurrent users determined for a Firebase app?
How to limit concurrent connections on Firebase Android
Having looked at your code. I recommend inserting a closing connection once the read of images from json are completed. In the free package there is a limit of connections so once they read the images, they're technically still connected.
Looking at your Datasnapshot, they don't do anything but still querying the Firebase. I also recommend look into indexing too.
https://firebase.google.com/docs/database/rest/save-data
https://firebase.google.com/docs/database/security/indexing-data
I am developing a new Android app using Firebase (first time to use Firebase). and I opted to use the Persistence mode as it suits my app better.
My problem is that the app doesn't sync data to the server, hence, other devices, even if the device used to store data is online!!
Code is fine (as far as I can tell), if I disable the Persistence mode, everything works fine, but for sure I don't have the cached data on the device.
This happens in different devices and emulator as well, and the weird thing is that sometimes the devices sync, and then stop syncing again, for no reason!
I appreciate any recommendations here.
code:
My App class:
//....
Firebase.setAndroidContext(this);
Firebase.getDefaultConfig().setPersistenceEnabled(true);
Firebase.getDefaultConfig().setLogLevel(Logger.Level.DEBUG);
//...
Sending code in Message class:
//....
Firebase senderRef = new Firebase(MyApp.FirebaseURL).child("Messages").child(sender_Id);
senderRef.keepSynced(true);
senderRef.push().setValue(this, null);
//....
receive code:
//...
Firebase ref = new Firebase(MyApp.FirebaseURL).child("Messages").child(sender_Id);
Query query = ref.orderByKey();
query.limitToLast(MAX_CHAT_MESSAGES_TO_SHOW);
query.keepSynced(true);
query.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
Log.d("Firebase", "Message Child Added");
Message message = dataSnapshot.getValue(Message.class);
mMessages.add(0, message);
mAdapter.notifyDataSetChanged();
}
//...
I have done in both ways:
If you want to sync with firebase server
FirebaseDatabase.getInstance().goOnline();
If you do not want to sync with firebase server
FirebaseDatabase.getInstance().goOffline();
Full code:
public class MyApplication extends Application {
#Override
public void onCreate() {
super.onCreate();
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
/**
* User anyone function from below as required,
* Read comment proper
*/
// If you want to sync with firebase server
FirebaseDatabase.getInstance().goOnline();
// If you do not want to sync with firebase server
FirebaseDatabase.getInstance().goOffline();
}
}
Hope this would help you.
This is how my firebase database looks like.
I want to access the node accepting-status and say patient23 for example. I can easily do this by using addListenerForSingleValueEvent on nodes accepting-status and patient23 separately but then it would take me two different requests to get the required data. I can also get the required data by single request by adding addListenerForSingleValueEvent on the parent node beacon-final but then that would retrieve huge data because the patients are in large numbers.How do I do it in a single request and yet only retrieve only the required nodes. Thanks.
...but then it would take me two different requests to get the
required data.
The Firebase database is all one JSON object. If you select one part, you'll get everything underneath.
But, Firebase uses WebSockets, which is a persistent connection to the server. This means that you don't have to worry about making a request, because the only HTTP request that gets made is in the very beginning to establish the socket.
So in your case it's completely feasible to make two separate "requests" for the data, because there's no real overhead to consider. The device's radio is already on, and a WebSocket header is merely 6 bytes.
You can easily create a listener on /accepting-status, as well as /patients/patient23. This is a standard Firebase practice to have multiple listeners.
// Get a root reference
Firebase rootRef = new Firebase("<my-firebase-app>");
// accepting-status ref
Firebase statusRef = rootRef.child("accepting-status");
// patient23 ref
Firebase patientRef = rootRef.child("patients").child("patient23");
// Listen for status updates
statusRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
System.out.println(snapshot.getValue());
}
#Override
public void onCancelled(FirebaseError firebaseError) {
// error
}
});
// Listen for patient updates
patientRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
System.out.println(snapshot.getValue());
}
#Override
public void onCancelled(FirebaseError firebaseError) {
// error
}
});