I made an Android app which receives realtime data from Firebase database. In my main Activity the user has to log in with e-mail and password and by success it opens a new Activity, which receives data.
It seems to be, that when the user leaves my app, the Firebase connection is still established. That is not good for the battery.
How is the way to manage the connection for closing and reopen the app? I think I need to use onPause() and onResume(). I found something like goOffline(), but I cannot found this method in the new Firebase.
Yes, you may use the activity lifecycle methods like onPause(), onStop() on onDestroy() to close your connection.
Also, it seems Firebase still has a goOffline() method - https://www.firebase.com/docs/android/api/#firebase_goOffline.
An alternative to using the goOffline() and goOnLine() methods is to remove ValueEventListeners and ChildEventListeners when they are no longer needed. My experience (with SDK 3.X only) is that Firebase will automatically disconnect after about 1 minute when then are no listeners registered and no other reason to be connected, such as a pending write.
I've recently added Firebase database to my App, but noticed occasionally high battery usage, and it appears to be linked to my App keeping the radio active (radio active for 1 hour 30 mins in 10 hours, but app usage approx 5 minutes).
I believe that this is linked to Firebase database as I have disabled the in-app purchase broadcast receiver which is the only other network element. I don't have any open listeners (I'm using single-value events), but some of the transactions are mutable, so it's possible one of them has failed to complete, and is regularly re-trying. Database persistence is off.
I'm currently testing the following simple addition to pause/resume (got to get some code in the answer :-) ) :
#Override
protected void onResume()
{
super.onResume();
if (FirebaseDatabase.getInstance() != null)
{
FirebaseDatabase.getInstance().goOnline();
}
}
#Override
public void onPause() {
super.onPause();
if(FirebaseDatabase.getInstance()!=null)
{
FirebaseDatabase.getInstance().goOffline();
}
}
And so far, so good. ** The main thing to note is switching activities, requires you to have this in every app that uses Firebase (I had a sign-in activity which stopped functioning as it was reading the username from the database) **
5/12/16 - update after a couple of weeks of testing. The high battery use returned yesterday. I'm testing primarily on Lollipop, and there's loads of complaints about the radio being left open for other apps. Others testing my App on Android M don't report any problems, so it's possibly a Lollipop issue. I'm going to continue to test, this time trying to remove unnecessary transactions to see if this helps... Hope this helps someone.
Related
I am very new to Cloud Firestore and have some doubts regarding the querylistener read count. As most of the answer suggested on stack overflow that, we should attach the listener in onStart() and detach in OnStop() in the Android activity. I am building an app in which I have to attach the listener on Dashboard Activity and the user will navigate back and forth between dashboard and other activity frequently.
So,
when the user navigates to another activity the listener will be detached (onStop()) and if they come back it will be attached again (onStart()). And suppose between this, nothing has changed in documents on the server, will I still be charged for all read count even though nothing has changed on the server?
Should I keep the listener attach and detach only when the user kills the app? By doing this, I will be only charged for the document which is changed while the app is in the foreground and not the all-read count if navigates between Dashboard and other activity.
we should attach the listener in onStart() and detach in OnStop() in the Android activity
If you are attaching a listener for getting real-time updates, yes, you need to remove the listener according to the life-cycle of the activity, as explained in my answer from the following post:
How to set addSnapshotListener and remove in populateViewHolder in RecyclerView Item?
However, if you simply create a get() call, followed by addOnCompleteListener(), there is no listener that needs to be removed.
And to answer your first questions. It depends. If you are using a SnapshotListener and nothing is changed on the server, you'll always read the data from the cache. So you can go back and forth, as long as you want. But also keep in mind, that if the listener is disconnected for more than 30 minutes, you’ll be charged for reads as if we had issued a brand-new query. On the other hand, if you are using get(), you'll be charged with a number of read operation that is equal to the number of documents that you get, every time you call it, even if there is nothing changed on the server.
Why?
In order to provide up-to-date data, the Firestore SDK needs to check the online version of the documents against the cached one. That’s the reason why you are charged, regardless of what exists in the cache or if something is changed or not on the server.
To answer your second question, if you are using a SnapshotListener, you should always consider removing the listener, otherwise, your app will remain synchronized until Android OS will eventually close the app. It doesn't really matter if the app is in the foreground or in the background, if the listener stays active, you'll always be charged accordingly. You can find more info in the following article:
How to drastically reduce the number of reads when no documents are changed in Firestore?
I'm considering the use of keepSynced() for some data from Firebase Realtime Database. I understand that it will automatically sync those paths. But how does that relate to Android lifecycle? If the user leaves all activities (and all normal listeners disconnect), will it stop syncing? I don't want the app to become data or battery hog.
On the other hand, I would like to update cached data when FCM notification arrives. I can launch some service which will connect to Firebase. I would like to sync all paths which are in keepSynced() and stop it when it's synced. I'm not sure how to achieve that. Create a listener to one of the paths and keep the service running for some time? After the service is finished, will it stop syncing?
firebaser here
Great question!
When there is no active activity, the operating system may close the connection to the Firebase database at any time. Our SDKs don't try to prevent that, but will reconnect when the app becomes active again.
What you're describing in your second paragraph is what we call "push to sync", where you send a push notification (typically a silent FCM data message) to trigger synchronizing of the data.
We did something like that in last year's I/O app and, while it was a bit more complex than we wanted it to be, it worked great. We explicitly managed the connection in that case, calling goOnline() and goOffline() (after 5 minutes iirc). The main sync code can be found in the IOSched github repo.
I'm considering the use of keepSynced() for some data from Firebase Realtime Database. I understand that it will automatically sync those paths. But how does that relate to Android lifecycle? If the user leaves all activities (and all normal listeners disconnect), will it stop syncing? I don't want the app to become data or battery hog.
On the other hand, I would like to update cached data when FCM notification arrives. I can launch some service which will connect to Firebase. I would like to sync all paths which are in keepSynced() and stop it when it's synced. I'm not sure how to achieve that. Create a listener to one of the paths and keep the service running for some time? After the service is finished, will it stop syncing?
firebaser here
Great question!
When there is no active activity, the operating system may close the connection to the Firebase database at any time. Our SDKs don't try to prevent that, but will reconnect when the app becomes active again.
What you're describing in your second paragraph is what we call "push to sync", where you send a push notification (typically a silent FCM data message) to trigger synchronizing of the data.
We did something like that in last year's I/O app and, while it was a bit more complex than we wanted it to be, it worked great. We explicitly managed the connection in that case, calling goOnline() and goOffline() (after 5 minutes iirc). The main sync code can be found in the IOSched github repo.
I have been using and testing my Android app thoroughly for the last 2 weeks.
I am using the Firebase Realtime Database exhaustively in my app and the most important thing is that, my app needs to work offline seamlessly.
I have a splash screen, where I sync all the data for a particular user node just while starting and proceed.
I using keepSynced(true); and setPersistenceEnabled(true); for sure.
Have noticed a serious issue, which is causing a lot of problem.
Scenario 1
I run the app for the first time with internet connection, the app works fine.
Scenario 2
I run the app for the first time without internet connection, nothing works. No callback is fired.
Scenario 3
I run the app consecutively with/without internet connection, it works fine.
Scenario 4
But, when I run the app after 1 or 2 days without internet connection, nothing works. No callback is fired.
I seems that the disk persistence is cleared automatically, that is why the data is not available offline and no callback is fired.
So, how can I control when the disk persistence is cleared? Is there a way to make sure that the persistence is never cleared? Is there a way to configure the size of the disk persistence also?
EDIT
This is what I am doing in the onCreate() of my application class.
private void initializeFirebaseDatabase() {
if (!FirebaseApp.getApps(this).isEmpty()) {
FirebaseDatabase firebaseDatabase = FirebaseDatabase.getInstance();
firebaseDatabase.setPersistenceEnabled(true);
}
}
Check this link out com.google.firebase.database.DatabaseException: Calls to setPersistenceEnabled() must be made before any other usage of FirebaseDatabase instance I guess this question is similar to the one in the link and its solved.
I had the same problem, with a Splash screen setting setPersistenceEnabled(true) there, but when I called finish() and tried to open the app again got the error.
Solved it killing the app instead of Calling finish().
I use OnBackPressed() to finish the activity and a Boolean isFinishing flag, to distinguish between sending the app to background and killing it.
And finally I kill it in OnDestroy() with:
android.os.Process.killProcess(android.os.Process.myPid());
I am using firebase for a school dashboard application. The application is built using android. It logs in via google credentials to firebase. Disk persistence is enabled on firebase. There are no other services or connections that the app uses. Firebase version set in build is 'com.firebase:firebase-client-android:2.4.0+'
I opened the app - it logs in and gets the data - then put it in background. The data usage looks like the following -
Foreground data usage stays steady at - 38.87kb
Background data usage keeps increasing every few minutes - 2.53kb, 2.94kb, 11.49kb, 11.9kb, 12.18kb, 12.59kb, 13.90kb, 67kb, 92kb. It is consuming about 1.5kb of data every minute.
I've made sure all firebase listeners are disconnected on pause. Why is the application background data usage continuously increasing over time?
The Firebase client keeps a network connection open to its servers. As part of keeping this connection, the client and server occasionally exchange keep-alive messages. These will likely use some memory, which should be (largely) reclaimed upon a garbage collection.
You might want to upgrade to the latest version of the Firebase SDK for Android btw. It's now on version 2.5.2.
I just found 2 methods on Firebase class - goOffline() and goOnline() that might help me reduce the background data usage. I am going to try them in onPause, and onResume for all the activities in the app.
What I've done to reduce data usage is create a service that mantain a reference counted state of when firebase is needed to be online and use goOnline()/goOffline() according that state:
class OnlineTracker {
void onlineNeeded(); //Increase reference count
void releaseOnlineNeeded(); //Decrease reference count
}
And when you need to do operations that requiere Firebase to be online you do:
try {
OnlineTracker.onlineNeeded();
...
Firebase operations that need online status
...
} finally {
OnlineTracker.releaseOnline()
}
That way when OnlineTracker has reference count>0 it calls goOnline() and when it returns to 0 it calls goOffline();
To prevent too much switching you can also delay when you goOffline and wait some time to see if online needed status will be needed in very small time.
See this gist for complete real code