Firebase Persistence mode never syncs to server (Android) - android

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.

Related

Firebase offline sync => can I use condition to sync ? ANDROID

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?

I want to sync real time location data from android after every 5 seconds on server through socket in android app

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) {
}
};

Firebase setOffline() and reading offline data (android)

Following situation:
User not registered, I save data while offline (Firebase setOffline())
Cannot read the local data (populate listview etc) - the ValueEventListener and ChildEventListener dont fire
I set setOnline() on Firebase instance
Data is synced with web and displayed (listeners fire)
I set setOffline() again.
I save local data and read local data, works (listeners fire)
Question:
How to read local data stored BEFORE going online?
Scenario is: User uses the android app offline and decides later to register
Scenario 1:
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
FirebaseDatabase.getInstance().goOffline(); // <--------NOTE THIS
DatabaseReference mDatabase = FirebaseDatabase.getInstance().getReference();
mDatabase.child("users").child(App.get().getUid()).child("items").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// THIS IS NOT FIRING
}
#Override
public void onCancelled(DatabaseError databaseError) {
...
}
});
After Scenario 1 I change code to this and run:
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
FirebaseDatabase.getInstance().goOnline(); // <--------NOTE THIS
DatabaseReference mDatabase = FirebaseDatabase.getInstance().getReference();
mDatabase.child("users").child(App.get().getUid()).child("items").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// THIS IS FIRING. ALL GOOD
}
#Override
public void onCancelled(DatabaseError databaseError) {
...
}
});
After this I change code to following and it works
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
FirebaseDatabase.getInstance().goOffline(); // <--------NOTE THIS
DatabaseReference mDatabase = FirebaseDatabase.getInstance().getReference();
mDatabase.child("users").child(App.get().getUid()).child("items").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// THIS IS FIRING. ALL GOOD
}
#Override
public void onCancelled(DatabaseError databaseError) {
...
}
});
I added 3 segments (code blocks).
I execute first block - does not work
Second block - works
Third block - works
Here is a gist with the code.
Problem is that first block does not work before being online with setOnline()
Since you force the client to go offline in scenario 1 before it has a chance to synchronize any data, I indeed expect it to not fire onDataChange() in that scenario. In the 3rd fragment it will fire, because it has had a chance to synchronize data to the local cache.
But why are you explicitly trying to manage online/offline state? By doing this you're digging a hole that you may find it hard to get out of.
If you want to avoid having the user sign-in, you can start off with Anonymous Authentication and then upgrade that to a email/password or social account later.
Just keep in mind that starting offline and only enabling synchronizing later is not an ideal way of working with the Firebase Database, which is primarily an online database that continues working offline.

Android: Subscribe to Firebase Cloud Messaging(FCM) Topic

According to Firebase cloud messaging documentation, for subscribing a user to a topic I need to call
FirebaseMessaging.getInstance().subscribeToTopic("news");
In my application, I need all users to be subscribed to my cloud
messaging topic. Since return value is void, the question is how
can I understand that subscription was successful?
Is it a bad practice to call subscribeToTopic each time my
application starts?
1. How can I understand that subscription was successful?
Edit:
You could now check if subscription is successful by adding addOnSuccessListener()
FirebaseMessaging.getInstance().subscribeToTopic("news").addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Toast.makeText(getApplicationContext(),"Success",Toast.LENGTH_LONG).show();
}
});
Original:
There is nothing explicitly mentioned in the docs about a response received when the subscription is successful.
However, if you need to mandate all of your users to be subscribed to a specific topic, you should call the subscribeToTopic on your app's first install. This will most likely make sure that there is a connection to the internet (since it's probably been downloaded and installed via the Play Store) and the subscription successful.
However, if you want to make sure, you can also handle he checking via your own App Server. As mentioned in the docs:
You can take advantage of Instance ID APIs to perform basic topic management tasks from the server side. Given the registration token(s) of client app instances, you can do the following:
Find out details about a client app instance's subscriptions, including each topic name and subscribe date. See Get information about app instances.
Check through the registration tokens, if they haven't been successfully subsribed to your topic, send a notification to it where it will trigger your client app to call subscribeToTopic.
2. Is it a bad practice to call subscribeToTopic each time my application starts?
Edit: Adding it in from the comments section: Subscribing on app start should be fine.
Thank you #FrankvanPuffelen for verifying. :)
I have written this function and tested. May be helpful.
private void subscribeToMessaging(){
SharedPreferences prefs = getSharedPreferences(SETTINGS_TITLE, MODE_PRIVATE);
// Getting value from shared preferences
boolean isSubscriptionEnable = prefs.getBoolean(SETTING_NOTIFICATION, true);
// if "isSubscriptionEnable" is true then check whether its already subscribed or not
if (isSubscriptionEnable){
boolean alreadySubscribed = prefs.getBoolean(SETTING_ALREADY_SUBSCRIBED, false);
// if not already subscribed then subscribe to topic and save value to shared preferences
if (!alreadySubscribed){
FirebaseMessaging.getInstance().subscribeToTopic("global").addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Toast.makeText(getApplicationContext(),"Success",Toast.LENGTH_LONG).show();
}
});
SharedPreferences.Editor editor = getSharedPreferences(SETTINGS_TITLE, MODE_PRIVATE).edit();
editor.putBoolean(SETTING_ALREADY_SUBSCRIBED, true);
editor.apply();
Toast.makeText(this, "Subscribed", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(this, "Already subscribed", Toast.LENGTH_LONG).show();
}
}
}
Don't forget to write these lines above onCreate()
public static final String SETTINGS_TITLE = "settings";
public static final String SETTING_NOTIFICATION = "notification_state";
public static final String SETTING_ALREADY_SUBSCRIBED = "already_subscribed";

Firebase Remote Config: Can't read any values, but fetch is successful

I'm trying to have a remote config parameter using the new Remote Config feature of Firebase, and I'm having an issue.
Here's my Remote Config console:
I'm doing a fetch and update in my Application's onCreate():
final FirebaseRemoteConfig remoteConfig = FirebaseRemoteConfig.getInstance();
remoteConfig.fetch().addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
if (task.isSuccessful()) {
remoteConfig.activateFetched();
}
}
});
And here's how I'm reading it:
FirebaseRemoteConfig remoteConfig = FirebaseRemoteConfig.getInstance();
String value = remoteConfig.getString("active_subscriptions");
Value is returning null.
If I call remoteConfig.getInfo().getLastFetchStatus(), it returns LAST_FETCH_STATUS_SUCCESS, so it seems the fetch is going through successfully.
Any idea why my value is blank?
Workaround found! See below
I'm running into the "silent completion" thing - I call "fetch" but onComplete, onSuccess, or onFailure listeners never fire. I tried moving it to an activity onCreate, and still nothing happened, and therefore, the config items never get loaded from the server. I've got Developer Mode enabled, and am calling fetch with a cache value of 0.
I was able to (once) put a breakpoint on the line "public void onComplete(#NonNull Task task) {", which got hit, and then I was able to step through and the onComplete fired. I was then unable to reproduce this same result any other way, including doing the same thing (I think) a second time.
Seems like a timing or concurrency issue, but that makes little sense, given this is an asynchronous call.
Workaround
If you fetch from Activity#onResume (or, I presume, Activity#onStart), it works perfectly. Calling fetch from Activity#onCreate or Application#onCreate results in a call that seemingly never gets handled, and in fact, performance of the app degrades noticeably after the fetch begins, so I think there's a looper running or something.*
Workaround #2
If you really want this to run from Application#onCreate (which I do), this seems to work as well:
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
// Run mFirebaseRemoteConfig.fetch(timeout) here, and it works
}
}, 0);
You're likely hitting the caching in Remote Config. The way it works is that Config will cache incoming items locally, and return them. So your last (cached) fetch status was probably before the value was defined, and we get a cached blank value.
You can control the cache expiry, but if you fetch too often you risk getting throttled.
Because this is a common development problem though, there is a developer mode that lets you request more rapidly (for small groups of users):
FirebaseRemoteConfigSettings configSettings =
new FirebaseRemoteConfigSettings.Builder()
.setDeveloperModeEnabled(BuildConfig.DEBUG)
.build();
FirebaseRemoteConfig.getInstance().setConfigSettings(configSettings);
When you call fetch you can then pass a short cache expiration time
long cacheExpiration = 3600;
FirebaseRemoteConfig mFirebaseRemoteConfig = FirebaseRemoteConfig.getInstance();
if (mFirebaseRemoteConfig.getInfo().getConfigSettings().isDeveloperModeEnabled()) {
cacheExpiration = 0;
}
mFirebaseRemoteConfig.fetch(cacheExpiration)
.addOnCompleteListener(new OnCompleteListener<Void>() {
// ...
});
That's how its done in the quickstart sample if you want a full reference.
Found the problem.
After adding some logging, I found that the fetch job's onComplete() was never being called. I moved the fetch from my Application's onCreate to a fragment's, and now it works properly!
(Ian Barber, this might be something to look into or clarify, as the logs indicated that Firebase was initialized without an issue when it was in the Application, and the fetches were silent failures.)
I also encountered this problem. Turns out I hadn't seen the 'Publish' button in the the Firebase console. :facepalm:
I had the same problem and no workarounds were helpful in my case. The problem was in the testing device. I used emulator without installing Google Mobile Services, because of this the Complete event was not fired. I tried my phone with GMS and everything worked great. Good luck.
First thing in such case is check if you have the correct firebase config and you are connected to firebase .If you have android studio 2.2 got to Tools->Firebase->RemoteConfig - Connect to Firebase and see if you get a notification saying connected.Once Connected do the following in your code:
mFirebaseRemoteConfig = FirebaseRemoteConfig.getInstance();
/** NOTE: At this point, your app can use in-app default parameter values.To use in-app
* default values,skip the next section. You can deploy your app without setting
* parameter values on the server,and then later set values on the server to
* override the default behavior and appearance of your app.
*/
mFirebaseRemoteConfig.setDefaults(R.xml.remote_config_defaults);
FirebaseRemoteConfigSettings configSettings = new FirebaseRemoteConfigSettings.Builder()
.setDeveloperModeEnabled(true)
.build();
mFirebaseRemoteConfig.setConfigSettings(configSettings);
And then for fetching config do the following
long cacheExpiration = 2000; // Can increase this usually 12hrs is what is recommended
/** If in developer mode cacheExpiration is set to 0 so each fetch will retrieve values from
* the server.*/
if (mFirebaseRemoteConfig.getInfo().getConfigSettings().isDeveloperModeEnabled()) {
cacheExpiration = 0;
}
/** cacheExpirationSeconds is set to cacheExpiration here, indicating that any previously
* fetched and cached config would be considered expired because it would have been fetched
* more than cacheExpiration seconds ago. Thus the next fetch would go to the server unless
* throttling is in progress. The default expiration duration is 43200 (12 hours).
*/
mFirebaseRemoteConfig.fetch(cacheExpiration)//TODO Bring this from a config file
.addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
if (task.isSuccessful()) {
Log.d(TAG, "Firebase Remote config Fetch Succeeded");
// Once the config is successfully fetched it must be activated before newly fetched
// values are returned.
mFirebaseRemoteConfig.activateFetched();
} else {
Log.d(TAG, "Firebase Remote config Fetch failed");
}
showRemoteConfig();
}
});
Run your App and check in logs " Firebase Remote config Fetch Succeeded ". If you see the same your remote configs are loaded and activated.
I've used a similar code like #Ian Barber (copy):
FirebaseRemoteConfigSettings configSettings =
new FirebaseRemoteConfigSettings.Builder()
.setDeveloperModeEnabled(BuildConfig.DEBUG)
.build();
FirebaseRemoteConfig.getInstance().setConfigSettings(configSettings);
My problem was the "BuildConfig.DEBUG", it returns false. So it takes the value 1h in cache until it was fetched again!
I had a problem that Firebase Remote Config didn't fire OnCompleteListener with fetch(0), but with fetch() did.
Looking at FirebaseRemoteConfig.fetch() does not trigger OnCompleteListener every time, I found that the first answer was working sometimes even with fetch(0). Then I again set 3600 seconds for interval, as errors continued to appear:
override fun onPostResume() {
super.onPostResume()
// Initialize FirebaseRemoteConfig here.
...
firebaseRemoteConfig.fetch(3600).addOnCompleteListener { task ->
if (task.isSuccessful) {
firebaseRemoteConfig.activateFetched()
//calling function to check if new version is available or not
checkForUpdate(currentVersionCode, firebaseRemoteConfig.getString(VERSION_CODE_KEY))
} else
Toast.makeText(this#MainActivity, "Someting went wrong please try again",
Toast.LENGTH_SHORT).show()
}
}
Well in my case, I am able to receive control in addOnCompleteListener for fetch method but I have fetched values firebaseRemoteConfig just after I called firebaseRemoteConfig.activate(), so when I have tried to get the values from firebaseRemoteConfig it returns me previously saved values because firebaseRemoteConfig.activate() runs asynchronously and new values didn't saved before I am getting them from firebaseRemoteConfig, so I have added complete listener for activate() method also, Here:
firebaseRemoteConfig.fetch()
.addOnCompleteListener(activity, OnCompleteListener {
if (it.isSuccessful)
{
Log.d("task","success")
firebaseRemoteConfig.activate().addOnCompleteListener { // here I have added a listener
val base_url=firebaseRemoteConfig.getString("base_url")
Log.d("base url",base_url)
Toast.makeText(activity, "Base url: $base_url",Toast.LENGTH_SHORT).show()
}
}
else
{
Log.d("task","failure")
}
})
Does Android Studio's Build Variant match your intended Firebase project?
I work on a big project and the problem was buried in an unexpected place.
Long story short: the firebase application id(normally set through google-services.json) was changed through code:
FirebaseOptions.Builder builder = new FirebaseOptions.Builder();
builder.setApplicationId(applicationId);
builder.setApiKey(apiKey);
FirebaseOptions options = builder.build();
FirebaseApp.initializeApp(context, options);
The solution was to remove that code and let firebase use the info from "google-services.json".
Use fetchAndActivate instead of fetch
I was facing the same problem. After fetching, no listener get call for first time only. I try fetchAndActivate in single line and it works for me. Use below code
mFirebaseRemoteConfig.fetchAndActivate()
.addOnCompleteListener(this, new OnCompleteListener<Boolean>() {
#Override
public void onComplete(#NonNull Task<Boolean> task) {
if (task.isSuccessful()) {
boolean updated = task.getResult();
Log.d(TAG, "Config params updated: " + updated);
Toast.makeText(MainActivity.this, "Fetch and activate succeeded",
Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(MainActivity.this, "Fetch failed",
Toast.LENGTH_SHORT).show();
}
displayWelcomeMessage();
}
});
It will fetch and activate immediately. You can find this way in official documentation here

Categories

Resources