I got an app which uses Firebase.
The Firebase's records holds a "status" field which is supposed to give information if the user is offline or online.
I initialize it like that:
fireBase = new Firebase("https://myProj.firebaseio.com/users");
child = fireBase.child(MyApp.myStringIdentifier);
child.child("status").setValue(ClientStatus.ONLINE);
child.child("status").onDisconnect().setValue(ClientStatus.OFFLINE);
The initialization works fine, and the onDisconnect() is also called after a lot of time there's no internet connection and firebase does change the status to offline as expected.
The problem is that the onDisconnect event is also called when the user swipes out the app from the recent apps list, but I want the user to remain online since I got services which are still running in the background which should handle some events even when the app is closed (They are still running, but other users get the offline status of the closing user and then the actions to that user are blocked) .
Is there a way to prevent to onDisconnect event to be called when the user swipes out the app?
The onDisconnect handlers of your Firebase Database client are called:
when the database client actively disconnects from the Firebase server
when the server detects that the database client has disappeared (by the socket connection timing out)
Note that neither of these has anything to do with your application lifecycle, which seems to be what you are interested in.
If you want to change the database when your application is destroyed, you should probably listen to application lifecycle events to detect when the application exits.
When the user of an Android devices uses the overview display to "swipe away" an app, that's a signal that they don't want to use the app any more, and they're not interested in returning to it any time soon. Android will kill the app process. Killing the app process will close the connection that it has to your Realtime Database. Closing the connection will cause onDisconnect() to execute on the server. You have no control over this process when the user makes their decision. This is by design of the Android platform, which is to allow the user to make the final decision about what can actually run on their device. As the app developer, you are not empowered to force your own decision on the matter.
If you want to send the app a message even though it's been killed by the user, you can instead use Firebase Cloud Messaging to deliver events that the user has expressed interest in.
Related
The firestore documentation says:
Cloud Firestore caches data that your app is actively using, so the app can write, read, listen to, and query data even if the device is offline. When the device comes back online, Cloud Firestore synchronizes any local changes back to Cloud Firestore.
Now, I have tested this and, apparently, device coming back online is not all that it takes for the synchronization to happen.
I tested that by:
Turning on "Airplane mode" on my emulator
Trying to send a data to firestore*
Closing my app
Turning off "Airplane mode" on my emulator
* I used FirebaseFirestore.getInstance().collection("foo").document().set(bar)
On the end of this test, the data was not sent to firestore remote database. I needed to open my app again in order for the data to be sent.
But then, I still wasn't sure what exactly was triggering the synchronization. Is it only my app being open or does it needs to have an open socket to firestore?
I tested one last thing, which was:
Turning on "Airplane mode" on my emulator
Trying to send a data to firestore*
Closing my app
Altering my code so the app stays on the splash screen
Turning off "Airplane mode" on my emulator
Opened my app
On the end of this test, the data was not sent to firestore remote database.
But then, once again, I still wasn't sure what exactly was triggering the synchronization. It's probably the open socket, but if so, does it need to be open on a query related to my collection?
My final question is:
What exactly does trigger the synchronization?
Is it only my app being open or does it needs to have an open socket to firestore?
It depends on what "my app being open" means. If it means on foreground, that might not make too much sense since you can have jobs running on background with an open socket to Firestore.
It's probably the open socket, but if so, does it need to be open on a query related to my collection?
No, it does not. Any CRUD action on any part of your database will make the synchronization starts, whether it is by querying through a collection or fetching, saving, updating or removing a document.
I have to add the following feature in my android application.
There is a client, when the client logs into the application, clicks a button to make themselves available to the user by going online. When he clicks the button a rest call is made, and which change the DB status(status changed to online) and make him online.
Now my problem is I want to make the client unavailable(offline) if the app is getting forced close or removed by android from the background due to low memory or removed by the user from the recent app. If any of these situations occur I do a rest call to handle the situation.
In the normal scenario if he is online he can simply click the button again to make a rest call which will change his status to unavailable(offline) in DB.
NOTE: I don't want to use onPause() or onStop() method because I want the client to be online even if he navigates out of the application and comes back.
In principle you can't because there's always a case when user can get disconnected without feedback (e.g. losing signal, battery).
IMO you should look for ways to detect the timeout from the server side, similar to socket.io disconnect() outlined in this SO answer.
I'm building an Android game which requires the user to be online while playing. I'd like to detect if a game was interrupted while playing ( the internet connection was lost \ the device was turned off, etc...),and if it was - this should be considered as a loss for the user, and be written in the database. The only thing I have in mind for now is to save this loss offline, and update it when the user goes back online.
I know I can detect app connection status like this, but it doesn't help be because I can only execute offline operations after this. I also know I can listen to writing events in the database like this, but it doesn't help me, because no child in being updated after the game is interrupted.
My question is: Is there a way to write a Cloud Function which can listen to specific app connection status? If there is no such a way, what are the other options I have?
You can use a onDisconnect() handler. This is a piece of code you run while the app is connected, that sets an operation for the server to run when it detects that the app has disconnected.
A simple example of this from the documentation:
DatabaseRef presenceRef = FirebaseDatabase.getInstance().getReference("disconnectmessage");
// Write a string when this client loses connection
presenceRef.onDisconnect().setValue("I disconnected!");
But I recommend that you read the full documentation on managing presence for more examples.
With this you can have a Cloud Function trigger on the operation of the onDisconnect handler and then update the game status.
Note that it may take a few minutes before the server detects the lost connection in case of a non-clean disconnect.
My app loses connection to Firebase Realtime Database after being put in the background for a while ... In other words when I reopen my app again after being in the background for a while, Realtime Database services stop functioning until I clear the app data at the phone settings and everything back to normal ... I read about similar cases and found out that Android destroys resources (Socket connection) with Firebase after being put in the background for a specific period of time. However, there was no straightforward solution provided to restore connection after going back to the app ... so any help ?
It is expected that apps will lose their socket connections when there is no visible activity. That's the way the Firebase SDK works. Also, Android will aggressively pause background applications so they don't consume data and battery when the user isn't actively using it.
When an activity becomes started again, the Firebase SDK should attempt to restore the websocket it uses to communicate with the server. This websocket is fully managed by the SDK. You don't have to do anything to tell it to reconnect.
I have a system where one user can be logged into multiple devices. The use case is as follows:
Suppose the user A subscribes to a weather topic from device A. This user will now get regular push messages from this topic.
Now assume that the same user is logged in to a device B. This device also needs to get subscribed to the weather topic as there is no gurantee that the user will just use device A.
Same use case can be applied in reverse case of unsubscription as well as more than two devices.
What is the best possible way to solve such a problem.
The current approaches which i have thought of are:
1) Make a node under each user id inside firebase db called subscriptions. This node will have subscribed data in the form of
Topidname :boolean issubscribed
This approach has a listener attached to this location in a background service. Each device with the same user id will always listen to the same location thus solving the issue in most cases. It can't gurantee integrity due to the normal service lifecycle reasons.
A service is used because if the listener is kept only for the lifetime of the app, if the user unsubscribes from a topic on one device and the second device app is closed, the second app will keep receiving notifications as it's registration token is still related to that topic and will only unsubscribe on app restart.
Here the service has been made START_STICKY, has a listener inside onCreate and has a backup alarm set to restart the service in onDestroy method. Thus the only case where this won't work is an app force stop or the listener being in a backoff mode due to long non connectivity.
When the app launches it also syncs once with the location and subs the left out topics. Service will stop on signout and unsub from all the previous user's topics. I have kept a keepSynced on the location to ensure it stays in sync
Potential issues are -> service killed by force stop. May cause some ram usage. Sync might not be instantaneous depending on how long the connectivity was lost and how long it takes firebase to resync with the db. Resync and sub unsub all topics in each service restart.
2) This is a relatively more complicated approach:
Save the firebase token from the instance id service for each user in the app server.
On each signout, instead of calling delete iid, keep the same token but overwrite the user attached with the new logged in uid.
On each subscription, do a bulk subscribe to the topic using the Instance Id server api using all the registration tokens for one user.
Each time a user signs out call unsubscribe on all the topics one by one using the instance id api for that device's particular token. Same process for token refresh.
Do to same on unsubscription of each topic.
This case will need one synchronous call for all the topics subscriptions and unsubscriptions. It will also require the token to be constantly updated in the app server.
Please suggest any other better scenario to solve this issue.
If my scenarios have any flaws or needs modifications, please do suggest. I'm stuck and can't think of anything else at the moment.
I'm assuming that you're keeping track of each of the tokens for a user's devices. So the code on your server should be able to know, for a given user, how to send a message to all of their devices, regardless of subscriptions.
Try thinking of user topic subscriptions in the same way that you think of the data broadcasted within those topic. When topic subscriptions change for a user, that event could be broadcast to all of their devices with FCM, telling your app to read the subscriptions from your database and set subscriptions again as necessary. Your app will only wake a receive a notice to change subscriptions when the user requests it.
Then, when it comes time to publish a message to the topic, you can be sure that all the devices that checked in like this will receive the message to the topic.
To boil it down:
1. When a user's subscriptions change in your app, notify the server of that change.
2. In your server, update the user's subscriptions to your database.
3. Then, send an FCM data message to all of the user's devices to reload subscriptions.
4. When that message is received in your app, read the subscription updates out of your database and update the FCM subscriptions accordingly. Your app will have to arrange to stay alive for as long as it takes to do this.