New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add StorIOContentResolver get().numberOfResults()! #534
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -36,19 +36,15 @@ public final class PreparedGetListOfObjects<T> extends PreparedGet<List<T>> { | |
@NonNull | ||
private final Class<T> type; | ||
|
||
@NonNull | ||
private final Query query; | ||
|
||
@Nullable | ||
private final GetResolver<T> explicitGetResolver; | ||
|
||
PreparedGetListOfObjects(@NonNull StorIOContentResolver storIOContentResolver, | ||
@NonNull Class<T> type, | ||
@NonNull Query query, | ||
@Nullable GetResolver<T> explicitGetResolver) { | ||
super(storIOContentResolver); | ||
super(storIOContentResolver, query); | ||
this.type = type; | ||
this.query = query; | ||
this.explicitGetResolver = explicitGetResolver; | ||
} | ||
|
||
|
@@ -140,7 +136,7 @@ public Observable<List<T>> createObservable() { | |
} | ||
|
||
/** | ||
* Compile-time safe part of builder for {@link PreparedGetListOfObjects}. | ||
* Builder for {@link PreparedGetListOfObjects}. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wow, hadn't saw this typo, thanks! |
||
* | ||
* @param <T> type of objects for query. | ||
*/ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,175 @@ | ||
package com.pushtorefresh.storio.contentresolver.operations.get; | ||
|
||
import android.database.Cursor; | ||
import android.support.annotation.NonNull; | ||
import android.support.annotation.Nullable; | ||
import android.support.annotation.WorkerThread; | ||
|
||
import com.pushtorefresh.storio.StorIOException; | ||
import com.pushtorefresh.storio.contentresolver.StorIOContentResolver; | ||
import com.pushtorefresh.storio.contentresolver.queries.Query; | ||
import com.pushtorefresh.storio.operations.internal.MapSomethingToExecuteAsBlocking; | ||
import com.pushtorefresh.storio.operations.internal.OnSubscribeExecuteAsBlocking; | ||
|
||
import rx.Observable; | ||
import rx.schedulers.Schedulers; | ||
|
||
import static com.pushtorefresh.storio.internal.Checks.checkNotNull; | ||
import static com.pushtorefresh.storio.internal.Environment.throwExceptionIfRxJavaIsNotAvailable; | ||
|
||
public class PreparedGetNumberOfResults extends PreparedGet<Integer> { | ||
|
||
@NonNull | ||
private final GetResolver<Integer> getResolver; | ||
|
||
PreparedGetNumberOfResults(@NonNull StorIOContentResolver storIOContentResolver, @NonNull Query query, @NonNull GetResolver<Integer> getResolver) { | ||
super(storIOContentResolver, query); | ||
this.getResolver = getResolver; | ||
} | ||
|
||
/** | ||
* Executes Get Operation immediately in current thread. | ||
* <p> | ||
* Notice: This is blocking I/O operation that should not be executed on the Main Thread, | ||
* it can cause ANR (Activity Not Responding dialog), block the UI and drop animations frames. | ||
* So please, call this method on some background thread. See {@link WorkerThread}. | ||
* | ||
* @return non-null {@link Integer} with number of results of the query. | ||
*/ | ||
@WorkerThread | ||
@NonNull | ||
@Override | ||
public Integer executeAsBlocking() { | ||
final Cursor cursor; | ||
|
||
try { | ||
cursor = getResolver.performGet(storIOContentResolver, query); | ||
try { | ||
return getResolver.mapFromCursor(cursor); | ||
} finally { | ||
cursor.close(); | ||
} | ||
} catch (Exception exception) { | ||
throw new StorIOException(exception); | ||
} | ||
} | ||
|
||
/** | ||
* Creates "Hot" {@link Observable} which will be subscribed to changes of tables from query | ||
* and will emit result each time change occurs. | ||
* <p> | ||
* First result will be emitted immediately after subscription, | ||
* other emissions will occur only if changes of tables from query will occur during lifetime of | ||
* the {@link Observable}. | ||
* <dl> | ||
* <dt><b>Scheduler:</b></dt> | ||
* <dd>Operates on {@link Schedulers#io()}.</dd> | ||
* </dl> | ||
* <p> | ||
* Please don't forget to unsubscribe from this {@link Observable} because | ||
* it's "Hot" and endless. | ||
* | ||
* @return non-null {@link Observable} which will emit non-null | ||
* number of results of the executed query and will be subscribed to changes of tables from query. | ||
*/ | ||
@NonNull | ||
@Override | ||
public Observable<Integer> createObservable() { | ||
throwExceptionIfRxJavaIsNotAvailable("createObservable()"); | ||
|
||
return storIOContentResolver | ||
.observeChangesOfUri(query.uri()) // each change triggers executeAsBlocking | ||
.map(MapSomethingToExecuteAsBlocking.newInstance(this)) | ||
.startWith(Observable.create(OnSubscribeExecuteAsBlocking.newInstance(this))) // start stream with first query result | ||
.subscribeOn(Schedulers.io()); | ||
} | ||
|
||
/** | ||
* Builder for {@link PreparedGetNumberOfResults}. | ||
*/ | ||
public static final class Builder { | ||
|
||
@NonNull | ||
private final StorIOContentResolver storIOContentResolver; | ||
|
||
Builder(@NonNull StorIOContentResolver storIOContentResolver) { | ||
this.storIOContentResolver = storIOContentResolver; | ||
} | ||
|
||
/** | ||
* Required: Specifies query which will be passed to {@link StorIOContentResolver} | ||
* to get list of objects. | ||
* | ||
* @param query non-null query. | ||
* @return builder. | ||
* @see Query | ||
*/ | ||
@NonNull | ||
public CompleteBuilder withQuery(@NonNull Query query) { | ||
checkNotNull(query, "Please specify query"); | ||
return new CompleteBuilder(storIOContentResolver, query); | ||
} | ||
} | ||
|
||
/** | ||
* Compile-time safe part of builder for {@link PreparedGetNumberOfResults}. | ||
*/ | ||
public static final class CompleteBuilder { | ||
|
||
@NonNull | ||
static final GetResolver<Integer> STANDARD_GET_RESOLVER = new DefaultGetResolver<Integer>() { | ||
@NonNull | ||
@Override | ||
public Integer mapFromCursor(@NonNull Cursor cursor) { | ||
return cursor.getCount(); | ||
} | ||
}; | ||
|
||
@NonNull | ||
private final StorIOContentResolver storIOContentResolver; | ||
|
||
@NonNull | ||
private final Query query; | ||
|
||
@Nullable | ||
private GetResolver<Integer> getResolver; | ||
|
||
CompleteBuilder(@NonNull StorIOContentResolver storIOContentResolver, @NonNull Query query) { | ||
this.storIOContentResolver = storIOContentResolver; | ||
this.query = query; | ||
} | ||
|
||
/** | ||
* Optional: Specifies resolver for Get Operation which can be used | ||
* to provide custom behavior of Get Operation. | ||
* <p> | ||
* | ||
* @param getResolver nullable resolver for Get Operation. | ||
* @return builder. | ||
*/ | ||
@NonNull | ||
public CompleteBuilder withGetResolver(@Nullable GetResolver<Integer> getResolver) { | ||
this.getResolver = getResolver; | ||
return this; | ||
} | ||
|
||
/** | ||
* Builds new instance of {@link PreparedGetNumberOfResults}. | ||
* | ||
* @return new instance of {@link PreparedGetNumberOfResults}. | ||
*/ | ||
@NonNull | ||
public PreparedGetNumberOfResults prepare() { | ||
if (getResolver == null) { | ||
getResolver = STANDARD_GET_RESOLVER; | ||
} | ||
|
||
return new PreparedGetNumberOfResults( | ||
storIOContentResolver, | ||
query, | ||
getResolver | ||
); | ||
} | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
package com.pushtorefresh.storio.contentresolver.integration; | ||
|
||
import android.content.ContentValues; | ||
import android.database.Cursor; | ||
|
||
import com.pushtorefresh.storio.contentresolver.BuildConfig; | ||
|
@@ -165,4 +166,38 @@ public void getListOfObjectsAsObservableOnlyInitialValue() { | |
changesTestSubscriber.assertNoErrors(); | ||
changesTestSubscriber.assertValues(Changes.newInstance(TestItem.CONTENT_URI)); | ||
} | ||
|
||
@Test | ||
public void getNumberOfResults() { | ||
TestSubscriber<Changes> changesTestSubscriber = new TestSubscriber<Changes>(); | ||
|
||
storIOContentResolver | ||
.observeChangesOfUri(TestItem.CONTENT_URI) | ||
.take(1) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You should take 8 changes here because you're inserting 8 items separately and ContentProvider throws 8 notifications. That was the problem of our tests flakiness :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. maybe There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure, no problem, but don't forget to override it in ContentProvider because by default it'll just call |
||
.subscribe(changesTestSubscriber); | ||
|
||
final int testItemsQuantity = 8; | ||
|
||
ContentValues[] contentValues = new ContentValues[testItemsQuantity]; | ||
for (int i = 0; i < testItemsQuantity; ++i) { | ||
TestItem testItemToInsert = TestItem.create(null, "value"); | ||
contentValues[i] = testItemToInsert.toContentValues(); | ||
} | ||
contentResolver.bulkInsert(TestItem.CONTENT_URI, contentValues); | ||
|
||
Integer numberOfResults = storIOContentResolver | ||
.get() | ||
.numberOfResults() | ||
.withQuery(Query.builder() | ||
.uri(TestItem.CONTENT_URI) | ||
.build()) | ||
.prepare() | ||
.executeAsBlocking(); | ||
|
||
assertThat(numberOfResults).isEqualTo(testItemsQuantity); | ||
|
||
changesTestSubscriber.awaitTerminalEvent(60, SECONDS); | ||
changesTestSubscriber.assertNoErrors(); | ||
changesTestSubscriber.assertValue(Changes.newInstance(TestItem.CONTENT_URI)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here you have to check that 8 changes with same Uri were received |
||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice :)