Android manage multi request rxJava on rotation device

若如初见. 提交于 2019-12-05 03:28:54

Handling rotation is a cool challenge in Android. There're a few ways to do that.

1- Services: You can use a service and handle your network requests or other background operations in service. Also with Services, you'll seperate your business logic from ui.

2- Worker Fragment: Worker fragment is a fragment instance without a layout. You should set your worker fragment's retainInstanceState to true. So you'll save your fragment from orientation change and will not lose your background operations.

Why Worker Fragment? If you set retainInstanceState true to a fragment with layout, you'll leak views.

If you're using MVVM you can implement ViewModel as a Worker Fragment which as setRetainInstanceState = true

3- Global Singleton Data Source: You can create a global singleton data source class which handles your operations in an independent scope from Activity / Fragment lifecycle in your application.

4- Loaders: Loaders can recover state from orientation changes. You handle your operations with loaders but they are designed to load data from disk and are not well suited for long-running network requests.

Extra: You can use Path's Priority Job Queue to persist your jobs: https://github.com/path/android-priority-jobqueue

Edit: You can check my repo for handling device rotation without using Google's new architecture components. (As an example of Worker Fragment which i pointed in my answer.) https://github.com/savepopulation/bulk-action

You have the following options:

  • Use some global Singleton, or your Application class, that holds your logic, not within your Activity's lifecycle
  • Use a Service that runs next to your activity/application
  • Use a Loader

Global state is often bad and makes your code hard to test / debug. Services tend to be overkill.

For your use case of device rotation and continuing where one left off you'd usually use a Loader, which keeps running on rotation and only gets destroyed once you leave the activity.

I also recently wrote an article about one possible solution to use Loaders together with RxJava to keep state during orientation changes.

You can take advantage of Fragment#setRetainInstance(true). With that flag set, fragment is not destroyed after device rotation and can be used as an object container. Please look at this sample which also stores Observable - https://github.com/krpiotrek/RetainFragmentSample

you need to override

@Override
public void onSaveInstanceState(Bundle outState) {
  super.onSaveInstanceState(outState);
}

When device is rotated store data in bundle then inside on create check

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

if(savedInstanceState == null){
   //saved instance is null
}else{
    //get your stored values here
    counter = savedInstanceState.getInt("value",0); //here zero is the default value
 }
}

How I'm doing this is to have a singleton class (or any long living Object as explained by savepopulation earlier, but - the trick is to store the loaded data in a BehaviorSubject, and subscribe to that subject in the Activity instead of the original network request.

This way:

public class MyNetworkSingleton {
  // This static service survives orientation changes
  public static MyNetworkSingleton INSTANCE = new MyNetworkSingleton();

  private final BehaviorSubject<String> dataSubject = BehaviorSubject.create();

  public Observable<String> getData() {
     if (!dataSubject.hasValue()) {
        refreshData(); // No data is loaded yet, load initial data from network
     }

     return dataSubject;
  }

  public void refreshData() {
     someDataSourceCall().subscribe(new Observer<String>() {
       @Override
       public void onError(Throwable e) {
           // Remember, this point also needs error handling of some form,
           // e.g. propagating the error to the UI as a Toast
       }

       @Override
       public void onComplete() {

       }

       @Override
       public void onSubscribe(Disposable d) {

       }

       @Override
       public void onNext(String data) {
          dataSubject.onNext(data); // this refreshes the internally stored data
       }
     });
  }

  private Observable<String> someDataSourceCall() {
     return // some network request here etc. where you get your data from
  }
}

and then:

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ...
    Observer<String> myObserver = new Observer<String>() {
        @Override
        public void onError(Throwable e) {
            // Called when the observable encounters an error
        }

        @Override
        public void onComplete() {

        }

        @Override
        public void onSubscribe(Disposable d) {

        }

        @Override
        public void onNext(String s) {
            // Called each time the observable emits data
            Log.e("MY OBSERVER", s);
        }
    };

    MyNetworkSingleton.INSTANCE.getData().subscribe(myObserver);

    myRefreshButton.setOnClickListener(new Button.OnClickListener() {
        public void onClick(View v) {
            // refresh data from network only when button is pressed
            MyNetworkSingleton.INSTANCE.refreshData();
        }
    });
}

This way only first time you need the data from network it will be loaded, or when the user clicks a refresh button (myRefreshButton).

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!