安卓如何在旋转设备时处理多个请求,使用RxJava管理。

9

我正在使用android应用的MVVM架构,我想在设备旋转时管理请求和rxJava,请问如何在设备旋转后禁止请求并从上一次请求继续执行?

这是我的简单代码,但我找不到任何关于它的文档和示例代码。

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

    binding = DataBindingUtil.setContentView(this, R.layout.activity_register);
    ...
    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);
        }
    };

    Observable.just("Hello").subscribe(myObserver);
}

我正在使用最新版本的rxJava
5个回答

3
处理Android设备旋转是一个很酷的挑战。有几种方法可以处理它。
1- 服务: 您可以使用服务并在服务中处理网络请求或其他后台操作。此外,使用服务,您可以将业务逻辑与UI分离。
2- 工作片段: 工作片段是一个没有布局的片段实例。您应该将工作片段的retainInstanceState设置为true。这样,您就可以保存片段以防止方向更改,并且不会丢失后台操作。
为什么要使用工作片段?如果您将具有布局的片段的retainInstanceState设置为true,则会泄漏视图。
如果您正在使用MVVM,可以将ViewModel实现为Worker Fragment,其中setRetainInstanceState = true。
3- 全局单例数据源:您可以创建一个全局单例数据源类,在应用程序中独立于Activity / Fragment生命周期处理操作。
4- 加载器:加载器可以从方向更改中恢复状态。您可以使用加载器处理操作,但它们设计用于从磁盘加载数据,并不适合长时间运行的网络请求。
额外提示:您可以使用Path的Priority Job Queue来保留您的作业:https://github.com/path/android-priority-jobqueue 编辑:您可以查看我的存储库,以处理设备旋转而不使用Google的新架构组件。(例如我在答案中指出的Worker Fragment的示例。)https://github.com/savepopulation/bulk-action

在不点击任何按钮的情况下向服务器发出请求会导致多次请求,因为在旋转设备时我们没有任何处理方式。你能帮我通过Loaders恢复方向更改的状态吗?请提供示例代码以了解如何实现这一点。 - tux-world
1
我想补充一点,选择第三个选项时,全局单例不是必须的。数据源实例也可以通过依赖注入传递。 - ULazdins
这并没有回答问题。它明确说明了如何在旋转设备/重新订阅可观察对象时不重新触发请求的方法。 - User

1
您有以下选项:
  • 使用一些全局的Singleton或者您的Application类来保存逻辑,而不是在Activity的生命周期内部保存。
  • 使用一个运行在您的Activity/应用程序旁边的Service。
  • 使用一个Loader。

全局状态通常是不好的,会使您的代码难以测试/调试。服务往往过于臃肿。

对于设备旋转和继续上次操作的用例,通常会使用 Loader,它会在旋转时持续运行,并且只有在退出活动时才会被销毁。

我最近还写了一篇关于如何将Loader与RxJava结合使用来在方向更改期间保持状态的文章。


谢谢您,先生。请让我检查一下您的帖子并尝试使用加载器。 - tux-world
我该如何在这段代码Observable.Transformer中导入Transformer - tux-world

0
你可以利用Fragment#setRetainInstance(true)来获取好处。有了这个标记,片段在设备旋转后不会被销毁,可以用作对象容器。请查看这个样例,它还存储了Observable - https://github.com/krpiotrek/RetainFragmentSample

那是活动而不是片段。 - tux-world
这不会改变任何东西 - 您只需使用片段作为容器。请参阅示例 - 它适用于 Activity,就像您的情况一样。 - krp
问题是我没有任何片段,先生,而且我不想使用片段来制造更困难的问题或解决这个问题。 - tux-world

0

你需要重写

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

当设备旋转时,将数据存储在Bundle中,然后在onCreate方法中进行检查。

@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
 }
}

请求服务器并在手机旋转时保持状态,直到收到响应,您怎么看? - tux-world
你可以在Bundle中添加一个布尔值isLoading,并在onCreate方法中进行检查。 - amodkanthe

0

我是通过创建一个单例类(或者像 savepopulation 之前所解释的任何长时间存活的对象)来实现这一点的。但是,关键是将已加载的数据存储在一个 BehaviorSubject 中,并在 Activity 中订阅该主题而不是原始网络请求。

这样做的好处是:

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
  }
}

然后:

@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();
        }
    });
}

这种方式只有在第一次需要从网络获取数据时才会加载,或者当用户点击刷新按钮 (myRefreshButton) 时。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接