Issue
I have a database in Cloud Firestore where each document has a particular key "last update" with value, a String, representing a date in the form YYYY-MM-DD. Each time a document is updated, the value of "last update" is set as the date of the update.
Now, I want my activity to have a method that checks documents for their last update. As the documents contain fairly big lists of objects, this update check takes a few seconds. So I decided to defer it to an AsyncTask. The doInBackground method of the AsyncTask should create a DocumentReference, noteRef, for the document and read its "last update" with noteRef.get(), equipped with onSuccess- and onFailure listeners, into a String, which is then returned by the method.
In order to test this, I have created a toy activity, MyTestActivity, which calls the above AsyncTask with String arguments "myCollection" and "myDocument" and displays the value of this document's last update in a TextView. Now, instead of showing the actual value, "2019-10-03", the TextView displays the value, "1970-01-01", which is the one used in doInBackground to initialize the String variable which is returned. It's as if doInBackground doesn't bother to wait until the document has been read. The code is as follows.
public class MyTestActivity extends AppCompatActivity{
private Button button;
private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my_test);
button = findViewById(R.id.update_button);
textView = findViewById(R.id.update_text_view);
}
public void buttonClicked(View view) throws ExecutionException, InterruptedException {
UpdateTask task = new UpdateTask(this, "myCollection", "myDocument");
String date = task.execute().get();
textView.setText("Last update on "+date);
}
private static class UpdateTask extends AsyncTask<Integer, Integer, String> {
private WeakReference<MyTestActivity> activityWeakReference;
String collection;
String document;
String lastUpdate;
UpdateTask(MyTestActivity activity, String collection, String document) {
activityWeakReference = new WeakReference<MyTestActivity>(activity);
this.collection = collection;
this.document = document;
lastUpdate = new String();
}
@Override
protected void onPreExecute() {
super.onPreExecute();
MyTestActivity activity = activityWeakReference.get();
if (activity == null || activity.isFinishing()) {
return;
}
}
@Override
protected String doInBackground(Integer... params) {
FirebaseFirestore db = FirebaseFirestore.getInstance();
DocumentReference noteRef = db.collection(collection).document(document);
lastUpdate = "1970-01-01";
noteRef.get()
.addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
@Override
public void onSuccess(DocumentSnapshot documentSnapshot) {
if (documentSnapshot.exists()) {
Map<String, Object> map = documentSnapshot.getData();
lastUpdate = (String)map.get("last update");
activityWeakReference.get().textView.setText(lastUpdate);
} else {
lastUpdate = "Document doesn't exist";
}
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
lastUpdate = "Listener failed.";
}
});
return lastUpdate;
}
}
}
Can anyone explain what is going on here?
Solution
I have a database in Firebase Firestore where each document has a particular key "last update" with value, a String, representing a date in the form YYYY-MM-DD.
That's uncommon to store the date as a String, instead you should store it as a:
FieldValue.serverTimestamp()
As explained in my answer from the following post:
So I decided to defer it to an AsyncTask.
The Cloud Firestore database client, already runs all network operations in a background thread. This means that all operations take place without blocking your main thread. Adding it in an AsyncTask
does not give any any benefits at all.
Now, instead of showing the actual value, "2019-10-03", the TextView displays the value, "1970-01-01", which is the one used in doInBackground
This is happening because you are trying to return a message synchronously from a method that is asynchronous. That's not a good idea. You should handle the APIs asynchronously as intended.
A quick solve for this problem would be to use value of lastUpdate
only inside the onSuccess()
method, otherwise I recommend you see the last part of my anwser from this post in which I have explained how it can be done using a custom callback. You can also take a look at this video for a better understanding.
Answered By - Alex Mamo
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.