I'm currently using Firebase Firestore for an Android Project but I'm having some trouble retrieving data when the phone is on Airplane mode. Here's my code:
public void loadThings() {
FirebaseFirestore db = FirebaseFirestore.getInstance();
db.collection("medidas").whereEqualTo("id_user", mAuth.getCurrentUser().getUid()).get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
QuerySnapshot snapshot = task.getResult();
int tam = snapshot.getDocuments().size();
data = new String[tam];
StringBuilder temp;
DocumentSnapshot doc;
for (int i = 0; i < tam; i++) {
temp = new StringBuilder();
doc = snapshot.getDocuments().get(i);
temp.append("Date: ").append(doc.get("fecha")).append(";");
temp.append("Min: ").append(doc.get("min")).append(";");
temp.append("Max: ").append(doc.get("max")).append(";");
temp.append("Avg: ").append(doc.get("avg")).append(";");
data[i] = temp.toString();
}
if(tam==0)
{
noMeasures();
}
}
else
{
data=null;
}
mLoadingIndicator.setVisibility(View.INVISIBLE);
mMeasuresAdapter.setMeasuresData(data);
if (null == data) {
showErrorMessage();
} else {
showMeasuresDataView();
}
}
});
}
The specific problem is that sometimes it takes to long to show the data (more than 10 seconds) while some others it shows the data inmediatly. Since the phone is on airplane mode is obvious that the data I'm retrieving comes from the cache. However, I don't understand why it is taking so long sometimes. Is there some way to tell firestore explicitly to bring data from the cache instead of trying to fetch it from the server? Thanks in advance
Is there some way to tell firestore explicitly to bring data from the cache instead of trying to fetch it from the server?
Yes it is, starting with the 16.0.0 SDK version update, you 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 you can 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 in code might look like this:
FirebaseFirestore db = FirebaseFirestore.getInstance();
DocumentReference docIdRef = db.collection("yourCollection").document("yourDocument");
docIdRef.get(Source.CACHE).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 CACHE only. If you want to force the data to be retrieved from the SERVER only, you should pass as an argument to the get() method, Source.SERVER. More informations here.
Related
Please read carefully what I want. Here is my Firestore database structure:
Source Image
I want to start a query to find user-entered refer code is available / not in the Firestore database. All available refer codes of my users are stored in [collection]users--->[uid/Document Id]--->[Field]myRefer.
In my app user can create an account with a referral code I want to check if entered refer code is available in the database, I want to show the document id where is available the referral code. I want to show the document id in a text view.
Please help me with the Java language in Android Studio.
I want to check if entered refer code is available in the database
To be able to check if for example, #e0f2f67 exists in the "users" collection or not, please use the following lines of code:
String myRefer = "#e0f2f67";
FirebaseFirestore db = FirebaseFirestore.getInstance();
Query queryByMyRefer = db.collection("users").whereEqual("myRefer", myRefer);
queryByMyRefer.get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
for (QueryDocumentSnapshot document : task.getResult()) {
if (document.exists()) {
String name = document.getString("name");
Log.d(TAG, name + " already exists");
} else {
Log.d(TAG, name + " does not exists");
}
}
} else {
Log.d(TAG, task.getException().getMessage()); //Never ignore potential errors!
}
}
});
In which myRefer is defined as in the screenshot, so we can make the comparison.
I believe my security rules and code is correct, but I am getting a permission error in Android studio when I try to retrieve from the user document...
This is the Get User Profile method in my Android java code:
public void getUserProfile(){
db.collection("users")
.get()
.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull com.google.android.gms.tasks.Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
for (QueryDocumentSnapshot document : task.getResult()) {
String displayName = document.getString("DisplayName");
String userEmail = user.getEmail();
}
} else {
Log.w("DEBUG_FirestoreActivity", "Error getting documents.", task.getException());
}
}
});
}
... and this is how my security rules are set up:
match /users/{userId} {
allow read, write: if request.auth != null && request.auth.uid == userId;}
Here is the output of the error I'm receiving in android studio:
com.google.firebase.firestore.FirebaseFirestoreException: PERMISSION_DENIED: Missing or insufficient permissions.
What's strange is that everything should work, and I have tested it with the Rules Playground.
Your security rules allow each user to read their own document. But your code is trying to read the documents of all users, and is thus rejected.
Note that security rules are not filters on their own. If you want to only read the current user's document, you need to do so from the code: the security rules won't do this for you.
For example:
db.collection("users")
.doc(FirebaseAuth.getInstance().getCurrentUser().getUid())
.get()
Let me explain my situation. I have an Activity in Android Studio where I fill some stuff like; names, descriptions,... and an image. The data goes to the Firebase Realtime Database with the image reference from the Firebase Storage as well like this:
HashMap boulderHash = new HashMap();
boulderHash.put("boulderkey", latlong);
boulderHash.put("userid", currentUserID);
boulderHash.put("username", dataSnapshot.child("username").getValue().toString());
boulderHash.put("profileimage", downloadURL2);
...
final HashMap boulderImageHash = new HashMap();
boulderImageHash.put("images", downloadURL1);
boulderImageHash.put("profilestorage", storage);
boulderImageHash.put("counter", counter);
boulderImageHash.put("userid", currentUserID);
BoulderRef.child(latlong).updateChildren(boulderHash).addOnCompleteListener(new OnCompleteListener() {
#Override
public void onComplete(#NonNull final Task task) {
if (task.isSuccessful()) {
DatabaseReference key = BoulderRef.child(finalLatlong).child("BoulderImages").push();
imageKey = key.getKey();
BoulderRef.child(finalLatlong).child("BoulderImages").child(imageKey).updateChildren(boulderImageHash
All this happens when I click a button and if I have no internet connection I use the FirebaseDatabase.getInstance().setPersistenceEnabled(true); so the operation is waiting for connection to upload this. Everything is fine with this 'cause if I don't close the app eventually my phone gets the connection back and it uploads the data.
The problem comes when I close the app from the background, is there any option to save this data or the operation into localstorage or cache and automatically send this like it does without closing the app?
I have a system which, pulls data from my server and stores it to a mobile SQL database via android studio. It works but it is painful slow like 30mins. I have around 86000 records in my database and want to pull all of them out of the server. What is the best way to do this?
Presently I get the count from the server and then query the server database until i find each ID and then send that result back to my mobile app.
app.post("/get_data", function(req, res)
{
var Id_request = req.body.Id_request;//The requested ID
var query = {Val_String : Id_request};//Query value
//console.log(query);
//Data.find({}, function(err, result) {//Finds all data
Data.findOne(query, function(err, result) {//Finds all data
if (err) {
//res.status(400).send(err);
console.log("Sending error");
res.status(200).send();
} else {
return res.json(result);
}
});
});
I use a recersive function in my pull request for each ID
private void call_method()
{
HashMap<String, String> map = new HashMap<>();
map.put("Id_request", Integer.toString(data_pointer));//The ID value
Call<Fetch_result> call = retrofitInterface.executeGet_data(map);//Run the post
call.enqueue(new Callback<Fetch_result>() {
//call.enqueue(new Callback<Fetch_result>() {
#Override
public void onResponse(Call<Fetch_result> call, Response<Fetch_result> response) {
if (response.code() == 200)//Successful login
{
D1= response.body().getT1_String();
D2= response.body().getT2_String();
data_pointer = data_pointer + 1;
boolean result = BLE_DB.addData_Downloaded(D1,D2);//Add data
if(data_pointer<= Total_entries) {//Call method again
call_method();//Recursive here
}else if (data_pointer > Total_entries){
Utils.toast(getApplicationContext(),"All data received");
}
} else if (response.code() == 404) {
Utils.toast(getApplicationContext(), "Get data fail");
}
}
#Override
public void onFailure(Call<Fetch_result> call, Throwable t) {
Utils.toast(getApplicationContext(), "Get data error");
}
});
}
How Can I speed this up or do it differently to speed it up?
Try to fetch as much data as possible at once ( limit the amount of queries you do ). It is hard to tell you how since I don't know your monogDB collection.
Try to do this with as little requests as possible. If you can return all the fetched data at once, this will save you some time.
JSON may be very slow when doing it on 86000 documents
Consider caching the data for future users
Right now i suspect what is limiting you is the fact that you are doing 86000 queries to the db... If you can get the entire mongoDB collection it may be a bit faster( look at notes )
Notes:
https://docs.mongodb.com/manual/reference/method/db.collection.find/#db-collection-find ( omit the query parameter will result in fetching the entire collection )
I have an Android application that uses Firebase as backend. Now that Firebase has support for local persistence I decided to get rid of my own local storage solution and use the built-in instead.
Everything is working, except for one thing. Making changes while being offline, closing the app, connect, and start the app.
The code that I use to update my data on the server is something like this:
public void saveDataToServer(String id, Boolean isHandled) {
Firebase firebase = new Firebase("https://example.firebaseio.com/item_data/items/1234/data").child(id);
if(firebase == null)
return;
firebase.authWithCustomToken(mToken, mAuthResultHandler);
Map<String, Object> children = new HashMap<>();
children.put("updated_at", FireBaseHelper.getFormattedTimestamp());
children.put("is_handled", isHandled);
firebase.updateChildren(children);
}
mMainFirebaseInstance is some Firebase object that is on the root level of where this data is saved. And this all runs in a Service that is connected to my Activities/Fragments
Sidenote:
I get the authentication token mToken from some REST API that someone else made for me to use.
When I am connected, have the app connected and make changes: everything works
When I am not connected, open the app, make changes, close the app, open the app and connect: everything works
When I am not connected, open the app, make changes, close the app, connect and open the app:
The following error is logged:
06-22 17:51:52.343 28073-28395/? W/RepoOperation﹕ Persisted write at /item_data/items/7454/data/7454141033945571998119 failed: FirebaseError: Invalid token in path
I've searched in Firebase's documentation and can't figure out where the problem is. I would say that this has something to do with the authentication, but I don't know anymore where to look.
So my question is: What is the problem here? What am I doing wrong?
EDIT:
The FireBaseHelper looks like this:
class FireBaseHelper {
public static Firebase getItemsBase(String itemId) {
Firebase fb = new Firebase(Constants.FIREBASE_URL + "item_data/items/" + itemId + "/data");
return fb;
}
public static String getFormattedTimestamp() {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US);
return simpleDateFormat.format(Calendar.getInstance().getTime());
}
}
It can create the main Firebase instance and return a timestamp in a specific format. Constants.FIREBASE_URL is just https://example.firebaseio.com/
EDIT2:
mMainFirebaseInstance = FireBaseHelper.getItemsBase("1234");
which would be replaceable by
mMainFirebaseInstance = new Firebase("https://example.firebaseio.com/item_data/items/1234/data");
A possible timestamp is:
2015-06-22 23:12:24
The id that is used in the saveDataToServer is retrieved from a snapshot that is given to me in a ValueEventListener. For example:
ValueEventListener valueEventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
HashMap data = dataSnapshot.getValue(HashMap.class);
Set keySet = data.keySet();
String id = keySet.get(0);
}
...
}
EDIT3:
Android: 5.0
Firebase: 2.3.0+
Java: 7
This turned out to be a bug in the Firebase Android SDK. It has been fixed in version 2.3.1 that was just released. See https://www.firebase.com/docs/android/changelog.html
I can reproduce the behavior you're seeing with this Activity.onCreate:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Firebase.setAndroidContext(this);
Firebase.getDefaultConfig().setPersistenceEnabled(true);
Firebase.getDefaultConfig().setLogLevel(Logger.Level.DEBUG);
Firebase ref = new Firebase("https://stackoverflow.firebaseio.com/30987733");
Map<String, Object> children = new HashMap<>();
children.put("updated_at", new Date().toString());
children.put("is_handled", true);
ref.updateChildren(children);
}
The steps to reproduce that go with the above code:
Go into airplane mode
Start the app (so that it calls updateChildren
Kill the app
Go online
Start the app
In my tests, the second update does get written to Firebase. But there is a warning written in the Android logs:
W/RepoOperation﹕ Persisted write at /30987733 failed: FirebaseError: Invalid token in path