【问题标题】:Android/RxJava, prevent multiple subscriptions of the same kindAndroid/RxJava,防止同类型的多个订阅
【发布时间】:2017-05-07 03:11:29
【问题描述】:

早上好,我最近开始玩 RxJava,但遇到了一个问题。

我在 ViewPager 中有一个 Fragment,它在 onViewCreated() 上启动一个 HTTP 请求(由 Retrofit 和 RxJava 处理)以检索字符串列表。 它的实现方式是创建订阅并将其添加到 CompositeSubscription 对象

这里是代码

final Subscription subscription = MyManager.getService().getListStringsFromWeb()
            .subscribeOn(Schedulers.newThread())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(new Observer<List<String>() {
                @Override
                public void onCompleted() {
                    // DO NOTHING
                }

                @Override
                public void onError(Throwable e) {
                    // DO SOMETHING
                }

                @Override
                public void onNext(List<String> result) {
                    returnResultToView(result);
                }
            });
addSubscription(subscription);

在 onViewCreated() 的 Fragment 中,我检查结果是否存在,如果没有我们调用上面的方法,如果有我们很高兴。

问题是当我旋转设备时,“第一个”订阅尚未返回,因此片段不会记住数据,它会启动另一个搜索,如果我继续旋转我继续添加订阅

我想知道是否有办法保留第一个订阅,当它的结果到达时,只需将其返回给 Fragment 并忽略所有其他订阅

一步一步想要的行为:

  • 启动片段和请求
  • 屏幕显示加载图像和启动订阅/请求的背景
  • 自第一个请求仍处于挂起状态以来,多次屏幕旋转保持加载图像
  • 第一次调用的结果返回后,将其返回到片段,从而隐藏加载图像并显示结果

非常感谢

【问题讨论】:

    标签: android rx-java observable rx-android subscriptions


    【解决方案1】:

    关于 Fragments 和 RxJava 的不幸之处在于,您必须以某种方式保持可观察性。一种方法是使它们静态到Fragment,这样您只创建一次,然后在onStart()/onStop() 中订阅/取消订阅,或者您决定。

    如果您不喜欢制作静态变量,另一种方法是使用保留的片段来处理数据。数据的视图Fragment 然后可以将自己“绑定”到数据片段。这种方法的优点是数据很容易被 Activity 的所有组件访问,它从视图中抽象出数据以便于测试,并且比生命周期组件更长寿。

    因此,对于保留的片段,您可以执行以下操作:

    public class DataFragment extends Fragment {
    
       private Observable<List<String> stringObs;
    
       @Override
       public void onAttach(Context ctx) {
          super.onAttach(ctx);
          setRetainInstance(true);
       }
    
       @Override
       public void onCreateView(LayoutInflater inflater, ViewGroup group, Bundle savedInsatnceState) {
          return null; // no view, no problem.
       }
    
       public Observable<List<String>> getStrings() {
         if (stringsObs == null) {
             stringsObs = MyManager.getService()
                .getListStringsFromWeb()
                .cache()
                .subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread());
         }
         return stringsObs;
       }
    }
    

    像这样将DataFragment 添加到 FragmentManager:

    FragmentManager fm = getFragmentManager();
    DataFragment df = fm.findFragmentByTag("DataFragment");
    if (df == null) {
       // Only add the retained fragment if it doesn't exist.  Otherwise, use the old one.
       fm.beginTransaction()
       .add(new DataFragment(), "DataFragment")
       .commit();
    }
    

    然后你可以像这样在 ViewFragment 上订阅它:

    public class ViewFragment extends Fragment {
    
       private Subscription s;
    
       @Override
       public void onStart() {
         super.onStart();
         DataFragment dataFragment = (DataFragment) getFragmentManager().findFragmentByTag("DataFragment");
         s = dataFragment.getStrings()
                .subscribe(new Observer<List<String>() {
                  // Do things
                });
       }
    
       @Override
       public void onStop() {
          super.onStop();
          s.unsubscribe();
       }
    }
    

    现在,您的 ViewFragment 可以随心所欲地消失和重新出现,而不会妨碍下载。以您需要的方式操作 Observable 也更容易。

    【讨论】:

    • 是的,我记得来自 Alex Lockwood 和其他人的一篇关于通过 setRetainInstance() 保留片段状态的帖子,以便与一粒盐一起使用,并且仅适用于以下片段不会持有 UI 组件 拥有静态 Observable 的缺点是什么?那么难道没有办法以 RxJava 的方式缓冲或忽略订阅吗?
    • 您没有为保留的片段提供 UI。它存在的原因是这样的。
    • 静态变量将在应用程序的整个生命周期中持续存在,因此即使您不再需要 Fragment,它也可以存在于内存中。它(在我看来)看起来更丑陋。更难测试。如果您愿意,它还会限制您扩展类的能力。
    • 我目前正在使用 MVP 模式,演示者根据生命周期处理订阅创建/取消,但即使我保留相同的 Observable 缓存它,每次都会订阅一个新的订阅,这并不能解决旋转设备时一直显示加载屏幕的问题
    • 加载屏幕将显示在 Fragment 的开头,然后在订阅中删除。当订阅缓存的 Observable 时,它​​几乎会立即获取数据。因此,结果将是一个进度对话框,在它获取数据之前始终处于打开状态。我不确定您对缓存的反对意见是什么。另一种方法是保留的片段可以保留数据本身,而不是依赖 RxJava 缓存。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-27
    • 1970-01-01
    • 2019-07-18
    • 1970-01-01
    • 2020-10-08
    相关资源
    最近更新 更多