I have Task, that fetches results from server. While storing that data to database in bolt's .continueWith It got interrupted with something "completedImmediately" (found while debugging cycle).
this.someMethod.getStatements()
.continueWith(new Continuation<List<Statement>, Object>() {
#Override public Object then(Task<List<Statement>> task) {
for (Statement statement : task.getResult()) {
saveStatement(statement);
}
return null;
}
});
Found solution, there was a problem in inner method, which was storing data to database.
Related
I'm trying to retrieve document references from Firestore in a for loop (without iterating through the subset). I need to wait for the loop to finish, wait for data to be received and on success, submit this data to the Firestore. Currently, my method does not wait for data to be received what so ever since it is async.
It would probably be a good idea to create a method that returns a Task and then wait for a result. Suggestions?
ArrayList<String> documentPath = new ArrayList<>();
private void getDocumentRef() {
try {
for (String path : documentPath) {
db.document(path).get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful() && task.getResult() != null) {
if (task.getResult().exists()) {
references.add(task.getResult().getReference());
}
}
}
});
}
} catch (Exception e) {
}
}
If I understood your issue correctly, following method should work:
private void getDocumentRef(List<String> documentPaths) {
List<Task<DocumentSnapshot>> tasks = new ArrayList<>();
for (String path : documentPaths) {
tasks.add(db.document(path).get());
}
Task<List<DocumentSnapshot>> finalTask = Tasks.whenAllSuccess(tasks);
finalTask.addOnSuccessListener(new OnSuccessListener<List<DocumentSnapshot>>() {
#Override
public void onSuccess(List<DocumentSnapshot> documentSnapshots) {
/* This list contains all the retrieved document snapshots. Now iterate
through this list to get the document references.*/
for (DocumentSnapshot snapshot : documentSnapshots) {
references.add(snapshot.getReference());
}
/* Here u can do whatever u want with the list named references
because now it has references of all required documents. */
}
});
}
Here, we iterate through the supplied list of paths, create a separate Task for retrieving the document at that path and add this task to a list of Task<DocumentSnapshot>. Then, we supply this list to Tasks.whenAllSuccess() and create a new Task named finalTask. We then attach an OnSuccessListener to finalTask whose onSuccess() method is called when all the supplied tasks get completed. What we get in onSuccess() is a list of DocumentSnapshot of each document. We can now go through this list and get the DocumentReference.
Hope it helps!
For continuing after completing your requests for the selected subset, you would need to keep track of how many queries you're making and how many have completed.
Create a member variable to track the queries and within your OnCompleteListener:
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
mCompleted++; // Update your member variable
if(task.isSuccessful(){
// Do something with your returned data
}else{
// The task failed
}
// Check if the last query has completed
if(mCompleted == numOfQueries){
mCompleted = 0; // Reset the completed queries if you might run this process again
// All of your queries have returned and you can now do something with the complete data set
}
}
I have a Map of references which I read from Firestore. these refs lead me to documents that I'm willing to use their data to create an instance of my class 'Contact'.
In order to do that I've created a list of tasks which every task of it uses its ref to read from Firestore and retrieve the needed data.
Once it's all done I use Tasks.whenAll(tasks).addOnSuccessListener() willing to retrive my new array of Contacts.
On this method, 'contacts' is empty and 'data' is full of document references.
I expected Tasks.whenAll(tasks) to being called only when all this reading using the refs has completed, however it's being called immediately, therefore - nothing happens.
private void createContactArray(final ArrayList<Contact> contacts, final Map<String, DocumentReference> data) {
List<Task<DocumentSnapshot>> tasks = new ArrayList<>();
for (final Map.Entry<String, DocumentReference> entry : data.entrySet()) {
tasks.add(db.document(entry.getValue().getPath()).get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()) {
DocumentSnapshot document = task.getResult();
if (document.exists()) {
Map<String,String> contactDetails = (Map<String, String>) document.getData().get(entry.getKey());
Contact contact = createContact(contactDetails);
if(contact != null){ contacts.add(contact);}
} else {
Log.d(ACTION_FETCH_CONTACT_LIST,"There was ref problem with " + entry.getKey());
}
}else {
Log.d(ACTION_FETCH_CONTACT_LIST, "get failed with ", task.getException());
}
}
}));
}
Tasks.whenAll(tasks).addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
sendBroadcastActionContactList(contacts);
}
});
I would like Tasks.whenAll to be called once its all finished and not right away. I wish to have a proper explanation for the issue and a decent code that should do the job instead of mine.
I really appreciate your help!
You are using the APIs incorrectly. You should be collecting tasks returned by get() into an array, instead of immediately adding a callback to each one. Pass that list of tasks to Tasks.whenAll(). Then, in the callback for the task returned by Tasks.whenAll, you can examine each DocumentSnapshot results.
I want to store locally the data I am reading from the cloud.
To achieve this I am using a global variable(quizzes) to hold all the data.
For this, when I am building my Quiz objects, I need to make sure that before I am creating them, the relevant data has been already downloaded from the cloud. Since when reading data from firestore, it happens asynchronously.
I didn't enforced this (waiting for the read to finish) before -I just used onSuccess listeners, and I encountered synchronization problem because the reading tasks weren't finished before I created my Quiz objects with the data from the cloud.
I fixed this with a very primitive way of "busy waiting" until the read from the cloud is complete. I know this is very stupid, a very bad practice, and making the application to be super slow, and I am sure there is a better way to fix this.
private void downloadQuizzesFromCloud(){
String user_id = FirebaseAuth.getInstance().getCurrentUser().getUid();
final FirebaseFirestore db = FirebaseFirestore.getInstance();
CollectionReference quizzesRefrence = db.collection("users").document(user_id).collection("quizzes");
Task<QuerySnapshot> task = quizzesRefrence.get();
while(task.isComplete() == false){
System.out.println("busy wait");
}
for (QueryDocumentSnapshot document : task.getResult()) {
Quiz quizDownloaded = getQuizFromCloud(document.getId());
quizzes.add(quizDownloaded);
}
}
I looked online in the documentation of firestore and firebase and didn't find anything that I could use. (tried for example to use the "wait" method) but that didn't help.
What else can I do to solve this synchronization problem?
I didn't understand if you tried this solution, but I think this is the better and the easier: add an onCompleteListener to the Task object returned from the get() method, the if the task is succesfull, you can do all your stuff, like this:
private void downloadQuizzesFromCloud(){
String user_id = FirebaseAuth.getInstance().getCurrentUser().getUid();
final FirebaseFirestore db = FirebaseFirestore.getInstance();
CollectionReference quizzesRefrence = db.collection("users").document(user_id).collection("quizzes");
quizzesRefrence.get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccesful()) {
for (QueryDocumentSnapshot document : task.getResult()) {
Quiz quizDownloaded = getQuizFromCloud(document.getId());
quizzes.add(quizDownloaded);
}
}
});
}
}
In this way, you'll do all you have to do (here the for loop) as soon as the data is downloaded
You can make your own callback. For this, make an interface
public interface FireStoreResults {
public void onResultGet();
}
now send this call back when you get results
public void readData(final FireStoreResults){
db.collection("users").document(user_id).collection("quizzes")
.get().addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
#Override
public void onSuccess(QuerySnapshot queryDocumentSnapshots) {
for (QueryDocumentSnapshot document : task.getResult()) {
Quiz quizDownloaded = getQuizFromCloud(document.getId());
quizzes.add(quizDownloaded);
}
results.onResultGet();
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
results.onResultGet();
}
});
}
Now in your activity or fragment
new YourResultGetClass().readData(new FireStoreResults(){
#Override
public void onResultGet() {
new YourResultGetClass().getQuizzes(); //this is your list of quizzes
//do whatever you want with it
}
Hope this makes sense!
I have a list of product that have an image as uri (field) that I need to save on the internal storage. The issue that I didn't find how to do it with the current code :
I got data from WS using retrofit
RestClient.getClient().getProducts()
When I got data from WS I need to save them into Realm DB
After saving images on the Realm DB I try to save images on the internal storage but I can't because the onNext is called inside the mainThread.
public void getProductsHandler(final Context context) {
MyApplication application = MyApplication.get(context);
rx.Observable<ProductResponse> faq = RestClient.getClient()
.getProducts()
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(application.defaultSubscribeScheduler());
faq.subscribe(new Observer<ProductResponse>() {
#Override public void onCompleted() {
}
#Override public void onError(Throwable e) {
}
#Override public void onNext(ProductResponse response) {
RealmService realmService = new RealmService(realm);
realmService.setProducts(response.getResults());
((MainActivity) context).saveBannersIntoInternal(response.getResults());
}
});
}
Any good solution ?
Use flatMap to chain two operations, web service call and saving to DB, together.
RestClient.getClient().getProducts()
.flatMap(response -> saveToDB())
.subscribeOn(Schedulers.io());
The last subscribeOn(Schedulers.io()) call will let both Retrofit call and flatMap run on IO Scheduler.
I am using Bolts framework in my Android project. I read the documents several times, but I am still confused about the difference between continueWith() and onSuccess(), because the callback method and the return value are all the same. For example,
Task task = ParseGeoPoint.getCurrentLocationInBackground(10*1000);
And what's the difference between these two methods?
task.onSuccess(new Continuation<ParseGeoPoint, Object>() {
#Override
public Object then(Task<ParseGeoPoint> task) throws Exception {
Log.d(TAG, "task done");
return null;
}
});
task.continueWith(new Continuation<ParseGeoPoint, Object>() {
#Override
public Object then(Task<ParseGeoPoint> task) throws Exception {
Log.d(TAG, "task done");
return null;
}
});
Basically, onSuccess() is just called, as its name indicates, when the call completes without errors. On the other hand, continueWith() is called always, even in case of failure. Therefore, use onSuccess() when you are interested only in retrieve the result on successful requests, and use continueWith() if you want also be able to handle failed requests.