Skip to content

Commit 38c6f2e

Browse files
committed
Merge pull request #64 from lukas1994/bugfix
Fixed bug in ParseQueryAdapter throwing IndexOutOfBoundsException.
2 parents 0ecab77 + 48cd2e5 commit 38c6f2e

File tree

2 files changed

+90
-7
lines changed

2 files changed

+90
-7
lines changed

ParseLoginUI/src/androidTest/java/com/parse/ParseQueryAdapterTest.java

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,57 @@ public void onLoaded(List<Thing> objects, Exception e) {
516516
assertTrue(done.tryAcquire(10, TimeUnit.SECONDS));
517517
}
518518

519+
public void testLoadNextPageBeforeLoadObjects() throws Exception {
520+
final ParseQueryAdapter<Thing> adapter = new ParseQueryAdapter<>(activity, Thing.class);
521+
final Semaphore done = new Semaphore(0);
522+
adapter.addOnQueryLoadListener(new OnQueryLoadListener<Thing>() {
523+
@Override
524+
public void onLoading() {
525+
}
526+
527+
@Override
528+
public void onLoaded(List<Thing> objects, Exception e) {
529+
assertNull(e);
530+
assertEquals(TOTAL_THINGS, objects.size());
531+
done.release();
532+
}
533+
});
534+
535+
adapter.loadNextPage();
536+
537+
// Make sure we assert in callback is executed
538+
assertTrue(done.tryAcquire(10, TimeUnit.SECONDS));
539+
}
540+
541+
public void testIncomingQueryResultAfterClearing() throws Exception {
542+
final ParseQueryAdapter<Thing> adapter = new ParseQueryAdapter<>(activity, Thing.class);
543+
final int pageSize = 4;
544+
adapter.setObjectsPerPage(pageSize);
545+
final Semaphore done = new Semaphore(0);
546+
final Capture<Integer> timesThrough = new Capture<>(0);
547+
adapter.addOnQueryLoadListener(new OnQueryLoadListener<Thing>() {
548+
@Override
549+
public void onLoading() {}
550+
551+
@Override
552+
public void onLoaded(List<Thing> objects, Exception e) {
553+
switch (timesThrough.get()) {
554+
case 0:
555+
adapter.loadNextPage();
556+
adapter.clear();
557+
case 1:
558+
done.release();
559+
}
560+
timesThrough.set(timesThrough.get()+1);
561+
}
562+
});
563+
564+
adapter.loadObjects();
565+
566+
// Make sure we assert in callback is executed
567+
assertTrue(done.tryAcquire(10, TimeUnit.SECONDS));
568+
}
569+
519570
public void testLoadObjectsWithOverrideSetPageOnQuery() throws Exception {
520571
final int arbitraryLimit = 3;
521572
final ParseQueryAdapter<Thing> adapter =

ParseLoginUI/src/main/java/com/parse/ParseQueryAdapter.java

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121

2222
package com.parse;
2323

24-
import android.annotation.SuppressLint;
2524
import android.content.Context;
2625
import android.database.DataSetObserver;
2726
import android.graphics.drawable.Drawable;
@@ -36,9 +35,12 @@
3635
import com.parse.ParseQuery.CachePolicy;
3736

3837
import java.util.ArrayList;
38+
import java.util.Collections;
3939
import java.util.Iterator;
4040
import java.util.List;
41+
import java.util.Set;
4142
import java.util.WeakHashMap;
43+
import java.util.concurrent.ConcurrentHashMap;
4244

4345
import bolts.Capture;
4446

@@ -142,6 +144,9 @@ public static interface OnQueryLoadListener<T extends ParseObject> {
142144

143145
private List<T> objects = new ArrayList<>();
144146

147+
private Set<ParseQuery> runningQueries = Collections.newSetFromMap(new ConcurrentHashMap<ParseQuery, Boolean>());
148+
149+
145150
// Used to keep track of the pages of objects when using CACHE_THEN_NETWORK. When using this,
146151
// the data will be flattened and put into the objects list.
147152
private List<List<T>> objectPages = new ArrayList<>();
@@ -332,11 +337,19 @@ public void unregisterDataSetObserver(DataSetObserver observer) {
332337
*/
333338
public void clear() {
334339
this.objectPages.clear();
340+
cancelAllQueries();
335341
syncObjectsWithPages();
336342
this.notifyDataSetChanged();
337343
this.currentPage = 0;
338344
}
339345

346+
private void cancelAllQueries() {
347+
for (ParseQuery q : runningQueries) {
348+
q.cancel();
349+
}
350+
runningQueries.clear();
351+
}
352+
340353
/**
341354
* Clears the table and loads the first page of objects asynchronously. This method is called
342355
* automatically when this {@code Adapter} is attached to an {@code AdapterView}.
@@ -365,22 +378,36 @@ private void loadObjects(final int page, final boolean shouldClear) {
365378
// In the case of CACHE_THEN_NETWORK, two callbacks will be called. Using this flag to keep track,
366379
final Capture<Boolean> firstCallBack = new Capture<>(true);
367380

381+
runningQueries.add(query);
382+
383+
// TODO convert to Tasks and CancellationTokens
384+
// (depends on https://github.com/ParsePlatform/Parse-SDK-Android/issues/6)
368385
query.findInBackground(new FindCallback<T>() {
369-
@SuppressLint("ShowToast")
370386
@Override
371387
public void done(List<T> foundObjects, ParseException e) {
388+
if (!runningQueries.contains(query)) {
389+
return;
390+
}
391+
// In the case of CACHE_THEN_NETWORK, two callbacks will be called. We can only remove the
392+
// query after the second callback.
393+
if (query.getCachePolicy() != CachePolicy.CACHE_THEN_NETWORK ||
394+
(query.getCachePolicy() == CachePolicy.CACHE_THEN_NETWORK && !firstCallBack.get())) {
395+
runningQueries.remove(query);
396+
}
372397
if ((!Parse.isLocalDatastoreEnabled() &&
373-
query.getCachePolicy() == CachePolicy.CACHE_ONLY)
374-
&& (e != null) && e.getCode() == ParseException.CACHE_MISS) {
398+
query.getCachePolicy() == CachePolicy.CACHE_ONLY)
399+
&& (e != null) && e.getCode() == ParseException.CACHE_MISS) {
375400
// no-op on cache miss
376401
return;
377402
}
378403

379-
if ((e != null)
380-
&& ((e.getCode() == ParseException.CONNECTION_FAILED) || (e.getCode() != ParseException.CACHE_MISS))) {
404+
if ((e != null) && ((e.getCode() == ParseException.CONNECTION_FAILED) || (e.getCode() != ParseException.CACHE_MISS))) {
381405
hasNextPage = true;
382406
} else if (foundObjects != null) {
383407
if (shouldClear && firstCallBack.get()) {
408+
runningQueries.remove(query);
409+
cancelAllQueries();
410+
runningQueries.add(query); // allow 2nd callback
384411
objectPages.clear();
385412
objectPages.add(new ArrayList<T>());
386413
currentPage = page;
@@ -432,7 +459,12 @@ private void syncObjectsWithPages() {
432459
* changed.
433460
*/
434461
public void loadNextPage() {
435-
this.loadObjects(currentPage + 1, false);
462+
if (objects.size() == 0 && runningQueries.size() == 0) {
463+
loadObjects(0, false);
464+
}
465+
else {
466+
loadObjects(currentPage + 1, false);
467+
}
436468
}
437469

438470
/**

0 commit comments

Comments
 (0)