├── sample
├── .gitignore
├── src
│ ├── main
│ │ ├── ic_launcher-web.png
│ │ ├── res
│ │ │ ├── drawable-hdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── drawable-mdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── drawable-xhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── drawable-xxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── values
│ │ │ │ ├── styles.xml
│ │ │ │ ├── dimens.xml
│ │ │ │ └── strings.xml
│ │ │ ├── layout
│ │ │ │ ├── activity_viewpager.xml
│ │ │ │ └── activity_main.xml
│ │ │ ├── values-w820dp
│ │ │ │ └── dimens.xml
│ │ │ └── menu
│ │ │ │ └── main.xml
│ │ ├── java
│ │ │ └── me
│ │ │ │ └── tatarka
│ │ │ │ └── rxloader
│ │ │ │ └── sample
│ │ │ │ ├── MainActivityWithFragment.java
│ │ │ │ ├── test
│ │ │ │ ├── TestableRxLoaderActivity.java
│ │ │ │ ├── TestableRxLoaderActivityWithFragment.java
│ │ │ │ ├── RxLoaderActivity.java
│ │ │ │ ├── RxLoaderFragment.java
│ │ │ │ ├── RxLoaderFragmentActivity.java
│ │ │ │ ├── RxLoaderSupportFragment.java
│ │ │ │ ├── RxLoaderActivityWithFragment.java
│ │ │ │ └── RxLoaderActivityWithSupportFragment.java
│ │ │ │ ├── MainActivityWithViewPager.java
│ │ │ │ ├── SampleObservables.java
│ │ │ │ ├── MainActivity.java
│ │ │ │ └── MainFragment.java
│ │ └── AndroidManifest.xml
│ └── androidTest
│ │ └── java
│ │ └── me
│ │ └── tatarka
│ │ └── rxloader
│ │ └── sample
│ │ └── test
│ │ ├── RxLoaderActivityTest.java
│ │ ├── RxLoaderFragmentActivityTest.java
│ │ ├── RxLoaderActivityWithFragmentTest.java
│ │ ├── RxLoaderActivityWithSupportFragmentTest.java
│ │ ├── BaseRxLoaderActivityWithFragmentTest.java
│ │ └── BaseRxLoaderActivityTest.java
├── proguard-rules.txt
└── build.gradle
├── rxloader
├── .gitignore
├── src
│ └── main
│ │ └── java
│ │ └── me
│ │ └── tatarka
│ │ └── rxloader
│ │ ├── RxLoaderBackend.java
│ │ ├── ParcelableSaveCallback.java
│ │ ├── SaveCallback.java
│ │ ├── RxLoaderObserver.java
│ │ ├── RxLoaderManagerCompat.java
│ │ ├── RxLoaderBackendFragmentCompat.java
│ │ ├── RxLoaderBackendFragment.java
│ │ ├── RxLoader.java
│ │ ├── RxLoader1.java
│ │ ├── RxLoader2.java
│ │ ├── BaseRxLoader.java
│ │ ├── CachingWeakRefSubscriber.java
│ │ ├── RxLoaderBackendFragmentHelper.java
│ │ ├── RxLoaderBackendNestedFragment.java
│ │ ├── RxLoaderBackendNestedFragmentCompat.java
│ │ └── RxLoaderManager.java
└── build.gradle
├── settings.gradle
├── .gitignore
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew.bat
├── gradlew
├── README.md
└── LICENSE
/sample/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/rxloader/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':rxloader', ':sample'
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .gradle
2 | /local.properties
3 | .idea
4 | *.iml
5 | build/
6 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evant/rxloader/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/sample/src/main/ic_launcher-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evant/rxloader/HEAD/sample/src/main/ic_launcher-web.png
--------------------------------------------------------------------------------
/sample/src/main/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evant/rxloader/HEAD/sample/src/main/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evant/rxloader/HEAD/sample/src/main/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evant/rxloader/HEAD/sample/src/main/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/drawable-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evant/rxloader/HEAD/sample/src/main/res/drawable-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/sample/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
7 |
--------------------------------------------------------------------------------
/sample/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | My Module
5 | Hello world!
6 | Settings
7 |
8 |
9 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Jan 14 18:45:12 EST 2015
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
7 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/activity_viewpager.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
--------------------------------------------------------------------------------
/sample/src/androidTest/java/me/tatarka/rxloader/sample/test/RxLoaderActivityTest.java:
--------------------------------------------------------------------------------
1 | package me.tatarka.rxloader.sample.test;
2 |
3 | /**
4 | * Created by evan on 9/20/14.
5 | */
6 | public class RxLoaderActivityTest extends BaseRxLoaderActivityTest {
7 | public RxLoaderActivityTest() {
8 | super(RxLoaderActivity.class);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/sample/src/androidTest/java/me/tatarka/rxloader/sample/test/RxLoaderFragmentActivityTest.java:
--------------------------------------------------------------------------------
1 | package me.tatarka.rxloader.sample.test;
2 |
3 | /**
4 | * Created by evan on 9/20/14.
5 | */
6 | public class RxLoaderFragmentActivityTest extends BaseRxLoaderActivityTest {
7 | public RxLoaderFragmentActivityTest() {
8 | super(RxLoaderFragmentActivity.class);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/sample/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/sample/src/androidTest/java/me/tatarka/rxloader/sample/test/RxLoaderActivityWithFragmentTest.java:
--------------------------------------------------------------------------------
1 | package me.tatarka.rxloader.sample.test;
2 |
3 | /**
4 | * Created by evan on 9/20/14.
5 | */
6 | public class RxLoaderActivityWithFragmentTest extends BaseRxLoaderActivityWithFragmentTest {
7 | public RxLoaderActivityWithFragmentTest() {
8 | super(RxLoaderActivityWithFragment.class);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/sample/src/androidTest/java/me/tatarka/rxloader/sample/test/RxLoaderActivityWithSupportFragmentTest.java:
--------------------------------------------------------------------------------
1 | package me.tatarka.rxloader.sample.test;
2 |
3 | /**
4 | * Created by evan on 9/20/14.
5 | */
6 | public class RxLoaderActivityWithSupportFragmentTest extends BaseRxLoaderActivityWithFragmentTest {
7 | public RxLoaderActivityWithSupportFragmentTest() {
8 | super(RxLoaderActivityWithSupportFragment.class);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/sample/src/main/res/menu/main.xml:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/rxloader/src/main/java/me/tatarka/rxloader/RxLoaderBackend.java:
--------------------------------------------------------------------------------
1 | package me.tatarka.rxloader;
2 |
3 | import java.lang.ref.WeakReference;
4 |
5 | import rx.Observer;
6 |
7 | /**
8 | * @author Evan Tatarka
9 | */
10 | interface RxLoaderBackend {
11 | CachingWeakRefSubscriber get(String tag);
12 |
13 | void put(String tag, BaseRxLoader rxLoader, CachingWeakRefSubscriber subscriber);
14 |
15 | void setSave(String tag, Observer observer, WeakReference> saveCallbackRef);
16 |
17 | void unsubscribeAll();
18 |
19 | void clearAll();
20 | }
21 |
--------------------------------------------------------------------------------
/sample/src/main/java/me/tatarka/rxloader/sample/MainActivityWithFragment.java:
--------------------------------------------------------------------------------
1 | package me.tatarka.rxloader.sample;
2 |
3 | import android.os.Bundle;
4 | import android.support.v4.app.FragmentActivity;
5 |
6 | public class MainActivityWithFragment extends FragmentActivity {
7 | @Override
8 | protected void onCreate(Bundle savedInstanceState) {
9 | super.onCreate(savedInstanceState);
10 |
11 | if (savedInstanceState == null) {
12 | getSupportFragmentManager().beginTransaction()
13 | .add(android.R.id.content, new MainFragment())
14 | .commit();
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/sample/proguard-rules.txt:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /opt/android-sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the ProGuard
5 | # include property in project.properties.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
--------------------------------------------------------------------------------
/rxloader/src/main/java/me/tatarka/rxloader/ParcelableSaveCallback.java:
--------------------------------------------------------------------------------
1 | package me.tatarka.rxloader;
2 |
3 | import android.os.Bundle;
4 | import android.os.Parcelable;
5 |
6 | /**
7 | * A default implementation of {@link me.tatarka.rxloader.SaveCallback} that saves and restores an
8 | * object that is {@link android.os.Parcelable}.
9 | *
10 | * @param the value type
11 | */
12 | public class ParcelableSaveCallback implements SaveCallback {
13 | @Override
14 | public void onSave(String key, T value, Bundle outState) {
15 | outState.putParcelable(key, (Parcelable) value);
16 | }
17 |
18 | @Override
19 | public T onRestore(String key, Bundle savedState) {
20 | return (T) savedState.getParcelable(key);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/sample/src/main/java/me/tatarka/rxloader/sample/test/TestableRxLoaderActivity.java:
--------------------------------------------------------------------------------
1 | package me.tatarka.rxloader.sample.test;
2 |
3 | import me.tatarka.rxloader.RxLoader;
4 | import rx.Observable;
5 |
6 | /**
7 | * Created by evan on 9/20/14.
8 | */
9 | public interface TestableRxLoaderActivity {
10 | RxLoader createLoader(Observable observable);
11 |
12 | void waitForNext() throws InterruptedException;
13 |
14 | T getNext();
15 |
16 | void waitForError() throws InterruptedException;
17 |
18 | Throwable getError();
19 |
20 | void waitForStarted() throws InterruptedException;
21 |
22 | boolean isStarted();
23 |
24 | void waitForCompleted() throws InterruptedException;
25 |
26 | boolean isCompleted();
27 | }
28 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Settings specified in this file will override any Gradle settings
5 | # configured through the IDE.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
--------------------------------------------------------------------------------
/sample/src/main/java/me/tatarka/rxloader/sample/MainActivityWithViewPager.java:
--------------------------------------------------------------------------------
1 | package me.tatarka.rxloader.sample;
2 |
3 | import android.os.Bundle;
4 | import android.support.v4.app.Fragment;
5 | import android.support.v4.app.FragmentActivity;
6 | import android.support.v4.app.FragmentPagerAdapter;
7 | import android.support.v4.view.ViewPager;
8 |
9 | public class MainActivityWithViewPager extends FragmentActivity {
10 | @Override
11 | protected void onCreate(Bundle savedInstanceState) {
12 | super.onCreate(savedInstanceState);
13 | setContentView(R.layout.activity_viewpager);
14 |
15 | ViewPager pager = (ViewPager) findViewById(R.id.view_pager);
16 | pager.setAdapter(new FragmentPagerAdapter(getSupportFragmentManager()) {
17 | @Override
18 | public Fragment getItem(int i) {
19 | return new MainFragment();
20 | }
21 |
22 | @Override
23 | public int getCount() {
24 | return 5;
25 | }
26 | });
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/sample/src/main/java/me/tatarka/rxloader/sample/test/TestableRxLoaderActivityWithFragment.java:
--------------------------------------------------------------------------------
1 | package me.tatarka.rxloader.sample.test;
2 |
3 | import me.tatarka.rxloader.RxLoader;
4 | import rx.Observable;
5 |
6 | /**
7 | * Created by evan on 9/20/14.
8 | */
9 | public interface TestableRxLoaderActivityWithFragment extends TestableRxLoaderActivity {
10 | void removeFragment();
11 |
12 | void detachFragment();
13 |
14 | void reattchFragment();
15 |
16 | RxLoader createLoader(Observable observable, String tag);
17 |
18 | void waitForNext(String tag) throws InterruptedException;
19 |
20 | T getNext(String tag);
21 |
22 | void waitForError(String tag) throws InterruptedException;
23 |
24 | Throwable getError(String tag);
25 |
26 | void waitForStarted(String tag) throws InterruptedException;
27 |
28 | boolean isStarted(String tag);
29 |
30 | void waitForCompleted(String tag) throws InterruptedException;
31 |
32 | boolean isCompleted(String tag);
33 |
34 | void addFragment(String tag);
35 | }
36 |
--------------------------------------------------------------------------------
/sample/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
10 |
11 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/rxloader/src/main/java/me/tatarka/rxloader/SaveCallback.java:
--------------------------------------------------------------------------------
1 | package me.tatarka.rxloader;
2 |
3 | import android.os.Bundle;
4 |
5 | /**
6 | * A set of callbacks to handle saving and restoring a value from an Activity's or Fragment's
7 | * instance state.
8 | *
9 | * @param the value type
10 | */
11 | public interface SaveCallback {
12 | /**
13 | * Called when the value need to be saved.
14 | *
15 | * @param key A unique key for the given value. It is suggested that you use this to prefix
16 | * keys you add to the {@code Bundle}.
17 | * @param value the value
18 | * @param outState the {@code Bundle} to save the value in
19 | */
20 | void onSave(String key, T value, Bundle outState);
21 |
22 | /**
23 | * Called when the value needs to be restored.
24 | *
25 | * @param key A unique key for the given value. It is suggested that you use this to
26 | * prefix keys you add to the {@code Bundle}.
27 | * @param savedState the {@code Bundle} to restore the value from
28 | * @return the value
29 | */
30 | T onRestore(String key, Bundle savedState);
31 | }
32 |
--------------------------------------------------------------------------------
/sample/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | repositories {
4 | //maven { url "https://oss.sonatype.org/content/repositories/snapshots" }
5 | //mavenCentral()
6 | jcenter()
7 | }
8 |
9 | android {
10 | compileSdkVersion 20
11 | buildToolsVersion "20.0.0"
12 |
13 | defaultConfig {
14 | minSdkVersion 15
15 | targetSdkVersion 20
16 | versionCode 1
17 | versionName "1.0"
18 | testInstrumentationRunner "com.google.android.apps.common.testing.testrunner.GoogleInstrumentationTestRunner"
19 | }
20 |
21 | buildTypes {
22 | release {
23 | minifyEnabled false
24 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
25 | }
26 | }
27 |
28 | packagingOptions {
29 | exclude 'LICENSE.txt'
30 | }
31 | }
32 |
33 | dependencies {
34 | compile project(':rxloader')
35 | compile 'com.android.support:support-v4:20.0.0'
36 |
37 | androidTestCompile 'com.jakewharton.espresso:espresso:1.1-r3'
38 | androidTestCompile('com.squareup:fest-android:1.0.8') {
39 | exclude group: 'com.android.support', module: 'support-v4'
40 | }
41 | androidTestCompile 'com.jayway.android.robotium:robotium-solo:5.2.1'
42 | }
43 |
--------------------------------------------------------------------------------
/rxloader/src/main/java/me/tatarka/rxloader/RxLoaderObserver.java:
--------------------------------------------------------------------------------
1 | package me.tatarka.rxloader;
2 |
3 | import rx.Observer;
4 |
5 | /**
6 | * A specialized {@link rx.Observer} that can also respond to when it's subscribed.
7 | *
8 | * @param the observer value type
9 | */
10 | public abstract class RxLoaderObserver implements Observer {
11 | /**
12 | * Called either when the {@link rx.Observer} is subscribed to or when there is the
13 | * configuration change and the {@code Observer} has not completed. This is where you would show
14 | * that you are loading something.
15 | */
16 | public void onStarted() {
17 |
18 | }
19 |
20 | /**
21 | * Called either when the {@link rx.Observer} delivers a value or when there is a configuration
22 | * change and the {@code Observer} has delivered a value. If multiple values have been
23 | * delivered, only the last one will be re-sent. This is where you would show the result of your
24 | * async call.
25 | *
26 | * @param value the value
27 | */
28 | @Override
29 | public abstract void onNext(T value);
30 |
31 | /**
32 | * Called either when the {@link rx.Observer} completes or when there is a configuration change
33 | * and the {@code Observer} has been closed. This is where you would show that your async call
34 | * has completed.
35 | */
36 | @Override
37 | public void onCompleted() {
38 |
39 | }
40 |
41 | /**
42 | * Called either when the {@link rx.Observer} has an error or when there is a configuration
43 | * change and the {@code Observer} has received an error. This is where you would display the
44 | * error.
45 | *
46 | * @param e the error
47 | */
48 | @Override
49 | public void onError(Throwable e) {
50 |
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/sample/src/main/java/me/tatarka/rxloader/sample/SampleObservables.java:
--------------------------------------------------------------------------------
1 | package me.tatarka.rxloader.sample;
2 |
3 | import android.util.Log;
4 |
5 | import java.util.concurrent.TimeUnit;
6 |
7 | import rx.Notification;
8 | import rx.Observable;
9 | import rx.functions.Action1;
10 | import rx.functions.Func1;
11 |
12 | /**
13 | * Created by evan on 8/22/14.
14 | */
15 | public class SampleObservables {
16 | private static final String TAG = "RxLoader Sample";
17 |
18 | public static Observable delay() {
19 | return Observable.timer(2, TimeUnit.SECONDS).map(new Func1() {
20 | @Override
21 | public String call(Long aLong) {
22 | Log.d(TAG, "2 second delay!");
23 | return "Async Complete!";
24 | }
25 | });
26 | }
27 |
28 | public static Func1> inputDelay() {
29 | return new Func1>() {
30 | @Override
31 | public Observable call(final String input) {
32 | return Observable.timer(2, TimeUnit.SECONDS).map(new Func1() {
33 | @Override
34 | public String call(Long aLong) {
35 | Log.d(TAG, "2 second delay! [" + input + "]");
36 | return "Async Complete! [" + input + "]";
37 | }
38 | });
39 | }
40 | };
41 | }
42 |
43 | public static Observable count() {
44 | return Observable.interval(100, TimeUnit.MILLISECONDS).doOnEach(new Action1>() {
45 | @Override
46 | public void call(Notification super Long> notification) {
47 | Log.d(TAG, "tick!");
48 | }
49 | }).take(100);
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/rxloader/src/main/java/me/tatarka/rxloader/RxLoaderManagerCompat.java:
--------------------------------------------------------------------------------
1 | package me.tatarka.rxloader;
2 |
3 | import android.support.v4.app.Fragment;
4 | import android.support.v4.app.FragmentActivity;
5 |
6 | import static me.tatarka.rxloader.RxLoaderManager.FRAGMENT_TAG;
7 |
8 | /**
9 | * Get an instance of {@link me.tatarka.rxloader.RxLoaderManager} that works with the support
10 | * library.
11 | *
12 | * @author Evan Tatarka
13 | */
14 | public final class RxLoaderManagerCompat {
15 | private RxLoaderManagerCompat() {
16 |
17 | }
18 |
19 | /**
20 | * Get an instance of {@code RxLoaderManager} that is tied to the lifecycle of the given {@link
21 | * android.support.v4.app.FragmentActivity}.
22 | *
23 | * @param activity the activity
24 | * @return the {@code RxLoaderManager}
25 | */
26 | public static RxLoaderManager get(FragmentActivity activity) {
27 | RxLoaderBackendFragmentCompat manager = (RxLoaderBackendFragmentCompat) activity.getSupportFragmentManager().findFragmentByTag(FRAGMENT_TAG);
28 | if (manager == null) {
29 | manager = new RxLoaderBackendFragmentCompat();
30 | activity.getSupportFragmentManager().beginTransaction().add(manager, FRAGMENT_TAG).commit();
31 | }
32 | return new RxLoaderManager(manager);
33 | }
34 |
35 | /**
36 | * Get an instance of {@code RxLoaderManager} that is tied to the lifecycle of the given {@link
37 | * android.support.v4.app.Fragment}.
38 | *
39 | * @param fragment the fragment
40 | * @return the {@code RxLoaderManager}
41 | */
42 | public static RxLoaderManager get(Fragment fragment) {
43 | RxLoaderBackendNestedFragmentCompat manager = (RxLoaderBackendNestedFragmentCompat) fragment.getChildFragmentManager().findFragmentByTag(FRAGMENT_TAG);
44 | if (manager == null) {
45 | manager = new RxLoaderBackendNestedFragmentCompat();
46 | fragment.getChildFragmentManager().beginTransaction().add(manager, FRAGMENT_TAG).commit();
47 | }
48 | return new RxLoaderManager(manager);
49 | }
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/rxloader/src/main/java/me/tatarka/rxloader/RxLoaderBackendFragmentCompat.java:
--------------------------------------------------------------------------------
1 | package me.tatarka.rxloader;
2 |
3 |
4 | import android.os.Bundle;
5 | import android.support.v4.app.Fragment;
6 |
7 | import java.lang.ref.WeakReference;
8 |
9 | import rx.Observer;
10 |
11 | /**
12 | * Persists the task by running it in a fragment with {@code setRetainInstanceState(true)}. This is
13 | * used internally by {@link me.tatarka.rxloader.RxLoaderManager}.
14 | *
15 | * @author Evan Tatarka
16 | */
17 | public class RxLoaderBackendFragmentCompat extends Fragment implements RxLoaderBackend {
18 | private RxLoaderBackendFragmentHelper helper = new RxLoaderBackendFragmentHelper();
19 | private boolean wasDetached;
20 |
21 | @Override
22 | public void onCreate(Bundle savedInstanceState) {
23 | super.onCreate(savedInstanceState);
24 | setRetainInstance(true);
25 | helper.onCreate(savedInstanceState);
26 | }
27 |
28 | @Override
29 | public void onDestroy() {
30 | super.onDestroy();
31 | helper.onDestroy();
32 | }
33 |
34 | @Override
35 | public void onDetach() {
36 | super.onDetach();
37 | helper.onDetach();
38 | wasDetached = true;
39 | }
40 |
41 | @Override
42 | public void onSaveInstanceState(Bundle outState) {
43 | super.onSaveInstanceState(outState);
44 | helper.onSaveInstanceState(outState);
45 | }
46 |
47 | @Override
48 | public CachingWeakRefSubscriber get(String tag) {
49 | return helper.get(tag);
50 | }
51 |
52 | @Override
53 | public void put(String tag, BaseRxLoader rxLoader, CachingWeakRefSubscriber subscriber) {
54 | helper.put(tag, wasDetached ? null : rxLoader, subscriber);
55 | }
56 |
57 | @Override
58 | public void setSave(String tag, Observer observer, WeakReference> saveCallbackRef) {
59 | helper.setSave(tag, observer, saveCallbackRef);
60 | }
61 |
62 | @Override
63 | public void unsubscribeAll() {
64 | helper.unsubscribeAll();
65 | }
66 |
67 | @Override
68 | public void clearAll() {
69 | helper.clearAll();
70 | }
71 |
72 | public RxLoaderBackendFragmentHelper getHelper() {
73 | return helper;
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/rxloader/src/main/java/me/tatarka/rxloader/RxLoaderBackendFragment.java:
--------------------------------------------------------------------------------
1 | package me.tatarka.rxloader;
2 |
3 |
4 | import android.annotation.TargetApi;
5 | import android.app.Fragment;
6 | import android.os.Build;
7 | import android.os.Bundle;
8 |
9 | import java.lang.ref.WeakReference;
10 |
11 | import rx.Observer;
12 |
13 | /**
14 | * Persists the task by running it in a fragment with {@code setRetainInstanceState(true)}. This is
15 | * used internally by {@link me.tatarka.rxloader.RxLoaderManager}.
16 | *
17 | * @author Evan Tatarka
18 | */
19 | @TargetApi(Build.VERSION_CODES.HONEYCOMB)
20 | public class RxLoaderBackendFragment extends Fragment implements RxLoaderBackend {
21 | private RxLoaderBackendFragmentHelper helper = new RxLoaderBackendFragmentHelper();
22 | private boolean wasDetached;
23 |
24 | @Override
25 | public void onCreate(Bundle savedInstanceState) {
26 | super.onCreate(savedInstanceState);
27 | setRetainInstance(true);
28 | helper.onCreate(savedInstanceState);
29 | }
30 |
31 | @Override
32 | public void onDestroy() {
33 | super.onDestroy();
34 | helper.onDestroy();
35 | }
36 |
37 | @Override
38 | public void onDetach() {
39 | super.onDetach();
40 | helper.onDetach();
41 | wasDetached = true;
42 | }
43 |
44 | @Override
45 | public void onSaveInstanceState(Bundle outState) {
46 | super.onSaveInstanceState(outState);
47 | helper.onSaveInstanceState(outState);
48 | }
49 |
50 | @Override
51 | public CachingWeakRefSubscriber get(String tag) {
52 | return helper.get(tag);
53 | }
54 |
55 | @Override
56 | public void put(String tag, BaseRxLoader rxLoader, CachingWeakRefSubscriber subscriber) {
57 | helper.put(tag, wasDetached ? null : rxLoader, subscriber);
58 | }
59 |
60 | @Override
61 | public void setSave(String tag, Observer observer, WeakReference> saveCallbackRef) {
62 | helper.setSave(tag, observer, saveCallbackRef);
63 | }
64 |
65 | @Override
66 | public void unsubscribeAll() {
67 | helper.unsubscribeAll();
68 | }
69 |
70 | @Override
71 | public void clearAll() {
72 | helper.clearAll();
73 | }
74 |
75 | public RxLoaderBackendFragmentHelper getHelper() {
76 | return helper;
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/rxloader/src/main/java/me/tatarka/rxloader/RxLoader.java:
--------------------------------------------------------------------------------
1 | package me.tatarka.rxloader;
2 |
3 | import rx.Observable;
4 |
5 | /**
6 | * Manages a given {@link rx.Observable}, handling activity destruction, orientation changes, and
7 | * posting to the UI thread. You construct one using {@link me.tatarka.rxloader.RxLoaderManager#create(String,
8 | * rx.Observable, RxLoaderObserver)}.
9 | *
10 | * @param the observable's value type
11 | */
12 | public class RxLoader extends BaseRxLoader {
13 | private Observable observable;
14 |
15 | RxLoader(RxLoaderBackend manager, String tag, Observable observable, RxLoaderObserver observer) {
16 | super(manager, tag, observer);
17 | this.observable = observable;
18 | }
19 |
20 | /**
21 | * Starts the {@link rx.Observable} by subscribing to it. If the {@code Observable} is already
22 | * started, then does nothing.
23 | *
24 | * @return the {@code RxLoader} for chaining
25 | */
26 | public RxLoader start() {
27 | start(observable);
28 | return this;
29 | }
30 |
31 | /**
32 | * Restarts the {@link rx.Observable} by subscribing to it, unsubscribing first if it has
33 | * already been started.
34 | *
35 | * @return the {@code RxLoader} for chaining
36 | */
37 | public RxLoader restart() {
38 | restart(observable);
39 | return this;
40 | }
41 |
42 | /**
43 | * Saves the last value that the {@link rx.Observable} returns in {@link
44 | * rx.Observer#onNext(Object)} in the Activities'ss ore Fragment's instanceState bundle. When
45 | * the {@code Activity} or {@code Fragment} is recreated, then the value will be redelivered.
46 | *
47 | * The value must implement {@link android.os.Parcelable}. If not, you should use {@link
48 | * me.tatarka.rxloader.RxLoader#save(SaveCallback)} to save and restore the value yourself.
49 | *
50 | * @return the {@code RxLoader} for chaining
51 | */
52 | public RxLoader save() {
53 | super.save();
54 | return this;
55 | }
56 |
57 | /**
58 | * Saves the last value that the {@link rx.Observable} returns in {@link
59 | * rx.Observer#onNext(Object)} in the Activities's ore Fragment's instanceState bundle. When the
60 | * {@code Activity} or {@code Fragment} is recreated, then the value will be redelivered.
61 | *
62 | * @param saveCallback the callback to handle saving and restoring the value
63 | * @return the {@code RxLoader} for chaining
64 | */
65 | public RxLoader save(SaveCallback saveCallback) {
66 | super.save(saveCallback);
67 | return this;
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/rxloader/src/main/java/me/tatarka/rxloader/RxLoader1.java:
--------------------------------------------------------------------------------
1 | package me.tatarka.rxloader;
2 |
3 | import rx.Observable;
4 | import rx.functions.Func1;
5 |
6 | /**
7 | * A version of {@link me.tatarka.rxloader.RxLoader} that accepts an argument to construct the
8 | * observable.
9 | *
10 | * @param the argument type
11 | * @param the observable's value type
12 | * @see me.tatarka.rxloader.RxLoader
13 | */
14 | public class RxLoader1 extends BaseRxLoader {
15 | private Func1> observableFunc;
16 |
17 | RxLoader1(RxLoaderBackend manager, String tag, Func1> observableFunc, RxLoaderObserver observer) {
18 | super(manager, tag, observer);
19 | this.observableFunc = observableFunc;
20 | }
21 |
22 | /**
23 | * Starts the {@link rx.Observable} with the given argument.
24 | *
25 | * @param arg1 the argument
26 | * @return the {@code RxLoader1} for chaining
27 | * @see RxLoader#start()
28 | */
29 | public RxLoader1 start(A arg1) {
30 | start(observableFunc.call(arg1));
31 | return this;
32 | }
33 |
34 | /**
35 | * Restarts the {@link rx.Observable} with the given argument.
36 | *
37 | * @param arg1 the argument
38 | * @return the {@code RxLoader1} for chaining
39 | * @see RxLoader#restart()
40 | */
41 | public RxLoader1 restart(A arg1) {
42 | restart(observableFunc.call(arg1));
43 | return this;
44 | }
45 |
46 | /**
47 | * Saves the last value that the {@link rx.Observable} returns in {@link
48 | * rx.Observer#onNext(Object)} in the Activities'ss ore Fragment's instanceState bundle. When
49 | * the {@code Activity} or {@code Fragment} is recreated, then the value will be redelivered.
50 | *
51 | * The value must implement {@link android.os.Parcelable}. If not, you should use {@link
52 | * me.tatarka.rxloader.RxLoader#save(SaveCallback)} to save and restore the value yourself.
53 | *
54 | * @return the {@code RxLoader1} for chaining
55 | */
56 | public RxLoader1 save() {
57 | super.save();
58 | return this;
59 | }
60 |
61 | /**
62 | * Saves the last value that the {@link rx.Observable} returns in {@link
63 | * rx.Observer#onNext(Object)} in the Activities's ore Fragment's instanceState bundle. When the
64 | * {@code Activity} or {@code Fragment} is recreated, then the value will be redelivered.
65 | *
66 | * @param saveCallback the callback to handle saving and restoring the value
67 | * @return the {@code RxLoader1} for chaining
68 | */
69 | public RxLoader1 save(SaveCallback saveCallback) {
70 | super.save(saveCallback);
71 | return this;
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/sample/src/main/java/me/tatarka/rxloader/sample/test/RxLoaderActivity.java:
--------------------------------------------------------------------------------
1 | package me.tatarka.rxloader.sample.test;
2 |
3 | import android.app.Activity;
4 | import android.os.Bundle;
5 |
6 | import java.util.concurrent.Semaphore;
7 |
8 | import me.tatarka.rxloader.RxLoader;
9 | import me.tatarka.rxloader.RxLoaderManager;
10 | import me.tatarka.rxloader.RxLoaderObserver;
11 | import rx.Observable;
12 |
13 | /**
14 | * Created by evan on 9/20/14.
15 | */
16 | public class RxLoaderActivity extends Activity implements TestableRxLoaderActivity {
17 | private RxLoaderManager mLoaderManager;
18 | private boolean mStarted;
19 | private Semaphore mStartedSemaphore = new Semaphore(0);
20 | private Object mNext;
21 | private Semaphore mNextSemaphore = new Semaphore(0);
22 | private Throwable mError;
23 | private Semaphore mErrorSemaphore = new Semaphore(0);
24 | private boolean mCompleted;
25 | private Semaphore mCompletedSemaphore = new Semaphore(0);
26 |
27 | @Override
28 | protected void onCreate(Bundle savedInstanceState) {
29 | super.onCreate(savedInstanceState);
30 | mLoaderManager = RxLoaderManager.get(this);
31 | }
32 |
33 | public RxLoader createLoader(Observable observable) {
34 | return mLoaderManager.create(observable, new RxLoaderObserver() {
35 | @Override
36 | public void onStarted() {
37 | mStarted = true;
38 | mStartedSemaphore.release();
39 | }
40 |
41 | @Override
42 | public void onNext(T value) {
43 | mNext = value;
44 | mNextSemaphore.release();
45 | }
46 |
47 | @Override
48 | public void onError(Throwable e) {
49 | mError = e;
50 | mErrorSemaphore.release();
51 | }
52 |
53 | @Override
54 | public void onCompleted() {
55 | mCompleted = true;
56 | mCompletedSemaphore.release();
57 | }
58 | });
59 | }
60 |
61 | public void waitForNext() throws InterruptedException {
62 | mNextSemaphore.acquire();
63 | }
64 |
65 | public T getNext() {
66 | return (T) mNext;
67 | }
68 |
69 | public void waitForError() throws InterruptedException {
70 | mErrorSemaphore.acquire();
71 | }
72 |
73 | public Throwable getError() {
74 | return mError;
75 | }
76 |
77 | public void waitForStarted() throws InterruptedException {
78 | mStartedSemaphore.acquire();
79 | }
80 |
81 | public boolean isStarted() {
82 | return mStarted;
83 | }
84 |
85 | public void waitForCompleted() throws InterruptedException {
86 | mCompletedSemaphore.acquire();
87 | }
88 |
89 | public boolean isCompleted() {
90 | return mCompleted;
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/sample/src/main/java/me/tatarka/rxloader/sample/test/RxLoaderFragment.java:
--------------------------------------------------------------------------------
1 | package me.tatarka.rxloader.sample.test;
2 |
3 | import android.app.Fragment;
4 | import android.os.Bundle;
5 | import android.view.View;
6 |
7 | import java.util.concurrent.Semaphore;
8 |
9 | import me.tatarka.rxloader.RxLoader;
10 | import me.tatarka.rxloader.RxLoaderManager;
11 | import me.tatarka.rxloader.RxLoaderObserver;
12 | import rx.Observable;
13 |
14 | /**
15 | * Created by evan on 9/20/14.
16 | */
17 | public class RxLoaderFragment extends Fragment implements TestableRxLoaderActivity {
18 | private RxLoaderManager mLoaderManager;
19 | private boolean mStarted;
20 | private Semaphore mStartedSemaphore = new Semaphore(0);
21 | private Object mNext;
22 | private Semaphore mNextSemaphore = new Semaphore(0);
23 | private Throwable mError;
24 | private Semaphore mErrorSemaphore = new Semaphore(0);
25 | private boolean mCompleted;
26 | private Semaphore mCompletedSemaphore = new Semaphore(0);
27 |
28 | @Override
29 | public void onActivityCreated(Bundle savedInstanceState) {
30 | super.onActivityCreated(savedInstanceState);
31 | mLoaderManager = RxLoaderManager.get(this);
32 | }
33 |
34 | public RxLoader createLoader(Observable observable) {
35 | return mLoaderManager.create(observable, new RxLoaderObserver() {
36 | @Override
37 | public void onStarted() {
38 | mStarted = true;
39 | mStartedSemaphore.release();
40 | }
41 |
42 | @Override
43 | public void onNext(T value) {
44 | mNext = value;
45 | mNextSemaphore.release();
46 | }
47 |
48 | @Override
49 | public void onError(Throwable e) {
50 | mError = e;
51 | mErrorSemaphore.release();
52 | }
53 |
54 | @Override
55 | public void onCompleted() {
56 | mCompleted = true;
57 | mCompletedSemaphore.release();
58 | }
59 | });
60 | }
61 |
62 | public void waitForNext() throws InterruptedException {
63 | mNextSemaphore.acquire();
64 | }
65 |
66 | public T getNext() {
67 | return (T) mNext;
68 | }
69 |
70 | public void waitForError() throws InterruptedException {
71 | mErrorSemaphore.acquire();
72 | }
73 |
74 | public Throwable getError() {
75 | return mError;
76 | }
77 |
78 | public void waitForStarted() throws InterruptedException {
79 | mStartedSemaphore.acquire();
80 | }
81 |
82 | public boolean isStarted() {
83 | return mStarted;
84 | }
85 |
86 | public void waitForCompleted() throws InterruptedException {
87 | mCompletedSemaphore.acquire();
88 | }
89 |
90 | public boolean isCompleted() {
91 | return mCompleted;
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/sample/src/main/java/me/tatarka/rxloader/sample/test/RxLoaderFragmentActivity.java:
--------------------------------------------------------------------------------
1 | package me.tatarka.rxloader.sample.test;
2 |
3 | import android.os.Bundle;
4 | import android.support.v4.app.FragmentActivity;
5 |
6 | import java.util.concurrent.Semaphore;
7 |
8 | import me.tatarka.rxloader.RxLoader;
9 | import me.tatarka.rxloader.RxLoaderManager;
10 | import me.tatarka.rxloader.RxLoaderManagerCompat;
11 | import me.tatarka.rxloader.RxLoaderObserver;
12 | import rx.Observable;
13 |
14 | /**
15 | * Created by evan on 9/20/14.
16 | */
17 | public class RxLoaderFragmentActivity extends FragmentActivity implements TestableRxLoaderActivity {
18 | private RxLoaderManager mLoaderManager;
19 | private boolean mStarted;
20 | private Semaphore mStartedSemaphore = new Semaphore(0);
21 | private Object mNext;
22 | private Semaphore mNextSemaphore = new Semaphore(0);
23 | private Throwable mError;
24 | private Semaphore mErrorSemaphore = new Semaphore(0);
25 | private boolean mCompleted;
26 | private Semaphore mCompletedSemaphore = new Semaphore(0);
27 |
28 | @Override
29 | protected void onCreate(Bundle savedInstanceState) {
30 | super.onCreate(savedInstanceState);
31 | mLoaderManager = RxLoaderManagerCompat.get(this);
32 | }
33 |
34 | public RxLoader createLoader(Observable observable) {
35 | return mLoaderManager.create(observable, new RxLoaderObserver() {
36 | @Override
37 | public void onStarted() {
38 | mStarted = true;
39 | mStartedSemaphore.release();
40 | }
41 |
42 | @Override
43 | public void onNext(T value) {
44 | mNext = value;
45 | mNextSemaphore.release();
46 | }
47 |
48 | @Override
49 | public void onError(Throwable e) {
50 | mError = e;
51 | mErrorSemaphore.release();
52 | }
53 |
54 | @Override
55 | public void onCompleted() {
56 | mCompleted = true;
57 | mCompletedSemaphore.release();
58 | }
59 | });
60 | }
61 |
62 | public void waitForNext() throws InterruptedException {
63 | mNextSemaphore.acquire();
64 | }
65 |
66 | public T getNext() {
67 | return (T) mNext;
68 | }
69 |
70 | public void waitForError() throws InterruptedException {
71 | mErrorSemaphore.acquire();
72 | }
73 |
74 | public Throwable getError() {
75 | return mError;
76 | }
77 |
78 | public void waitForStarted() throws InterruptedException {
79 | mStartedSemaphore.acquire();
80 | }
81 |
82 | public boolean isStarted() {
83 | return mStarted;
84 | }
85 |
86 | public void waitForCompleted() throws InterruptedException {
87 | mCompletedSemaphore.acquire();
88 | }
89 |
90 | public boolean isCompleted() {
91 | return mCompleted;
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/sample/src/main/java/me/tatarka/rxloader/sample/test/RxLoaderSupportFragment.java:
--------------------------------------------------------------------------------
1 | package me.tatarka.rxloader.sample.test;
2 |
3 | import android.os.Bundle;
4 | import android.support.v4.app.Fragment;
5 | import android.view.View;
6 |
7 | import java.util.concurrent.Semaphore;
8 |
9 | import me.tatarka.rxloader.RxLoader;
10 | import me.tatarka.rxloader.RxLoaderManager;
11 | import me.tatarka.rxloader.RxLoaderManagerCompat;
12 | import me.tatarka.rxloader.RxLoaderObserver;
13 | import rx.Observable;
14 |
15 | /**
16 | * Created by evan on 9/20/14.
17 | */
18 | public class RxLoaderSupportFragment extends Fragment implements TestableRxLoaderActivity {
19 | private RxLoaderManager mLoaderManager;
20 | private boolean mStarted;
21 | private Semaphore mStartedSemaphore = new Semaphore(0);
22 | private Object mNext;
23 | private Semaphore mNextSemaphore = new Semaphore(0);
24 | private Throwable mError;
25 | private Semaphore mErrorSemaphore = new Semaphore(0);
26 | private boolean mCompleted;
27 | private Semaphore mCompletedSemaphore = new Semaphore(0);
28 |
29 | @Override
30 | public void onActivityCreated(Bundle savedInstanceState) {
31 | super.onActivityCreated(savedInstanceState);
32 | mLoaderManager = RxLoaderManagerCompat.get(this);
33 | }
34 |
35 | public RxLoader createLoader(Observable observable) {
36 | return mLoaderManager.create(observable, new RxLoaderObserver() {
37 | @Override
38 | public void onStarted() {
39 | mStarted = true;
40 | mStartedSemaphore.release();
41 | }
42 |
43 | @Override
44 | public void onNext(T value) {
45 | mNext = value;
46 | mNextSemaphore.release();
47 | }
48 |
49 | @Override
50 | public void onError(Throwable e) {
51 | mError = e;
52 | mErrorSemaphore.release();
53 | }
54 |
55 | @Override
56 | public void onCompleted() {
57 | mCompleted = true;
58 | mCompletedSemaphore.release();
59 | }
60 | });
61 | }
62 |
63 | public void waitForNext() throws InterruptedException {
64 | mNextSemaphore.acquire();
65 | }
66 |
67 | public T getNext() {
68 | return (T) mNext;
69 | }
70 |
71 | public void waitForError() throws InterruptedException {
72 | mErrorSemaphore.acquire();
73 | }
74 |
75 | public Throwable getError() {
76 | return mError;
77 | }
78 |
79 | public void waitForStarted() throws InterruptedException {
80 | mStartedSemaphore.acquire();
81 | }
82 |
83 | public boolean isStarted() {
84 | return mStarted;
85 | }
86 |
87 | public void waitForCompleted() throws InterruptedException {
88 | mCompletedSemaphore.acquire();
89 | }
90 |
91 | public boolean isCompleted() {
92 | return mCompleted;
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/rxloader/src/main/java/me/tatarka/rxloader/RxLoader2.java:
--------------------------------------------------------------------------------
1 | package me.tatarka.rxloader;
2 |
3 | import rx.Observable;
4 | import rx.functions.Func2;
5 |
6 | /**
7 | * A version of {@link me.tatarka.rxloader.RxLoader} that accepts two arguments to construct the
8 | * observable.
9 | *
10 | * @param the argument type
11 | * @param the observable's value type
12 | * @see me.tatarka.rxloader.RxLoader
13 | */
14 | public class RxLoader2 extends BaseRxLoader {
15 | private Func2> observableFunc;
16 |
17 | RxLoader2(RxLoaderBackend manager, String tag, Func2> observableFunc, RxLoaderObserver observer) {
18 | super(manager, tag, observer);
19 | this.observableFunc = observableFunc;
20 | }
21 |
22 | /**
23 | * Starts the {@link rx.Observable} with the given arguments.
24 | *
25 | * @param arg1 the first argument
26 | * @param arg2 the second argument
27 | * @return the {@code RxLoader2} for chaining
28 | * @see RxLoader#start()
29 | */
30 | public RxLoader2 start(A arg1, B arg2) {
31 | start(observableFunc.call(arg1, arg2));
32 | return this;
33 | }
34 |
35 | /**
36 | * Restarts the {@link rx.Observable} with the given arguments.
37 | *
38 | * @param arg1 the first argument
39 | * @param arg2 the second argument
40 | * @return the {@code RxLoader2} for chaining
41 | * @see RxLoader#restart()
42 | */
43 | public RxLoader2 restart(A arg1, B arg2) {
44 | restart(observableFunc.call(arg1, arg2));
45 | return this;
46 | }
47 |
48 | /**
49 | * Saves the last value that the {@link rx.Observable} returns in {@link
50 | * rx.Observer#onNext(Object)} in the Activities'ss ore Fragment's instanceState bundle. When
51 | * the {@code Activity} or {@code Fragment} is recreated, then the value will be redelivered.
52 | *
53 | * The value must implement {@link android.os.Parcelable}. If not, you should use {@link
54 | * me.tatarka.rxloader.RxLoader#save(SaveCallback)} to save and restore the value yourself.
55 | *
56 | * @return the {@code RxLoader2} for chaining
57 | */
58 | public RxLoader2 save() {
59 | super.save();
60 | return this;
61 | }
62 |
63 | /**
64 | * Saves the last value that the {@link rx.Observable} returns in {@link
65 | * rx.Observer#onNext(Object)} in the Activities'ss ore Fragment's instanceState bundle. When
66 | * the {@code Activity} or {@code Fragment} is recreated, then the value will be redelivered.
67 | *
68 | * The value must implement {@link android.os.Parcelable}. If not, you should use {@link
69 | * me.tatarka.rxloader.RxLoader#save(SaveCallback)} to save and restore the value yourself.
70 | *
71 | * @return the {@code RxLoader2} for chaining
72 | */
73 | public RxLoader2 save(SaveCallback saveCallback) {
74 | super.save(saveCallback);
75 | return this;
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/rxloader/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | maven { url 'http://repo.springsource.org/plugins-release' }
4 | }
5 | dependencies {
6 | classpath 'org.springframework.build.gradle:propdeps-plugin:0.0.6'
7 | }
8 | }
9 |
10 | apply plugin: 'java'
11 | apply plugin: 'maven'
12 | apply plugin: 'signing'
13 |
14 | apply plugin: 'propdeps'
15 |
16 | repositories {
17 | mavenLocal()
18 | }
19 |
20 | dependencies {
21 | compile 'io.reactivex:rxandroid:0.24.0'
22 | provided 'android:android:4.3_r2'
23 | provided 'android.support:compatibility-v4:21.0.0'
24 | }
25 |
26 | group = 'me.tatarka.rxloader'
27 | version = '1.1.0'
28 |
29 | sourceCompatibility=1.6
30 |
31 | task javadocJar(type: Jar, dependsOn: javadoc) {
32 | classifier = 'javadoc'
33 | from 'build/docs/javadoc'
34 | }
35 |
36 | task sourcesJar(type: Jar) {
37 | from sourceSets.main.allSource
38 | classifier = 'sources'
39 | }
40 |
41 | artifacts {
42 | archives jar
43 | archives javadocJar
44 | archives sourcesJar
45 | }
46 |
47 | signing {
48 | sign configurations.archives
49 | }
50 |
51 | if (project.hasProperty('sonatype.username') && project.hasProperty('sonatype.password'))
52 | uploadArchives {
53 | repositories {
54 | mavenDeployer {
55 | beforeDeployment { deployment -> signing.signPom(deployment) }
56 |
57 | def repoUrl
58 | if (project.version.endsWith("SNAPSHOT")) {
59 | repoUrl = "https://oss.sonatype.org/content/repositories/snapshots"
60 | } else {
61 | repoUrl = "https://oss.sonatype.org/service/local/staging/deploy/maven2/"
62 | }
63 |
64 | repository(url: repoUrl) {
65 | authentication(
66 | userName: project.getProperty('sonatype.username'),
67 | password: project.getProperty('sonatype.password'))
68 | }
69 |
70 | pom.project {
71 | name 'rxloader'
72 | packaging 'jar'
73 | description 'Handles the activity lifecyle for rxjava\'s Observable'
74 | url 'https://github.com/evant/rxloader'
75 |
76 | scm {
77 | url 'git@github.com:evant/rxloader.git'
78 | connection 'scm:git:git@github.com:evant/rxloader.git'
79 | developerConnection 'scm:git:git@github.com:evant/rxloader.git'
80 | }
81 |
82 | licenses {
83 | license {
84 | name 'The Apache Software License, Version 2.0'
85 | url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
86 | distribution 'repo'
87 | }
88 | }
89 |
90 | developers {
91 | developer {
92 | id 'evant'
93 | name 'Evan Tatarka'
94 | }
95 | }
96 | }
97 | }
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/rxloader/src/main/java/me/tatarka/rxloader/BaseRxLoader.java:
--------------------------------------------------------------------------------
1 | package me.tatarka.rxloader;
2 |
3 | import java.lang.ref.WeakReference;
4 |
5 | import rx.Observable;
6 | import rx.Scheduler;
7 | import rx.android.schedulers.AndroidSchedulers;
8 |
9 | abstract class BaseRxLoader {
10 | private RxLoaderBackend manager;
11 | private String tag;
12 | private RxLoaderObserver observer;
13 | private Scheduler scheduler;
14 | private SaveCallback saveCallback;
15 |
16 | BaseRxLoader(RxLoaderBackend manager, String tag, RxLoaderObserver observer) {
17 | scheduler = AndroidSchedulers.mainThread();
18 | this.manager = manager;
19 | this.tag = tag;
20 | this.observer = observer;
21 |
22 | CachingWeakRefSubscriber subscription = manager.get(tag);
23 | if (subscription != null) {
24 | subscription.set(observer);
25 | }
26 | }
27 |
28 | protected BaseRxLoader start(Observable observable) {
29 | CachingWeakRefSubscriber subscriber = manager.get(tag);
30 | if (subscriber == null) {
31 | manager.put(tag, this, createSubscriber(observable));
32 | }
33 | return this;
34 | }
35 |
36 | protected BaseRxLoader restart(Observable observable) {
37 | CachingWeakRefSubscriber subscriber = manager.get(tag);
38 | if (subscriber != null) {
39 | subscriber.unsubscribe();
40 | }
41 | manager.put(tag, this, createSubscriber(observable));
42 | if (saveCallback != null) {
43 | manager.setSave(tag, observer, new WeakReference>(saveCallback));
44 | }
45 | return this;
46 | }
47 |
48 | protected BaseRxLoader save(SaveCallback saveCallback) {
49 | this.saveCallback = saveCallback;
50 | manager.setSave(tag, observer, new WeakReference>(saveCallback));
51 | return this;
52 | }
53 |
54 | protected BaseRxLoader save() {
55 | return save(new ParcelableSaveCallback());
56 | }
57 |
58 | private CachingWeakRefSubscriber createSubscriber(Observable observable) {
59 | CachingWeakRefSubscriber subscriber = new CachingWeakRefSubscriber(observer);
60 | subscriber.setSubscription(observable.observeOn(scheduler).subscribe(subscriber));
61 | return subscriber;
62 | }
63 |
64 | /**
65 | * Cancels the task.
66 | *
67 | * @return true if the task was started, false otherwise
68 | */
69 | public boolean unsubscribe() {
70 | CachingWeakRefSubscriber subscriber = manager.get(tag);
71 | if (subscriber != null) {
72 | subscriber.unsubscribe();
73 | return true;
74 | }
75 | return false;
76 | }
77 |
78 | /**
79 | * Clears the loader's state. After a configuration change you will no longer received cached
80 | * values and {@link #start(rx.Observable)} will cause the observable to be executed again.
81 | * This is useful if the loader is handling transient state (showing a Toast for example).
82 | */
83 | public void clear() {
84 | CachingWeakRefSubscriber subscriber = manager.get(tag);
85 | if (subscriber != null) {
86 | subscriber.clear();
87 | }
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/rxloader/src/main/java/me/tatarka/rxloader/CachingWeakRefSubscriber.java:
--------------------------------------------------------------------------------
1 | package me.tatarka.rxloader;
2 |
3 | import java.lang.ref.WeakReference;
4 |
5 | import rx.Observer;
6 | import rx.Subscription;
7 |
8 | class CachingWeakRefSubscriber implements Observer, Subscription {
9 | private WeakReference> subscriberRef;
10 | private Subscription subscription;
11 | private SaveCallback saveCallback;
12 | private boolean isComplete;
13 | private boolean isError;
14 | private boolean hasValue;
15 | private Throwable error;
16 | private T lastValue;
17 |
18 | CachingWeakRefSubscriber() {
19 | set(null);
20 | }
21 |
22 | CachingWeakRefSubscriber(RxLoaderObserver observer) {
23 | set(observer);
24 | }
25 |
26 | public void set(RxLoaderObserver observer) {
27 | subscriberRef = new WeakReference>(observer);
28 | if (observer == null) return;
29 |
30 | if (!(isComplete || isError)) {
31 | observer.onStarted();
32 | }
33 |
34 | if (hasValue) {
35 | observer.onNext(lastValue);
36 | }
37 |
38 | if (isComplete) {
39 | observer.onCompleted();
40 | } else if (isError) {
41 | observer.onError(error);
42 | }
43 | }
44 |
45 | public void setSubscription(Subscription subscription) {
46 | this.subscription = subscription;
47 | }
48 |
49 | public void setSave(SaveCallback callback) {
50 | saveCallback = callback;
51 | if (callback == null) return;
52 |
53 | if (hasValue) {
54 | callback.onNext(lastValue);
55 | }
56 | }
57 |
58 | public Observer get() {
59 | return subscriberRef.get();
60 | }
61 |
62 | @Override
63 | public void onCompleted() {
64 | isComplete = true;
65 | Observer subscriber = subscriberRef.get();
66 | if (subscriber != null) subscriber.onCompleted();
67 | }
68 |
69 | @Override
70 | public void onError(Throwable e) {
71 | isError = true;
72 | error = e;
73 | Observer subscriber = subscriberRef.get();
74 | if (subscriber != null) subscriber.onError(e);
75 | }
76 |
77 | @Override
78 | public void onNext(T value) {
79 | hasValue = true;
80 | lastValue = value;
81 | Observer subscriber = subscriberRef.get();
82 | if (subscriber != null) subscriber.onNext(value);
83 | if (saveCallback != null) saveCallback.onNext(value);
84 | }
85 |
86 | @Override
87 | public void unsubscribe() {
88 | saveCallback = null;
89 | subscriberRef.clear();
90 | if (subscription != null) subscription.unsubscribe();
91 | }
92 |
93 | @Override
94 | public boolean isUnsubscribed() {
95 | Observer subscriber = subscriberRef.get();
96 | return subscriber == null;
97 | }
98 |
99 | public void clear() {
100 | unsubscribe();
101 | isComplete = false;
102 | isError = false;
103 | hasValue = false;
104 | error = null;
105 | lastValue = null;
106 | }
107 |
108 | interface SaveCallback {
109 | void onNext(T value);
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/sample/src/main/java/me/tatarka/rxloader/sample/test/RxLoaderActivityWithFragment.java:
--------------------------------------------------------------------------------
1 | package me.tatarka.rxloader.sample.test;
2 |
3 | import android.app.Activity;
4 | import android.os.Bundle;
5 |
6 | import me.tatarka.rxloader.RxLoader;
7 | import rx.Observable;
8 |
9 | /**
10 | * Created by evan on 9/20/14.
11 | */
12 | public class RxLoaderActivityWithFragment extends Activity implements TestableRxLoaderActivityWithFragment {
13 | private RxLoaderFragment mFragment;
14 |
15 | @Override
16 | protected void onCreate(Bundle savedInstanceState) {
17 | super.onCreate(savedInstanceState);
18 |
19 | if (savedInstanceState == null) {
20 | getFragmentManager().beginTransaction()
21 | .add(android.R.id.content, mFragment = new RxLoaderFragment())
22 | .commit();
23 | getFragmentManager().executePendingTransactions();
24 | } else {
25 | mFragment = (RxLoaderFragment) getFragmentManager().findFragmentById(android.R.id.content);
26 | }
27 | }
28 |
29 | @Override
30 | public void removeFragment() {
31 | getFragmentManager().beginTransaction()
32 | .remove(getFragment())
33 | .commit();
34 | getFragmentManager().executePendingTransactions();
35 | }
36 |
37 | @Override
38 | public void detachFragment() {
39 | getFragmentManager().beginTransaction()
40 | .detach(getFragment())
41 | .commit();
42 | getFragmentManager().executePendingTransactions();
43 | }
44 |
45 | @Override
46 | public void reattchFragment() {
47 | getFragmentManager().beginTransaction()
48 | .attach(getFragment())
49 | .commit();
50 | getFragmentManager().executePendingTransactions();
51 | }
52 |
53 | @Override
54 | public RxLoader createLoader(Observable observable, String tag) {
55 | return findFragmentByTag(tag).createLoader(observable);
56 | }
57 |
58 | public RxLoaderFragment findFragmentByTag(String tag) {
59 | return (RxLoaderFragment) getFragmentManager().findFragmentByTag(tag);
60 | }
61 |
62 | public RxLoaderFragment getFragment() {
63 | return mFragment;
64 | }
65 |
66 | @Override
67 | public void waitForNext(String tag) throws InterruptedException {
68 | findFragmentByTag(tag).waitForNext();
69 | }
70 |
71 | @Override
72 | public T getNext(String tag) {
73 | return findFragmentByTag(tag).getNext();
74 | }
75 |
76 | @Override
77 | public void waitForError(String tag) throws InterruptedException {
78 | findFragmentByTag(tag).waitForError();
79 | }
80 |
81 | @Override
82 | public Throwable getError(String tag) {
83 | return findFragmentByTag(tag).getError();
84 | }
85 |
86 | @Override
87 | public void waitForStarted(String tag) throws InterruptedException {
88 | findFragmentByTag(tag).waitForStarted();
89 | }
90 |
91 | @Override
92 | public boolean isStarted(String tag) {
93 | return findFragmentByTag(tag).isStarted();
94 | }
95 |
96 | @Override
97 | public void waitForCompleted(String tag) throws InterruptedException {
98 | findFragmentByTag(tag).waitForCompleted();
99 | }
100 |
101 | @Override
102 | public boolean isCompleted(String tag) {
103 | return findFragmentByTag(tag).isCompleted();
104 | }
105 |
106 | @Override
107 | public void addFragment(String tag) {
108 | getFragmentManager().beginTransaction()
109 | .add(new RxLoaderFragment(), tag)
110 | .commit();
111 | getFragmentManager().executePendingTransactions();
112 | }
113 |
114 | @Override
115 | public RxLoader createLoader(Observable observable) {
116 | return getFragment().createLoader(observable);
117 | }
118 |
119 | @Override
120 | public void waitForNext() throws InterruptedException {
121 | getFragment().waitForNext();
122 | }
123 |
124 | @Override
125 | public T getNext() {
126 | return getFragment().getNext();
127 | }
128 |
129 | @Override
130 | public void waitForError() throws InterruptedException {
131 | getFragment().waitForError();
132 | }
133 |
134 | @Override
135 | public Throwable getError() {
136 | return getFragment().getError();
137 | }
138 |
139 | @Override
140 | public void waitForStarted() throws InterruptedException {
141 | getFragment().waitForStarted();
142 | }
143 |
144 | @Override
145 | public boolean isStarted() {
146 | return getFragment().isStarted();
147 | }
148 |
149 | @Override
150 | public void waitForCompleted() throws InterruptedException {
151 | getFragment().waitForCompleted();
152 | }
153 |
154 | @Override
155 | public boolean isCompleted() {
156 | return getFragment().isCompleted();
157 | }
158 | }
159 |
--------------------------------------------------------------------------------
/sample/src/main/java/me/tatarka/rxloader/sample/test/RxLoaderActivityWithSupportFragment.java:
--------------------------------------------------------------------------------
1 | package me.tatarka.rxloader.sample.test;
2 |
3 | import android.os.Bundle;
4 | import android.support.v4.app.FragmentActivity;
5 |
6 | import me.tatarka.rxloader.RxLoader;
7 | import rx.Observable;
8 |
9 | /**
10 | * Created by evan on 9/20/14.
11 | */
12 | public class RxLoaderActivityWithSupportFragment extends FragmentActivity implements TestableRxLoaderActivityWithFragment {
13 | private RxLoaderSupportFragment mFragment;
14 |
15 | @Override
16 | protected void onCreate(Bundle savedInstanceState) {
17 | super.onCreate(savedInstanceState);
18 |
19 | if (savedInstanceState == null) {
20 | getSupportFragmentManager().beginTransaction()
21 | .add(android.R.id.content, mFragment = new RxLoaderSupportFragment())
22 | .commit();
23 | getSupportFragmentManager().executePendingTransactions();
24 | } else {
25 | mFragment = (RxLoaderSupportFragment) getSupportFragmentManager().findFragmentById(android.R.id.content);
26 | }
27 | }
28 |
29 | @Override
30 | public void removeFragment() {
31 | getSupportFragmentManager().beginTransaction()
32 | .remove(getFragment())
33 | .commit();
34 | getSupportFragmentManager().executePendingTransactions();
35 | }
36 |
37 | @Override
38 | public void detachFragment() {
39 | getSupportFragmentManager().beginTransaction()
40 | .detach(getFragment())
41 | .commit();
42 | getSupportFragmentManager().executePendingTransactions();
43 | }
44 |
45 | @Override
46 | public void reattchFragment() {
47 | getSupportFragmentManager().beginTransaction()
48 | .attach(getFragment())
49 | .commit();
50 | getSupportFragmentManager().executePendingTransactions();
51 | }
52 |
53 | @Override
54 | public RxLoader createLoader(Observable observable, String tag) {
55 | return findFragmentByTag(tag).createLoader(observable);
56 | }
57 |
58 | public RxLoaderSupportFragment findFragmentByTag(String tag) {
59 | return (RxLoaderSupportFragment) getSupportFragmentManager().findFragmentByTag(tag);
60 | }
61 |
62 | public RxLoaderSupportFragment getFragment() {
63 | return mFragment;
64 | }
65 |
66 | @Override
67 | public void waitForNext(String tag) throws InterruptedException {
68 | findFragmentByTag(tag).waitForNext();
69 | }
70 |
71 | @Override
72 | public T getNext(String tag) {
73 | return findFragmentByTag(tag).getNext();
74 | }
75 |
76 | @Override
77 | public void waitForError(String tag) throws InterruptedException {
78 | findFragmentByTag(tag).waitForError();
79 | }
80 |
81 | @Override
82 | public Throwable getError(String tag) {
83 | return findFragmentByTag(tag).getError();
84 | }
85 |
86 | @Override
87 | public void waitForStarted(String tag) throws InterruptedException {
88 | findFragmentByTag(tag).waitForStarted();
89 | }
90 |
91 | @Override
92 | public boolean isStarted(String tag) {
93 | return findFragmentByTag(tag).isStarted();
94 | }
95 |
96 | @Override
97 | public void waitForCompleted(String tag) throws InterruptedException {
98 | findFragmentByTag(tag).waitForCompleted();
99 | }
100 |
101 | @Override
102 | public boolean isCompleted(String tag) {
103 | return findFragmentByTag(tag).isCompleted();
104 | }
105 |
106 | @Override
107 | public void addFragment(String tag) {
108 | getSupportFragmentManager().beginTransaction()
109 | .add(new RxLoaderSupportFragment(), tag)
110 | .commit();
111 | getSupportFragmentManager().executePendingTransactions();
112 | }
113 |
114 | @Override
115 | public RxLoader createLoader(Observable observable) {
116 | return getFragment().createLoader(observable);
117 | }
118 |
119 | @Override
120 | public void waitForNext() throws InterruptedException {
121 | getFragment().waitForNext();
122 | }
123 |
124 | @Override
125 | public T getNext() {
126 | return getFragment().getNext();
127 | }
128 |
129 | @Override
130 | public void waitForError() throws InterruptedException {
131 | getFragment().waitForError();
132 | }
133 |
134 | @Override
135 | public Throwable getError() {
136 | return getFragment().getError();
137 | }
138 |
139 | @Override
140 | public void waitForStarted() throws InterruptedException {
141 | getFragment().waitForStarted();
142 | }
143 |
144 | @Override
145 | public boolean isStarted() {
146 | return getFragment().isStarted();
147 | }
148 |
149 | @Override
150 | public void waitForCompleted() throws InterruptedException {
151 | getFragment().waitForCompleted();
152 | }
153 |
154 | @Override
155 | public boolean isCompleted() {
156 | return getFragment().isCompleted();
157 | }
158 | }
159 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
13 |
14 |
20 |
21 |
31 |
32 |
33 |
36 |
37 |
43 |
44 |
54 |
55 |
56 |
59 |
60 |
66 |
67 |
77 |
78 |
79 |
82 |
83 |
89 |
90 |
101 |
102 |
103 |
106 |
107 |
113 |
114 |
124 |
125 |
126 |
132 |
133 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # For Cygwin, ensure paths are in UNIX format before anything is touched.
46 | if $cygwin ; then
47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
48 | fi
49 |
50 | # Attempt to set APP_HOME
51 | # Resolve links: $0 may be a link
52 | PRG="$0"
53 | # Need this for relative symlinks.
54 | while [ -h "$PRG" ] ; do
55 | ls=`ls -ld "$PRG"`
56 | link=`expr "$ls" : '.*-> \(.*\)$'`
57 | if expr "$link" : '/.*' > /dev/null; then
58 | PRG="$link"
59 | else
60 | PRG=`dirname "$PRG"`"/$link"
61 | fi
62 | done
63 | SAVED="`pwd`"
64 | cd "`dirname \"$PRG\"`/" >&-
65 | APP_HOME="`pwd -P`"
66 | cd "$SAVED" >&-
67 |
68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
69 |
70 | # Determine the Java command to use to start the JVM.
71 | if [ -n "$JAVA_HOME" ] ; then
72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
73 | # IBM's JDK on AIX uses strange locations for the executables
74 | JAVACMD="$JAVA_HOME/jre/sh/java"
75 | else
76 | JAVACMD="$JAVA_HOME/bin/java"
77 | fi
78 | if [ ! -x "$JAVACMD" ] ; then
79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
80 |
81 | Please set the JAVA_HOME variable in your environment to match the
82 | location of your Java installation."
83 | fi
84 | else
85 | JAVACMD="java"
86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
87 |
88 | Please set the JAVA_HOME variable in your environment to match the
89 | location of your Java installation."
90 | fi
91 |
92 | # Increase the maximum file descriptors if we can.
93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
94 | MAX_FD_LIMIT=`ulimit -H -n`
95 | if [ $? -eq 0 ] ; then
96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
97 | MAX_FD="$MAX_FD_LIMIT"
98 | fi
99 | ulimit -n $MAX_FD
100 | if [ $? -ne 0 ] ; then
101 | warn "Could not set maximum file descriptor limit: $MAX_FD"
102 | fi
103 | else
104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105 | fi
106 | fi
107 |
108 | # For Darwin, add options to specify how the application appears in the dock
109 | if $darwin; then
110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111 | fi
112 |
113 | # For Cygwin, switch paths to Windows format before running java
114 | if $cygwin ; then
115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 | JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 |
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 |
--------------------------------------------------------------------------------
/rxloader/src/main/java/me/tatarka/rxloader/RxLoaderBackendFragmentHelper.java:
--------------------------------------------------------------------------------
1 | package me.tatarka.rxloader;
2 |
3 | import android.os.Bundle;
4 | import android.util.SparseArray;
5 |
6 | import java.lang.ref.WeakReference;
7 | import java.util.HashMap;
8 | import java.util.Map;
9 |
10 | import rx.Observer;
11 |
12 | class RxLoaderBackendFragmentHelper implements RxLoaderBackend {
13 | private State state = new State();
14 | private Map childFragmentStates = new HashMap();
15 |
16 | public void onCreate(Bundle savedState) {
17 | onCreate(null, savedState);
18 | }
19 |
20 | public void onCreate(String id, Bundle savedState) {
21 | getState(id).savedState = savedState;
22 | }
23 |
24 | public void onDestroy() {
25 | onDestroy(null);
26 | }
27 |
28 | public void onDestroy(String id) {
29 | unsubscribeAll(id);
30 | getState(id).subscriptionMap.clear();
31 | }
32 |
33 | public void onDetach() {
34 | onDetach(null);
35 | }
36 |
37 | public void onDetach(String id) {
38 | getState(id).rxLoader = null;
39 | }
40 |
41 | public void onSaveInstanceState(Bundle outState) {
42 | onSaveInstanceState(null, outState);
43 | }
44 |
45 | public void onSaveInstanceState(String id, Bundle outState) {
46 | for (SaveItem> item : getState(id).saveItemMap.values()) {
47 | onSave(item, outState);
48 | }
49 | }
50 |
51 | private static void onSave(SaveItem item, Bundle outState) {
52 | SaveCallback saveCallback = item.saveCallbackRef.get();
53 | if (saveCallback != null) {
54 | saveCallback.onSave(item.tag, item.value, outState);
55 | }
56 | }
57 |
58 | public void onDestroyView(String id) {
59 | for (CachingWeakRefSubscriber subscription : getState(id).subscriptionMap.values()) {
60 | subscription.set(null);
61 | }
62 | }
63 |
64 | @Override
65 | public CachingWeakRefSubscriber get(String tag) {
66 | return get(null, tag);
67 | }
68 |
69 | public CachingWeakRefSubscriber get(String id, String tag) {
70 | return getState(id).subscriptionMap.get(tag);
71 | }
72 |
73 | @Override
74 | public void put(final String tag, BaseRxLoader rxLoader, CachingWeakRefSubscriber subscriber) {
75 | put(null, tag, rxLoader, subscriber);
76 | }
77 |
78 | public void put(String id, final String tag, BaseRxLoader rxLoader, CachingWeakRefSubscriber subscriber) {
79 | final State state = getState(id);
80 | state.rxLoader = rxLoader;
81 | state.subscriptionMap.put(tag, subscriber);
82 | if (state.saveItemMap.containsKey(tag)) {
83 | subscriber.setSave(new CachingWeakRefSubscriber.SaveCallback() {
84 | @Override
85 | public void onNext(Object value) {
86 | SaveItem item = state.saveItemMap.get(tag);
87 | if (item != null) item.value = value;
88 | }
89 | });
90 | }
91 | }
92 |
93 | @Override
94 | public void setSave(final String tag, Observer observer, WeakReference> saveCallbackRef) {
95 | setSave(null, tag, observer, saveCallbackRef);
96 | }
97 |
98 | public void setSave(String id, final String tag, Observer observer, WeakReference> saveCallbackRef) {
99 | final State state = getState(id);
100 | SaveItem item = new SaveItem(tag, saveCallbackRef);
101 |
102 | if (state.savedState != null) {
103 | SaveCallback saveCallback = saveCallbackRef.get();
104 | if (saveCallback != null) {
105 | T value = saveCallback.onRestore(tag, state.savedState);
106 | item.value = value;
107 | observer.onNext(value);
108 | }
109 | }
110 |
111 | state.saveItemMap.put(tag, item);
112 |
113 | CachingWeakRefSubscriber subscriber = get(tag);
114 | if (subscriber != null) {
115 | subscriber.setSave(new CachingWeakRefSubscriber.SaveCallback() {
116 | @Override
117 | public void onNext(Object value) {
118 | SaveItem item = state.saveItemMap.get(tag);
119 | if (item != null) item.value = value;
120 | }
121 | });
122 | }
123 | }
124 |
125 | @Override
126 | public void unsubscribeAll() {
127 | unsubscribeAll(null);
128 | }
129 |
130 | @Override
131 | public void clearAll() {
132 | clearAll(null);
133 | }
134 |
135 | public void unsubscribeAll(String id) {
136 | for (CachingWeakRefSubscriber subscription : getState(id).subscriptionMap.values()) {
137 | subscription.unsubscribe();
138 | }
139 | }
140 |
141 | public void clearAll(String id) {
142 | for (CachingWeakRefSubscriber subscription : getState(id).subscriptionMap.values()) {
143 | subscription.clear();
144 | }
145 | }
146 |
147 | private State getState(String id) {
148 | return id == null ? state : getChildFragmentState(id);
149 | }
150 |
151 | private State getChildFragmentState(String id) {
152 | State state = childFragmentStates.get(id);
153 | if (state == null) {
154 | state = new State();
155 | childFragmentStates.put(id, state);
156 | }
157 | return state;
158 | }
159 |
160 | private static class State {
161 | private BaseRxLoader rxLoader;
162 | private Map subscriptionMap = new HashMap();
163 | private Map saveItemMap = new HashMap();
164 | private Bundle savedState;
165 | }
166 |
167 | private static class SaveItem {
168 | final String tag;
169 | final WeakReference> saveCallbackRef;
170 | T value;
171 |
172 | private SaveItem(String tag, WeakReference> saveCallbackRef) {
173 | this.tag = tag;
174 | this.saveCallbackRef = saveCallbackRef;
175 | }
176 | }
177 | }
178 |
--------------------------------------------------------------------------------
/sample/src/androidTest/java/me/tatarka/rxloader/sample/test/BaseRxLoaderActivityWithFragmentTest.java:
--------------------------------------------------------------------------------
1 | package me.tatarka.rxloader.sample.test;
2 |
3 | import android.app.Activity;
4 | import android.test.ActivityInstrumentationTestCase2;
5 | import android.test.suitebuilder.annotation.SmallTest;
6 |
7 | import me.tatarka.rxloader.RxLoader;
8 | import rx.Observable;
9 | import rx.schedulers.TestScheduler;
10 | import rx.subjects.TestSubject;
11 |
12 | import static org.fest.assertions.api.Assertions.assertThat;
13 |
14 | /**
15 | * Created by evan on 9/20/14.
16 | */
17 | public abstract class BaseRxLoaderActivityWithFragmentTest extends BaseRxLoaderActivityTest {
18 | public BaseRxLoaderActivityWithFragmentTest(Class activityClass) {
19 | super(activityClass);
20 | }
21 |
22 | @SmallTest
23 | public void testLoaderStartRemoveFragment() throws InterruptedException {
24 | TestSubject subject = TestSubject.create(testScheduler);
25 | createLoader(getActivity(), subject).start();
26 | getActivity().waitForStarted();
27 | getInstrumentation().runOnMainSync(new Runnable() {
28 | @Override
29 | public void run() {
30 | getActivity().removeFragment();
31 | }
32 | });
33 | Thread.sleep(500); // Need to wait for onDestroy() to be called.
34 | subject.onNext("test");
35 | subject.onCompleted();
36 | testScheduler.triggerActions();
37 | getInstrumentation().waitForIdleSync();
38 |
39 | assertThat(getActivity().getNext()).isNull();
40 | assertThat(getActivity().isCompleted()).isFalse().as("onCompleted() is not called if the activity is destroyed");
41 | }
42 |
43 | @SmallTest
44 | public void testLoaderStartDetachFragment() throws InterruptedException {
45 | TestSubject subject = TestSubject.create(testScheduler);
46 | createLoader(getActivity(), subject).start();
47 | getActivity().waitForStarted();
48 | getInstrumentation().runOnMainSync(new Runnable() {
49 | @Override
50 | public void run() {
51 | getActivity().detachFragment();
52 | }
53 | });
54 | Thread.sleep(500); // Need to wait for onDestroy() to be called.
55 | subject.onNext("test");
56 | subject.onCompleted();
57 | testScheduler.triggerActions();
58 | getInstrumentation().waitForIdleSync();
59 |
60 | assertThat(getActivity().getNext()).isNull();
61 | assertThat(getActivity().isCompleted()).isFalse().as("onCompleted() is not called if the fragment is detached");
62 | }
63 |
64 | @SmallTest
65 | public void testLoaderStartDetachAndAttachFragment() throws InterruptedException {
66 | TestSubject subject = TestSubject.create(testScheduler);
67 | createLoader(getActivity(), subject).start();
68 | getActivity().waitForStarted();
69 | getInstrumentation().runOnMainSync(new Runnable() {
70 | @Override
71 | public void run() {
72 | getActivity().detachFragment();
73 | }
74 | });
75 | Thread.sleep(500); // Need to wait for onDestroy() to be called.
76 | subject.onNext("test");
77 | subject.onCompleted();
78 | testScheduler.triggerActions();
79 | getInstrumentation().waitForIdleSync();
80 | getInstrumentation().runOnMainSync(new Runnable() {
81 | @Override
82 | public void run() {
83 | getActivity().reattchFragment();
84 | }
85 | });
86 | getInstrumentation().waitForIdleSync();
87 | createLoader(getActivity(), subject);
88 | getActivity().waitForNext();
89 | getActivity().waitForCompleted();
90 |
91 | assertThat(getActivity().getNext()).isEqualTo("test").as("result is value delivered from observable");
92 | assertThat(getActivity().isCompleted()).isTrue().as("onCompleted() called when fragment is reattached");
93 | }
94 |
95 | @SmallTest
96 | public void testMultipleLoaderFragments() throws InterruptedException {
97 | final String fragment1 = "fragment1";
98 | final String fragment2 = "fragment2";
99 | TestSubject subject1 = TestSubject.create(testScheduler);
100 | TestSubject subject2 = TestSubject.create(testScheduler);
101 | getInstrumentation().runOnMainSync(new Runnable() {
102 | @Override
103 | public void run() {
104 | getActivity().addFragment(fragment1);
105 | getActivity().addFragment(fragment2);
106 | }
107 | });
108 | createLoader(subject1, fragment1).start();
109 | createLoader(subject2, fragment2).start();
110 | getActivity().waitForStarted(fragment1);
111 | getActivity().waitForStarted(fragment2);
112 | subject1.onNext("test1");
113 | subject2.onNext("test2");
114 | subject1.onCompleted();
115 | subject2.onCompleted();
116 | testScheduler.triggerActions();
117 | getActivity().waitForNext(fragment1);
118 | getActivity().waitForCompleted(fragment1);
119 | getActivity().waitForNext(fragment2);
120 | getActivity().waitForCompleted(fragment2);
121 |
122 | assertThat(getActivity().getNext(fragment1)).isEqualTo("test1").as("result is value delivered from observable");
123 | assertThat(getActivity().isCompleted(fragment1)).isTrue().as("onCompleted() called when observable completed");
124 |
125 | assertThat(getActivity().getNext(fragment2)).isEqualTo("test2").as("result is value delivered from observable");
126 | assertThat(getActivity().isCompleted(fragment2)).isTrue().as("onCompleted() called when observable completed");
127 | }
128 |
129 | protected RxLoader createLoader(final Observable observable, final String tag) {
130 | final RxLoader>[] currentLoader = new RxLoader>[1];
131 | getInstrumentation().runOnMainSync(new Runnable() {
132 | @Override
133 | public void run() {
134 | currentLoader[0] = getActivity().createLoader(observable, tag);
135 | }
136 | });
137 | return (RxLoader) currentLoader[0];
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/rxloader/src/main/java/me/tatarka/rxloader/RxLoaderBackendNestedFragment.java:
--------------------------------------------------------------------------------
1 | package me.tatarka.rxloader;
2 |
3 |
4 | import android.annotation.TargetApi;
5 | import android.app.Activity;
6 | import android.app.Fragment;
7 | import android.os.Build;
8 | import android.os.Bundle;
9 |
10 | import java.lang.ref.WeakReference;
11 | import java.util.ArrayList;
12 | import java.util.List;
13 |
14 | import rx.Observer;
15 |
16 | /**
17 | * Persists the task by running it in a fragment with {@code setRetainInstanceState(true)}. This is
18 | * used internally by {@link RxLoaderManager}.
19 | *
20 | * @author Evan Tatarka
21 | */
22 | @TargetApi(Build.VERSION_CODES.HONEYCOMB)
23 | public class RxLoaderBackendNestedFragment extends Fragment implements RxLoaderBackend {
24 | private WeakReference helperRef;
25 | private List> pendingPuts = new ArrayList>();
26 | private boolean hasSavedState;
27 | private boolean wasDetached;
28 | private String stateId;
29 |
30 | private RxLoaderBackendFragmentHelper getHelper() {
31 | if (helperRef != null) {
32 | return helperRef.get();
33 | } else {
34 | Activity activity = getActivity();
35 | if (activity == null) {
36 | return null;
37 | }
38 |
39 | RxLoaderBackendFragment backendFragment = (RxLoaderBackendFragment) activity
40 | .getFragmentManager().findFragmentByTag(RxLoaderManager.FRAGMENT_TAG);
41 | if (backendFragment == null) {
42 | backendFragment = new RxLoaderBackendFragment();
43 | activity.getFragmentManager().beginTransaction()
44 | .add(backendFragment, RxLoaderManager.FRAGMENT_TAG)
45 | .commit();
46 | }
47 |
48 | RxLoaderBackendFragmentHelper helper = backendFragment.getHelper();
49 | setHelper(helper);
50 | helperRef = new WeakReference(helper);
51 | return helper;
52 | }
53 | }
54 |
55 | private void setHelper(RxLoaderBackendFragmentHelper helper) {
56 | helperRef = new WeakReference(helper);
57 | for (PendingPut pendingPut: pendingPuts) {
58 | put(pendingPut.tag, pendingPut.rxLoader, pendingPut.subscriber);
59 | }
60 | pendingPuts.clear();
61 | }
62 |
63 | @Override
64 | public void onActivityCreated(Bundle savedInstanceState) {
65 | super.onActivityCreated(savedInstanceState);
66 | RxLoaderBackendFragmentHelper helper = getHelper();
67 | if (helper != null) {
68 | helper.onCreate(getStateId(), savedInstanceState);
69 | }
70 | }
71 |
72 | @Override
73 | public void onDestroy() {
74 | super.onDestroy();
75 | if (!hasSavedState) {
76 | RxLoaderBackendFragmentHelper helper = getHelper();
77 | if (helper != null) {
78 | helper.onDestroy(getStateId());
79 | }
80 | }
81 | }
82 |
83 | @Override
84 | public void onDetach() {
85 | super.onDetach();
86 | RxLoaderBackendFragmentHelper helper = getHelper();
87 | if (helper != null) {
88 | helper.onDetach(getStateId());
89 | }
90 | pendingPuts.clear();
91 | wasDetached = true;
92 | }
93 |
94 | @Override
95 | public void onSaveInstanceState(Bundle outState) {
96 | super.onSaveInstanceState(outState);
97 | hasSavedState = true;
98 | RxLoaderBackendFragmentHelper helper = getHelper();
99 | if (helper != null) {
100 | helper.onSaveInstanceState(outState);
101 | }
102 | }
103 |
104 | @Override
105 | public CachingWeakRefSubscriber get(String tag) {
106 | RxLoaderBackendFragmentHelper helper = getHelper();
107 | if (helper != null) {
108 | return helper.get(getStateId(), tag);
109 | }
110 | return null;
111 | }
112 |
113 | @Override
114 | public void put(String tag, BaseRxLoader rxLoader, CachingWeakRefSubscriber subscriber) {
115 | RxLoaderBackendFragmentHelper helper = getHelper();
116 | if (helper != null) {
117 | helper.put(getStateId(), tag, wasDetached ? null : rxLoader, subscriber);
118 | } else {
119 | pendingPuts.add(new PendingPut(tag, rxLoader, subscriber));
120 | }
121 | }
122 |
123 | @Override
124 | public void setSave(String tag, Observer observer, WeakReference> saveCallbackRef) {
125 | RxLoaderBackendFragmentHelper helper = getHelper();
126 | if (helper != null) {
127 | helper.setSave(getStateId(), tag, observer, saveCallbackRef);
128 | }
129 | }
130 |
131 | @Override
132 | public void unsubscribeAll() {
133 | RxLoaderBackendFragmentHelper helper = getHelper();
134 | if (helper != null) {
135 | helper.unsubscribeAll(getStateId());
136 | }
137 | }
138 |
139 | @Override
140 | public void clearAll() {
141 | RxLoaderBackendFragmentHelper helper = getHelper();
142 | if (helper != null) {
143 | helper.clearAll(getStateId());
144 | }
145 | }
146 |
147 | @Override
148 | public void onDestroyView() {
149 | super.onDestroyView();
150 | RxLoaderBackendFragmentHelper helper = getHelper();
151 | if (helper != null) {
152 | helper.onDestroyView(getStateId());
153 | }
154 | }
155 |
156 | private String getStateId() {
157 | if (stateId != null) {
158 | return stateId;
159 | }
160 |
161 | Fragment parentFragment = getParentFragment();
162 | stateId = parentFragment.getTag();
163 | if (stateId == null) {
164 | int id = parentFragment.getId();
165 | if (id > 0) {
166 | stateId = Integer.toString(id);
167 | }
168 | }
169 |
170 | if (stateId == null) {
171 | throw new IllegalStateException("Fragment dose not have a valid id");
172 | }
173 |
174 | return stateId;
175 | }
176 |
177 | private static class PendingPut {
178 | String tag;
179 | BaseRxLoader rxLoader;
180 | CachingWeakRefSubscriber subscriber;
181 |
182 | private PendingPut(String tag, BaseRxLoader rxLoader, CachingWeakRefSubscriber subscriber) {
183 | this.tag = tag;
184 | this.rxLoader = rxLoader;
185 | this.subscriber = subscriber;
186 | }
187 | }
188 | }
189 |
--------------------------------------------------------------------------------
/rxloader/src/main/java/me/tatarka/rxloader/RxLoaderBackendNestedFragmentCompat.java:
--------------------------------------------------------------------------------
1 | package me.tatarka.rxloader;
2 |
3 |
4 | import android.annotation.TargetApi;
5 | import android.os.Build;
6 | import android.os.Bundle;
7 | import android.support.v4.app.Fragment;
8 | import android.support.v4.app.FragmentActivity;
9 |
10 | import java.lang.ref.WeakReference;
11 | import java.util.ArrayList;
12 | import java.util.List;
13 |
14 | import rx.Observer;
15 |
16 | /**
17 | * Persists the task by running it in a fragment with {@code setRetainInstanceState(true)}. This is
18 | * used internally by {@link me.tatarka.rxloader.RxLoaderManager}.
19 | *
20 | * @author Evan Tatarka
21 | */
22 | @TargetApi(Build.VERSION_CODES.HONEYCOMB)
23 | public class RxLoaderBackendNestedFragmentCompat extends Fragment implements RxLoaderBackend {
24 | private WeakReference helperRef;
25 | private List> pendingPuts = new ArrayList>();
26 | private boolean hasSavedState;
27 | private boolean wasDetached;
28 | private String stateId;
29 |
30 | private RxLoaderBackendFragmentHelper getHelper() {
31 | if (helperRef != null) {
32 | return helperRef.get();
33 | } else {
34 | FragmentActivity activity = getActivity();
35 | if (activity == null) {
36 | return null;
37 | }
38 |
39 | RxLoaderBackendFragmentCompat backendFragment = (RxLoaderBackendFragmentCompat) activity
40 | .getSupportFragmentManager().findFragmentByTag(RxLoaderManager.FRAGMENT_TAG);
41 | if (backendFragment == null) {
42 | backendFragment = new RxLoaderBackendFragmentCompat();
43 | activity.getSupportFragmentManager().beginTransaction()
44 | .add(backendFragment, RxLoaderManager.FRAGMENT_TAG)
45 | .commit();
46 | }
47 |
48 | RxLoaderBackendFragmentHelper helper = backendFragment.getHelper();
49 | setHelper(helper);
50 | helperRef = new WeakReference(helper);
51 | return helper;
52 | }
53 | }
54 |
55 | private void setHelper(RxLoaderBackendFragmentHelper helper) {
56 | helperRef = new WeakReference(helper);
57 | for (PendingPut pendingPut: pendingPuts) {
58 | put(pendingPut.tag, pendingPut.rxLoader, pendingPut.subscriber);
59 | }
60 | pendingPuts.clear();
61 | }
62 |
63 | @Override
64 | public void onActivityCreated(Bundle savedInstanceState) {
65 | super.onActivityCreated(savedInstanceState);
66 | RxLoaderBackendFragmentHelper helper = getHelper();
67 | if (helper != null) {
68 | helper.onCreate(getStateId(), savedInstanceState);
69 | }
70 | }
71 |
72 | @Override
73 | public void onDestroy() {
74 | super.onDestroy();
75 | if (!hasSavedState) {
76 | RxLoaderBackendFragmentHelper helper = getHelper();
77 | if (helper != null) {
78 | helper.onDestroy(getStateId());
79 | }
80 | }
81 | }
82 |
83 | @Override
84 | public void onDetach() {
85 | super.onDetach();
86 | RxLoaderBackendFragmentHelper helper = getHelper();
87 | if (helper != null) {
88 | helper.onDetach(getStateId());
89 | }
90 | pendingPuts.clear();
91 | wasDetached = true;
92 | }
93 |
94 | @Override
95 | public void onSaveInstanceState(Bundle outState) {
96 | super.onSaveInstanceState(outState);
97 | hasSavedState = true;
98 | RxLoaderBackendFragmentHelper helper = getHelper();
99 | if (helper != null) {
100 | helper.onSaveInstanceState(outState);
101 | }
102 | }
103 |
104 | @Override
105 | public CachingWeakRefSubscriber get(String tag) {
106 | RxLoaderBackendFragmentHelper helper = getHelper();
107 | if (helper != null) {
108 | return helper.get(getStateId(), tag);
109 | }
110 | return null;
111 | }
112 |
113 | @Override
114 | public void put(String tag, BaseRxLoader rxLoader, CachingWeakRefSubscriber subscriber) {
115 | RxLoaderBackendFragmentHelper helper = getHelper();
116 | if (helper != null) {
117 | helper.put(getStateId(), tag, wasDetached ? null : rxLoader, subscriber);
118 | } else {
119 | pendingPuts.add(new PendingPut(tag, rxLoader, subscriber));
120 | }
121 | }
122 |
123 | @Override
124 | public void setSave(String tag, Observer observer, WeakReference> saveCallbackRef) {
125 | RxLoaderBackendFragmentHelper helper = getHelper();
126 | if (helper != null) {
127 | helper.setSave(getStateId(), tag, observer, saveCallbackRef);
128 | }
129 | }
130 |
131 | @Override
132 | public void unsubscribeAll() {
133 | RxLoaderBackendFragmentHelper helper = getHelper();
134 | if (helper != null) {
135 | helper.unsubscribeAll(getStateId());
136 | }
137 | }
138 |
139 | @Override
140 | public void clearAll() {
141 | RxLoaderBackendFragmentHelper helper = getHelper();
142 | if (helper != null) {
143 | helper.clearAll(getStateId());
144 | }
145 | }
146 |
147 | @Override
148 | public void onDestroyView() {
149 | super.onDestroyView();
150 | RxLoaderBackendFragmentHelper helper = getHelper();
151 | if (helper != null) {
152 | helper.onDestroyView(getStateId());
153 | }
154 | }
155 |
156 | private String getStateId() {
157 | if (stateId != null) {
158 | return stateId;
159 | }
160 |
161 | Fragment parentFragment = getParentFragment();
162 | stateId = parentFragment.getTag();
163 | if (stateId == null) {
164 | int id = parentFragment.getId();
165 | if (id > 0) {
166 | stateId = Integer.toString(id);
167 | }
168 | }
169 |
170 | if (stateId == null) {
171 | throw new IllegalStateException("Fragment dose not have a valid id");
172 | }
173 |
174 | return stateId;
175 | }
176 |
177 | private static class PendingPut {
178 | String tag;
179 | BaseRxLoader rxLoader;
180 | CachingWeakRefSubscriber subscriber;
181 |
182 | private PendingPut(String tag, BaseRxLoader rxLoader, CachingWeakRefSubscriber subscriber) {
183 | this.tag = tag;
184 | this.rxLoader = rxLoader;
185 | this.subscriber = subscriber;
186 | }
187 | }
188 | }
189 |
--------------------------------------------------------------------------------
/sample/src/main/java/me/tatarka/rxloader/sample/MainActivity.java:
--------------------------------------------------------------------------------
1 | package me.tatarka.rxloader.sample;
2 |
3 | import android.app.Activity;
4 | import android.os.Bundle;
5 | import android.util.Log;
6 | import android.view.View;
7 | import android.widget.Button;
8 | import android.widget.EditText;
9 | import android.widget.ProgressBar;
10 |
11 | import java.util.concurrent.TimeUnit;
12 |
13 | import me.tatarka.rxloader.RxLoader;
14 | import me.tatarka.rxloader.RxLoader1;
15 | import me.tatarka.rxloader.RxLoaderManager;
16 | import me.tatarka.rxloader.RxLoaderObserver;
17 | import me.tatarka.rxloader.SaveCallback;
18 | import rx.Notification;
19 | import rx.Observable;
20 | import rx.functions.Action1;
21 | import rx.functions.Func1;
22 |
23 | public class MainActivity extends Activity {
24 | private static final String DELAY_TASK_INIT = "sleep_task_init";
25 | private static final String DELAY_TASK_RESTART = "sleep_task_restart";
26 | private static final String PROGRESS_TASK = "progress_task";
27 | private static final String INPUT_TASK = "input_task";
28 |
29 | RxLoaderManager loaderManager;
30 | ProgressBar progressLaunch;
31 | Button buttonLaunch;
32 | ProgressBar progressInit;
33 | Button buttonInit;
34 | ProgressBar progressRestart;
35 | Button buttonRestart;
36 | ProgressBar progressProgress;
37 | Button buttonProgress;
38 | ProgressBar progressInput;
39 | Button buttonInput;
40 | EditText editInput;
41 |
42 | @Override
43 | protected void onCreate(Bundle savedInstanceState) {
44 | super.onCreate(savedInstanceState);
45 | loaderManager = RxLoaderManager.get(this);
46 |
47 | setContentView(R.layout.activity_main);
48 |
49 | progressLaunch = (ProgressBar) findViewById(R.id.progress_launch);
50 | buttonLaunch = (Button) findViewById(R.id.button_launch);
51 | progressInit = (ProgressBar) findViewById(R.id.progress_init);
52 | buttonInit = (Button) findViewById(R.id.button_init);
53 | progressRestart = (ProgressBar) findViewById(R.id.progress_restart);
54 | buttonRestart = (Button) findViewById(R.id.button_restart);
55 | progressProgress = (ProgressBar) findViewById(R.id.progress_progress);
56 | buttonProgress = (Button) findViewById(R.id.button_progress);
57 | progressInput = (ProgressBar) findViewById(R.id.progress_input);
58 | buttonInput = (Button) findViewById(R.id.button_input);
59 | editInput = (EditText) findViewById(R.id.edit_input);
60 |
61 | // Start at launch
62 | loaderManager.create(
63 | SampleObservables.delay(),
64 | new RxLoaderObserver() {
65 | @Override
66 | public void onStarted() {
67 | progressLaunch.setVisibility(View.VISIBLE);
68 | buttonLaunch.setEnabled(false);
69 | }
70 |
71 | @Override
72 | public void onNext(String message) {
73 | progressLaunch.setVisibility(View.INVISIBLE);
74 | buttonLaunch.setEnabled(false);
75 | buttonLaunch.setText(message + " (launch)");
76 | }
77 | }
78 | ).start();
79 |
80 | // Init on button press
81 | final RxLoader initLoader = loaderManager.create(
82 | DELAY_TASK_INIT,
83 | SampleObservables.delay(),
84 | new RxLoaderObserver() {
85 | @Override
86 | public void onStarted() {
87 | progressInit.setVisibility(View.VISIBLE);
88 | buttonInit.setEnabled(false);
89 | }
90 |
91 | @Override
92 | public void onNext(String message) {
93 | progressInit.setVisibility(View.INVISIBLE);
94 | buttonInit.setEnabled(false);
95 | buttonInit.setText(message + " (init)");
96 | }
97 | }
98 | ).save(new SaveCallback() {
99 | @Override
100 | public void onSave(String key, String value, Bundle outState) {
101 | outState.putString(key, value);
102 | }
103 |
104 | @Override
105 | public String onRestore(String key, Bundle savedState) {
106 | return savedState.getString(key);
107 | }
108 | });
109 |
110 | buttonInit.setOnClickListener(new View.OnClickListener() {
111 | @Override
112 | public void onClick(View view) {
113 | initLoader.start();
114 | }
115 | });
116 |
117 | // Restart on button press
118 | final RxLoader restartLoader = loaderManager.create(
119 | DELAY_TASK_RESTART,
120 | SampleObservables.delay(),
121 | new RxLoaderObserver() {
122 | @Override
123 | public void onStarted() {
124 | progressRestart.setVisibility(View.VISIBLE);
125 | buttonRestart.setEnabled(false);
126 | buttonRestart.setText("Restart on Button Press");
127 | }
128 |
129 | @Override
130 | public void onNext(String message) {
131 | progressRestart.setVisibility(View.INVISIBLE);
132 | buttonRestart.setEnabled(true);
133 | buttonRestart.setText(message + " (restart)");
134 | }
135 | }
136 | );
137 |
138 | buttonRestart.setOnClickListener(new View.OnClickListener() {
139 | @Override
140 | public void onClick(View view) {
141 | restartLoader.restart();
142 | }
143 | });
144 |
145 | // Progress on button press
146 | final RxLoader progressLoader = loaderManager.create(
147 | PROGRESS_TASK,
148 | SampleObservables.count(),
149 | new RxLoaderObserver() {
150 | @Override
151 | public void onStarted() {
152 | buttonProgress.setEnabled(false);
153 | progressProgress.setProgress(0);
154 | progressProgress.setVisibility(View.VISIBLE);
155 | }
156 |
157 | @Override
158 | public void onNext(Long progress) {
159 | buttonProgress.setText("Progress Running (" + progress + ")");
160 | progressProgress.setProgress(progress.intValue());
161 | }
162 |
163 | @Override
164 | public void onCompleted() {
165 | buttonProgress.setText("Progress Complete! (restart)");
166 | buttonProgress.setEnabled(true);
167 | progressProgress.setVisibility(View.INVISIBLE);
168 | }
169 | }
170 | );
171 |
172 | buttonProgress.setOnClickListener(new View.OnClickListener() {
173 | @Override
174 | public void onClick(View v) {
175 | progressLoader.restart();
176 | }
177 | });
178 |
179 | // Button with input
180 | final RxLoader1 inputLoader = loaderManager.create(
181 | INPUT_TASK,
182 | SampleObservables.inputDelay(),
183 | new RxLoaderObserver() {
184 | @Override
185 | public void onStarted() {
186 | progressInput.setVisibility(View.VISIBLE);
187 | buttonInput.setEnabled(false);
188 | buttonInput.setText("Restart input on Button Press");
189 | }
190 |
191 | @Override
192 | public void onNext(String message) {
193 | progressInput.setVisibility(View.INVISIBLE);
194 | buttonInput.setEnabled(true);
195 | buttonInput.setText(message + " (restart)");
196 | }
197 | }
198 | );
199 |
200 | buttonInput.setOnClickListener(new View.OnClickListener() {
201 | @Override
202 | public void onClick(View v) {
203 | inputLoader.restart(editInput.getText().toString());
204 | }
205 | });
206 | }
207 | }
208 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## [Deprecated]
2 | There are better solutions out there, would recommend using [LiveData](https://developer.android.com/topic/libraries/architecture/livedata) or [AutoDispose](https://github.com/uber/AutoDispose) with rx.
3 |
4 | rxloader
5 | ========
6 | Asynchronous operations in Android are very hard to get right due to the
7 | Activity lifecycle. AsyncTasks don't handle any of it making them difficult to
8 | use. Loaders handle many things for you, but have a clunky api and fall down
9 | anytime you want to do anything more complex than loading data immediately when
10 | the Activity or Fragment is shown.
11 |
12 | This library builds upon [rxjava](https://github.com/Netflix/RxJava) to handle
13 | all these things for you with an easy-to-use api.
14 |
15 | Install
16 | -------
17 | ### Gradle
18 | ```groovy
19 | repositories {
20 | mavenCentral()
21 | }
22 |
23 | dependencies {
24 | implementation 'me.tatarka.rxloader:rxloader:1.1.0'
25 | }
26 | ```
27 |
28 | ### Maven
29 | ```xml
30 |
31 | me.tatarka.rxloader
32 | rxloader
33 | 1.1.0
34 |
35 | ```
36 |
37 | Usage
38 | -----
39 | The [sample app](https://github.com/evant/rxloader/blob/master/sample/src/main/java/me/tatarka/rxloader/sample/MainActivity.java)
40 | shows some common use cases of handling observables.
41 |
42 | Here is a simple example of loading data as soon as your activity starts.
43 | ```java
44 | public class MyActivity extends Activity {
45 | private RxLoaderManager loaderManager;
46 |
47 | public void onCreate(Bundle savedState) {
48 | // If you are using the support library,
49 | // use RxLoaderManagerCompat.get(this) instead.
50 | loaderManager = RxLoaderManager.get(this);
51 |
52 | loaderManager.create(
53 | asyncThatReturnsObservable(),
54 | new RxLoaderObserver() {
55 | @Override
56 | public void onStarted() {
57 | // Show your progress indicator.
58 | }
59 |
60 | @Override
61 | public void onNext(Result result) {
62 | // Hide your progress indicator and show the result.
63 | }
64 |
65 | @Override
66 | public void onError(Throwable error) {
67 | // Hide your progress indicator and show that there was an error.
68 | }
69 | }
70 | ).start(); // Make sure you call this to kick things off.
71 | }
72 | }
73 | ```
74 |
75 | Or in a fragment
76 | ```java
77 | public class MyFragment extends Fragment {
78 | private RxLoaderManager loaderManager;
79 |
80 | public void onViewCreated(View view, Bundle savedInstanceState) {
81 | // If you are using the support library,
82 | // use RxLoaderManagerCompat.get(this) instead.
83 | loaderManager = RxLoaderManager.get(this);
84 |
85 | loaderManager.create(
86 | asyncThatReturnsObservable(),
87 | new RxLoaderObserver() {
88 | @Override
89 | public void onStarted() {
90 | // Show your progress indicator.
91 | }
92 |
93 | @Override
94 | public void onNext(Result result) {
95 | // Hide your progress indicator and show the result.
96 | }
97 |
98 | @Override
99 | public void onError(Throwable error) {
100 | // Hide your progress indicator and show that there was an error.
101 | }
102 | }
103 | ).start(); // Make sure you call this to kick things off.
104 | }
105 | }
106 | ```
107 |
108 | All observer callbacks run on the UI thread. If the Activity or Fragment is
109 | destroyed, the callbacks will not be called. If there is a configuration change,
110 | the relevant callbacks will be called again.
111 |
112 | Here is an example of loading and reloading on a button press. Try doing this
113 | with loaders!
114 |
115 | ```java
116 | public class MyActivity extends Activity {
117 | private RxLoaderManager loaderManager;
118 |
119 | public void onCreate(Bundle savedState) {
120 | loaderManager = RxLoaderManager.get(this);
121 |
122 | final RxLoader myLoader = loaderManager.create(
123 | asyncThatReturnsObservable(),
124 | new RxLoaderObserver() {
125 | @Override
126 | public void onStarted() {
127 | // Show your progress indicator.
128 | }
129 |
130 | @Override
131 | public void onNext(Result result) {
132 | // Hide your progress indicator and show the result.
133 | }
134 |
135 | @Override
136 | public void onError(Throwable error) {
137 | // Hide your progress indicator and show that there was an error.
138 | }
139 | }
140 | );
141 |
142 | findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
143 | @Override
144 | public void onClick(View button) {
145 | myLoader.restart();
146 | }
147 | });
148 | }
149 | }
150 | ```
151 |
152 | Note that the loader is still created in `onCreate()` and not on the button
153 | callback. This is necessary to handle configuration changes properly if the
154 | button was pressed first.
155 |
156 | ### Passing arguments
157 | If you want to pass arguments to your observable, you can use one of the overloads that takes a `Func1>` or `Func2