Android firebase taking approx 30 second to load data first time [duplicate] - android

First time listener firing takes so much time (approx. 40s), subsequent loads takes lesser time (approx. 1s), how can speed up the first time load as well?
I have given,
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
on start of the activity.
DatabaseReference ref = FirebaseDatabase.getInstance().getReference("");
ref.keepSynced(true);
ref.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});

If you are using a listener, you need to know that you are trying to read data over the internet. You cannot compare this with an atempt to read a SQLite database, which is stored locally on disk. The speed of getting the data from Firebase servers, depends on the speed of your internet connection and on the ammount of data that you are trying to get. So most likely the reason for waiting so much is one of this. If the reason is the ammount of data, try to optimize your queries or try to get the data in small parts.
If we are speaking abot the first atempt to read a record, it might be slower than the subsequent ones, because it has to initiate the internet connection. I know that Firebase team is trying to improve the performance, but you can't expect 0ms when retrieving data over a network.
According to your comment, I need to tell you a few more things. There is no way to force the retrieval of the data from the cache while you're connected to the server, as you cannot to stop the retrieval of the data from the cache while you are not connected to the server.
Firebase is desinged to retrieve data from the chache when the device is permanently offline or while your application temporarily loses its network connection and you cannot change this behaviour.
Edit:
So to get the FirebaseDatabase object you need to use the following line of code once:
FirebaseDatabase firebaseDatabase = FirebaseDatabase.getInstance();
Then to get a DatabaseReference, you need to use the following line.
DatabaseReference rootRef = firebaseDatabase.getReference();
I'm sure you'll need to in your activity more then one reference. Let's say you'll use two:
DatabaseReference usersRef = rootRef.child("users");
DatabaseReference postRef = rootRef.child("post");
You can now add a listener for on each one of these references like this:
ValueEventListener eventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
//code to get the data
}
#Override
public void onCancelled(DatabaseError databaseError) {}
};
usersRef.addListenerForSingleValueEvent(eventListener);
And the code the remove the listener is as explained in my answer from this post.
Don't forget, onDestroy() is not always called.
As a conclusion, you create a single database connection, use as many references you need, add the listeners accordingly, remove then according to the life-cycle of you activity.

Related

How to check if Cloud Firestore data is cached? [duplicate]

After deleting data from my Firestore Database, it takes my Android app some time to realize that the data was deleted, and I assume that it's happening due the auto data cache. My app has nothing to do with offline usage and I'd like to disable this feature...
I have added this in my custom Application Class:
import android.app.Application;
import com.google.firebase.firestore.FirebaseFirestore;
import com.google.firebase.firestore.FirebaseFirestoreSettings;
public class ApplicationClass extends Application {
#Override
public void onCreate() {
super.onCreate();
FirebaseFirestore db=FirebaseFirestore.getInstance();
FirebaseFirestoreSettings settings = new FirebaseFirestoreSettings.Builder()
.setPersistenceEnabled(false)
.build();
db.setFirestoreSettings(settings);
}
}
The problem occurs after turning off the internet connection and than turning it back on (while the app is still running, in the background or not)- the Firestore module seems to lose connection to the server, and it makes the opposite operation than the intended one - instead of stop taking data from the cache, it takes data from the cache only.
For example, debugging this code will always show that isFromCache is true and documentSnapshot is empty (even though that on the server side - it's not empty):
usersRef.document(loggedEmail).collection("challenges_received").get().addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
#Override
public void onSuccess(QuerySnapshot documentSnapshots) {
boolean isFromCache=documentSnapshots.getMetadata().isFromCache();
if (!documentSnapshots.isEmpty()) {
}
}
});
Is this normal behavior?
Is there another way to disable the data cache in Cloud Firestore?
EDIT:
Adding: FirebaseFirestore.setLoggingEnabled(flase); (instead of the code above) in the custom Application Class gives the same result.
According to Cloud Firestore 16.0.0 SDK update, there is now a solution to this problem:
You are now able to choose if you would like to fetch your data from the server only, or from the cache only, like this (an example for server only):
DocumentReference documentReference= FirebaseFirestore.getInstance().document("example");
documentReference.get(Source.SERVER).addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
#Override
public void onSuccess(DocumentSnapshot documentSnapshot) {
//...
}
});
For cache only, just change the code above to Source.CACHE.
By default, both methods still attempt server and fall back to the cache.
I just ran a few tests in an Android application to see how this works. Because Firestore is currently still in beta release and the product might suffer changes any time, i cannot guarantee that this behaviour will still hold in the future.
db.collection("tests").document("fOpCiqmUjAzjnZimjd5c").get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
DocumentSnapshot documentSnapshot = task.getResult();
System.out.println("isFromCache: " + documentSnapshot.getMetadata().isFromCache());
}
});
Regarding the code, is the same no matter if we're getting the data from the cache or you are connected to the servers.
When I'm online it prints:
isFromCache: false
When I'm offline, it prints:
isFromCache: true
So, for the moment, there is no way to stop the retrieval of the data from the cache while you are not connected to the server, as you cannot force the retrieval of the data from the cache while you're connected to the server.
If instead I use a listener:
db.collection("tests").document("fOpCiqmUjAzjnZimjd5c").addSnapshotListener(new DocumentListenOptions().includeMetadataChanges(), new EventListener<DocumentSnapshot>() {
#Override
public void onEvent(DocumentSnapshot documentSnapshot, FirebaseFirestoreException e) {
System.out.println("listener.isFromCache: " + documentSnapshot.getMetadata().isFromCache());
}
});
I get two prints when I'm online:
listener.isFromCache: true
listener.isFromCache: false
Firestore is desinged to retrieve data from the chache when the device is permanently offline or while your application temporarily loses its network connection and for the moment you cannot change this behaviour.
As a concusion, an API that does something like this, currently doesn't exist yet.
Edit: Unlike in Firebase, where to enable the offline persistence you need use this line of code:
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
In Firestore, for Android and iOS, offline persistence is enabled by default.
Using the above line of code, means that you tell Firebase to create a local (internal) copy of your database so that your app can work even if it temporarily loses its network connection.
In Firestore we find the opposite, to disable persistence, we need to set the PersistenceEnabled option to false. This means that you tell Firestore not to create a local copy of your database on user device, which in term means that you'll not be able to query your database unless your are connected to Firebase servers. So without having a local copy of your database and if beeing disconected, an Exception will be thrown. That's why is a good practice to use the OnFailureListener.
Update (2018-06-13): As also #TalBarda mentioned in his answer this is now possible starting with the 16.0.0 SDK version update. So we can achieve this with the help of the DocumentReference.get(Source source) and Query.get(Source source) methods.
By default, get() attempts to provide up-to-date data when possible by waiting for data from the server, but it may return cached data or fail if you are offline and the server cannot be reached. This behavior can be altered via the Source parameter.
So we can now pass as an argument to the DocumentReference or to the Query the source so we can force the retrieval of data from the server only, chache only or attempt server and fall back to the cache.
So something like this is now possible:
FirebaseFirestore db = FirebaseFirestore.getInstance();
DocumentReference docIdRef = db.collection("tests").document("fOpCiqmUjAzjnZimjd5c");
docIdRef.get(Source.SERVER).addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
#Override
public void onSuccess(DocumentSnapshot documentSnapshot) {
//Get data from the documentSnapshot object
}
});
In this case, we force the data to be retrieved from the server only. If you want to force the data to be retrieved from the cache only, you should pass as an argument to the get() method, Source.CACHE. More informations here.
FirebaseFirestoreSettings settings = new FirebaseFirestoreSettings.Builder()
.setPersistenceEnabled(false)
.build();
dbEventHome.setFirestoreSettings(settings);
By setting this it is fetching from server always.
In Kotlin:
val db:FirebaseFirestore = Firebase.firestore
val settings = firestoreSettings {
isPersistenceEnabled = false
}
db.firestoreSettings = settings
// Enable Firestore logging
FirebaseFirestore.setLoggingEnabled(flase);
// Firestore
mFirestore = FirebaseFirestore.getInstance();
In general: the Firebase client tries to minimize the number of times it downloads data. But it also tries to minimize the amount of memory/disk space it uses.
The exact behavior depends on many things, such as whether the another listener has remained active on that location and whether you're using disk persistence. If you have two listeners for the same (or overlapping) data, updates will only be downloaded once. But if you remove the last listener for a location, the data for that location is removed from the (memory and/or disk) cache.
Without seeing a complete piece of code, it's hard to tell what will happen in your case.
Alternatively: you can check for yourself by enabling Firebase's logging [Firebase setLoggingEnabled:YES];
try this For FireBase DataBase
mDatabase.getReference().keepSynced(false);
FirebaseDatabase.getInstance().setPersistenceEnabled(false);
In Kotlin;
val settings = FirebaseFirestoreSettings.Builder()
with(settings){
isPersistenceEnabled = false
}
Firebase.firestore.firestoreSettings = settings.build()

Is using keepSynced() on Query instead of on DatabaseRef make any difference?

I am currently getting a single dataSnapshot from Firebase like so:
public Task<DataSnapshot> getLatestMessage(#NonNull String roomId) {
final TaskCompletionSource<DataSnapshot> source = new TaskCompletionSource<>();
DatabaseReference dbRef = mDatabase.getReference(NODE_MESSAGES).child(roomId);
dbRef.keepSynced(true);
ValueEventListener listener = new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
source.setResult(dataSnapshot);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
source.setException(databaseError.toException());
}
};
Query query = dbRef.orderByKey().limitToLast(1);
query.addListenerForSingleValueEvent(listener);
return source.getTask();
}
Notice that I called keepSynced() on dbRef object already.
Here's the sample data structure:
/root
/messages
/$roomId
/$messageId
/content
/timestamp
/etc...
I am able to get the most recent single snapshot data as expected, but I was wondering, does it make any difference if I move the keepSynced() call in the Query object instead of the DatabaseReference? i.e.
// dbRef.keepSynced(true); >> REMOVE THIS <<
ValueEventListener listener = new ValueEventListener() {...};
Query query = dbRef.orderByKey().limitToLast(1);
query.keepSynced(true); // >> ADD THIS <<
query.addListenerForSingleValueEvent(listener);
We're currently averaging 50% load (per day) on Firebase right now and with the steady inflow of users, I was wondering if it could improve anything in the app somehow, specially with the load. I even tried something as silly as this:
dbRef.keepSynced(true);
ValueEventListener listener = new ValueEventListener() {...};
Query query = dbRef.orderByKey().limitToLast(1);
query.addListenerForSingleValueEvent(listener);
dbRef.keepSynced(false);
-- enabling keepSynced() at the start to make sure that the reference is pointing to the most recent version, then disabling it after querying and adding the listener. Unfortunately, this doesn't provide the fresh data, not like when keeping it enabled.
I've already gone through the Optimization DB Performance documentation and believe that I followed the suggested practices as needed.
Putting the keepSynced() call on a limitToLast() query will not make any difference on the load on the database server. The server needs to load the exact same data and monitor it, it just only returns the last item to the client.
I recommend using keepSynced sparingly in your app. Each call to keepSynced keeps an empty listener to the reference/query you attach it to. That means that each client has an active listener to each chat room you call keepSynced on, even when the user is not looking at that room. While that may be precisely the right call for the use-cases of your app, it will limit the scalability of your app.
If you're worried about reaching peak load, you might want to consider looking into how to shard your data over multiple databases. Chat apps are typically relatively easy to shard, since each chat room is already isolated.

Firebase database still have slight downloads even after goOffline() and keepSynced(false)

My Firebase database still have slight downloads even after goOffline() and keepSynced(false).
I just want to get the data once. Then I save it to SharedPreferences. Next time user run the app it will check if the data exists in SharedPreferences, if it does then no need to get from Firebase again.
But in the Firebase dashboard I keep on seeing increased ammount of downloads (like 10KB) after some couple hours.
How do I completely turn off this Firebase? Am I missing something?
MainActivity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
---- SOME PSEUDO CODE ----
if (mySharedPreferences.has(theValue)) {
//already has the value.
//No need to get from Firebase.
}
else { //doesn't have the value, so get it from Firebase.
FirebaseDatabase.getInstance().goOnline(); //-->ONLINE
DatabaseReference myRef = FirebaseDatabase.getInstance().getReference("someNode");
myRef.child("datax").addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
mySharedPreferences.set(theValue);
FirebaseDatabase.getInstance().goOffline(); //-->OFFLINE
}
#Override
public void onCancelled(DatabaseError databaseError) {
FirebaseDatabase.getInstance().goOffline(); //-->OFFLINE
}
});
myRef.keepSynced(false); //-->NOT SYNCYED
}
}
You cannot completely turn off Firebase as long as you use are using the database. What can you see in Firebase Console is traffic that is gathered from all sources. Even if you have active listeners or only the users are accessing your app, all that traffic is recorded there. Even the usage of Firebase Console is considered usage and is present as traffic there. As Frank said, to have a more details view about your traffic you need to enable debug logging and check what happens in the logcat.

Firebase on Android - how to make event handler for data retrieval fire when I need it to?

I am new to Firebase and need some help with a query to retrieve data from a table. I am currently able to access and retrieve the data that I need from firebase, however, the timing is the problem I am having an issue with.
From everything I've seen, the firebase database requires me to add event listeners to the Query or DatabaseReference objects. I am trying to download the contents of a node called "questions" before a method to display the question contents is called, however, I cannot control the timing of the firing of the event which downloads the data, and as a result my display method is always called before the firebase event fires.
How can I execute a query when I want, and be sure it will be completed before a certain section of my code executes? I am used to traditional RDBs where you execute a query and get its results and then move forward with your logic. The need to use an event handler with firebase is what I am having a hard time with. I have even tried moving the definition of the firebase reference object and the event handler into onCreate() and moved the code that calls my display method into onStart() without any success - same problem. The data I am trying to get does not change so I only need to download it once at the beginning to have available for the display method.
Here is an image of my "questions" node which is a child of the root.
image of the child "questions" node on my firebase DB
Here is my code:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Get Firebase DB reference
firebase = FirebaseDatabase.getInstance();
fdbRef = firebase.getReference("questions");
// [START Question_event_listener]
fdbRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// Get Questions object and use the values to update the UI
objQuestions = dataSnapshot.getValue();
Log.w("Firebase:", "In Firebase ValueEventListener");
}
#Override
public void onCancelled(DatabaseError databaseError) {
// Getting Questions failed, log a message
Log.w("Firebase Error:", "onCancelled:", databaseError.toException());
Toast.makeText(ReviewActivity.this, "Failed to load question!", Toast.LENGTH_SHORT).show();
}
});
//. . . remaining onCreate logic removed for simplicity
} //end of onCreate
#Override
public void onStart() {
super.onStart();
// I moved this logic from onCreate to onStart but did not help...
// Firebase retrieve must execute before I call any of these
if (list_type == MainActivity.LIST_UNREVIEWED_DOCS)
displayNewReviewForm();
else if (list_type == MainActivity.LIST_REVIEWS)
displayCompletedReview();
else // (list_type == MainActivity.LIST_DRAFTS)
displayDraftReview();
}
Other alternatives if I can't get this resolved may be to move this retrieve logic to the prior Activity in my sequence and pass the retrieved data as an extra to this activity - but that seems really silly to have to do such a thing. I would think I should be able to get data from a DB when I need it... not when it feels like giving it to me.
I appreciate any help getting me past this issue.
Your code is downloading the snapshot data containing all the data at the first go only, and with Firebase, you cannot download data timely, you can only do it through different references.
What I would suggest you to do is, to have a DatabaseReference of q01, q02 respectively and then call data as in when required.
If your Keys "q01", "q02" are static, which they are looking at the scenario. I would suggest you to have their DatabaseReferences:
question_one = firebase.getReference("q01");
question_two = firebase.getReference("q02");
question_three = firebase.getReference("q03");
//Once you have the reference, you can call their ValueListeners respectively
question_one.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// Get Questions object and use the values to update the UI
objQuestions = dataSnapshot.getValue();
Log.w("Firebase:", "In Firebase ValueEventListener");
}
#Override
public void onCancelled(DatabaseError databaseError) {
// Getting Questions failed, log a message
Log.w("Firebase Error:", "onCancelled:", databaseError.toException());
Toast.makeText(ReviewActivity.this, "Failed to load question!", Toast.LENGTH_SHORT).show();
}
});
After looking at this a bit more, I came up with 2 possible solutions to the problem I had.
The first one I sort of mentioned already in my original question post, however it's not ideal in my opinion. It basically involves relocating the firebase retrieve logic to the prior Android Activity and passing the retrieved data to the Activity I need it in as an Extra. In my case the data is a HashMap so I would need to use the serialize versions of the methods to pass the serialized content to the desired Activity.
The best solution, is much simpler. I basically relocated the logic that I had in the onStart() function (which is calling my custom display methods) and moved it inside of the Firebase Event Listener's onDataChange() method, right after the call to dataSnapshot.getValue(). This ensures that I get the data before I call my display methods. This seems to be working well now.

Firebase addValueEventListener response time little slow?

I am working on Firebase to retreive data using addValueEventListener from Android SDK but i found sometime the response time take minimum 1 minute to get the result.
My Code :
Firebase firebase = new Firebase("https://example.firebaseio.com/");
firebase.child("XYZ").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Log.d("DataFirebase","onDataChange : "+dataSnapshot);
Toast.makeText(getApplicationContext(),"onDataChange",Toast.LENGTH_SHORT).show();
}
#Override
public void onCancelled(FirebaseError firebaseError) {
Log.d("DataFirebase","onCancelled : "+firebaseError);
Toast.makeText(getApplicationContext(),"onCancelled",Toast.LENGTH_SHORT).show();
}
});
This is my above code , please let me know , how can i get the result instantly from a key. Please suggest me some solution.
I too have a problem of slowness with firebase realtime db. Try calling, FirebaseDatabase.getInstance().setPersistenceEnabled(true); inside your Application class's "onCreate" method.
Be aware that this will save data on your device and the second call and above will be much faster but from cache and can be outdated, Read this for handling syncing data from server when needed immediately.

Categories

Resources