Prevent observers from sending events out of order, add documentation, annotations

This commit is contained in:
Anthony Restaino 2016-02-02 22:06:28 -05:00
parent 84627b3fae
commit ac3f43a76f
9 changed files with 119 additions and 16 deletions

View File

@ -20,7 +20,7 @@ public class IncognitoActivity extends BrowserActivity {
public Observable<Void> updateCookiePreference() {
return Observable.create(new Action<Void>() {
@Override
public void onSubscribe(Subscriber<Void> subscriber) {
public void onSubscribe(@NonNull Subscriber<Void> subscriber) {
CookieManager cookieManager = CookieManager.getInstance();
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
CookieSyncManager.createInstance(IncognitoActivity.this);

View File

@ -20,7 +20,7 @@ public class MainActivity extends BrowserActivity {
public Observable<Void> updateCookiePreference() {
return Observable.create(new Action<Void>() {
@Override
public void onSubscribe(Subscriber<Void> subscriber) {
public void onSubscribe(@NonNull Subscriber<Void> subscriber) {
CookieManager cookieManager = CookieManager.getInstance();
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
CookieSyncManager.createInstance(MainActivity.this);

View File

@ -17,7 +17,6 @@ import java.lang.ref.WeakReference;
import java.net.HttpURLConnection;
import java.net.URL;
import acr.browser.lightning.app.BrowserApp;
import acr.browser.lightning.constant.Constants;
import acr.browser.lightning.database.HistoryItem;
import acr.browser.lightning.utils.Utils;

View File

@ -3,5 +3,14 @@ package acr.browser.lightning.react;
import android.support.annotation.NonNull;
public interface Action<T> {
/**
* Should be overridden to send the subscriber
* events such as {@link Subscriber#onNext(Object)}
* or {@link Subscriber#onComplete()}.
*
* @param subscriber the subscriber that is sent in
* when the user of the Observable
* subscribes.
*/
void onSubscribe(@NonNull Subscriber<T> subscriber);
}

View File

@ -3,6 +3,7 @@ package acr.browser.lightning.react;
import android.os.Looper;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import java.util.concurrent.Executor;
@ -18,6 +19,8 @@ import acr.browser.lightning.utils.Preconditions;
*/
public class Observable<T> {
private static final String TAG = Observable.class.getSimpleName();
@NonNull private Action<T> mAction;
@Nullable private Executor mSubscriber;
@Nullable private Executor mObserver;
@ -30,54 +33,99 @@ public class Observable<T> {
mDefault = new ThreadExecutor(looper);
}
/**
* Static creator method that creates an Observable from the
* {@link Action} that is passed in as the parameter. Action
* must not be null.
*
* @param action the Action to perform
* @param <T> the type that will be emitted to the subscriber
* @return a valid non-null Observable.
*/
@NonNull
public static <T> Observable<T> create(@NonNull Action<T> action) {
Preconditions.checkNonNull(action);
return new Observable<>(action);
}
/**
* Tells the Observable what Executor that the subscription
* work should run on.
*
* @param subscribeExecutor the Executor to run the work on.
* @return returns this so that calls can be conveniently chained.
*/
public Observable<T> subscribeOn(@NonNull Executor subscribeExecutor) {
mSubscriber = subscribeExecutor;
return this;
}
/**
* Tells the Observable what Executor the subscriber should observe
* the work on.
*
* @param observerExecutor the Executor to run to callback on.
* @return returns this so that calls can be conveniently chained.
*/
public Observable<T> observeOn(@NonNull Executor observerExecutor) {
mObserver = observerExecutor;
return this;
}
/**
* Subscribes immediately to the Observable and ignores
* all onComplete and onNext calls.
*/
public void subscribe() {
executeOnSubscriberThread(new Runnable() {
@Override
public void run() {
mAction.onSubscribe(new Subscriber<T>() {
@Override
public void onComplete() {
}
public void onComplete() {}
@Override
public void onNext(T item) {
}
public void onNext(T item) {}
});
}
});
}
/**
* Immediately subscribes to the Observable and starts
* sending events from the Observable to the {@link Subscription}.
*
* @param subscription the class that wishes to receive onNext and
* onComplete callbacks from the Observable.
*/
public void subscribe(@NonNull final Subscription<T> subscription) {
Preconditions.checkNonNull(subscription);
executeOnSubscriberThread(new Runnable() {
private boolean mOnCompleteExecuted = false;
@Override
public void run() {
mAction.onSubscribe(new Subscriber<T>() {
@Override
public void onComplete() {
if (!mOnCompleteExecuted) {
mOnCompleteExecuted = true;
executeOnObserverThread(new OnCompleteRunnable(subscription));
} else {
Log.e(TAG, "onComplete called more than once");
throw new RuntimeException("onComplete called more than once");
}
}
@Override
public void onNext(final T item) {
if (!mOnCompleteExecuted) {
executeOnObserverThread(new OnNextRunnable(subscription, item));
} else {
Log.e(TAG, "onComplete has been already called, onNext should not be called");
throw new RuntimeException("onNext should not be called after onComplete has been called");
}
}
});

View File

@ -10,11 +10,21 @@ public class Schedulers {
private static final Executor sWorker = Executors.newCachedThreadPool();
private static final Executor sMain = new ThreadExecutor(Looper.getMainLooper());
/**
* The worker thread.
*
* @return a non-null executor.
*/
@NonNull
public static Executor worker() {
return sWorker;
}
/**
* The main thread.
*
* @return a non-null executor that does work on the main thread.
*/
@NonNull
public static Executor main() {
return sMain;

View File

@ -1,7 +1,25 @@
package acr.browser.lightning.react;
public interface Subscriber<T> {
void onNext(T item);
import android.support.annotation.Nullable;
public interface Subscriber<T> {
/**
* Called when the Observer emits an
* item. It can be called multiple times.
* It cannot be called after onComplete
* has been called.
*
* @param item the item that has been emitted,
* can be null.
*/
void onNext(@Nullable T item);
/**
* This method is called when the observer is
* finished sending the subscriber events. It
* is guaranteed that no other methods will be
* called on the Subscription after this method
* has been called.
*/
void onComplete();
}

View File

@ -1,7 +1,26 @@
package acr.browser.lightning.react;
public interface Subscription<T> {
void onNext(T item);
import android.support.annotation.Nullable;
public interface Subscription<T> {
/**
* Called when the Observer emits an
* item. It can be called multiple times.
* It cannot be called after onComplete
* has been called.
*
* @param item the item that has been emitted,
* can be null.
*/
void onNext(@Nullable T item);
/**
* This method is called when the observer is
* finished sending the subscriber events. It
* is guaranteed that no other methods will be
* called on the Subscription after this method
* has been called.
*/
void onComplete();
}

View File

@ -417,7 +417,7 @@ public class LightningView {
private Observable<File> getPathObservable(final String subFolder) {
return Observable.create(new Action<File>() {
@Override
public void onSubscribe(Subscriber<File> subscriber) {
public void onSubscribe(@NonNull Subscriber<File> subscriber) {
File file = BrowserApp.get(mActivity).getDir(subFolder, 0);
subscriber.onNext(file);
subscriber.onComplete();