简体   繁体   English

RxJava Android方向更改和网络请求

[英]RxJava Android orientation change and network request

I'm new to RxJava and I need to integrate it into an existing project. 我是RxJava ,我需要将其集成到现有项目中。 I need to refactor the existing code, adding Observables for networking (Socket IO). 我需要重构现有代码,添加用于网络的Observables (套接字IO)。

Currently when a network request is made (client -> server) a callback (interface) is added to a HashMap and once the request is completed, it will deliver the data back to the caller: 当前,当发出网络请求(客户端->服务器)时,会将回调(接口)添加到HashMap并且一旦请求完成,它将把数据传递回调用方:

// Singleton
public class API {

   public void checkTicket(String ticketId, final String networkRequestId, Callback callback) {
      // Add the callback to the hashmap
      registerCallback(networkRequestId, callback);

      JSONObject json = RequestFactory.createTicketCheckerRequest(ticketId);

      // Make the network request
      getSocket().checkTicket(json, new Callback() {
          @Override
          public void onRequestDone(Response response) {

              // Retrieve the callback
              callback = getCallback(networkRequestId);

              // Don't keep reference, remove from hashmap
              unsubscribeCallback(networkRequestId);

              // Check if it's unsuccessful and build the corresponding error response
              if (!response.isSuccess()) {
                  // build custom error response
                  response = ResponseFactory.buildError(response);
              }

              // Deliver response from server
              callback.onRequestDone(response);
          }
      });
  }

}

It can be called from Activities and Fragments : 可以从ActivitiesFragments调用它:

private void checkTicket() {
   String ticketId = editText.getText().toString();

   API.getInstance().checkTicket(ticketId, REQUEST_ID_CHECK_TICKET, new Callback() {
      @Override
      protected void onRequestDone(Response response) {
         textView.setText(response.getData());
      }
   });
}

@Override
public void onDestroy() {
   super.onDestroy();

   // Removes callback from HashMap in case of the UI is destroyed before the arrives
   API.getInstance().unsubscribe(REQUEST_ID_CHECK_TICKET);
}

The above code works but it's really tight with the UI's lifecycle and sometimes it's causing memory leak, because onDestroy() is not getting called (if you navigate between activities and Android OS kills the "paused" activities from the stack) or because the anonymous inner classes (callbacks) which are holding a reference to the UI, and from now on I need to support orientation change . 上面的代码有效,但是它确实与UI的生命周期紧密相关,有时会导致内存泄漏,因为未调用onDestroy() (如果您在活动之间导航,并且A​​ndroid OS杀死了堆栈中的“已暂停”活动),或者因为匿名内部类(回调),其中包含对UI的引用,从现在开始, 我需要支持方向更改

This is the code that I have implemented using RxJava : 这是我使用RxJava实现的代码:

API : API

public Observable<Response> checkTicket(String ticketId) {
   return Observable.create(subscriber -> {
      JSONObject json = RequestFactory.createTicketCheckerRequest(ticketId);

      // Make the network request
      getSocket().checkTicket(json, new Callback() {
         @Override
         public void onRequestDone(Response response) {
            subscriber.onNext(response);
            subscriber.onComplete();
         }
      });
   });
}

This is how it's called from the UI: 这是从用户界面调用的方式:

private CompositeDisposable mDisposables = new CompositeDisposable();

private void checkTicket() {
   //////

   Disposable disposable = API.getInstance().checkTicket(ticketId)
      .observeOn(AndroidSchedulers.mainThread())
      .subscribeOn(Schedulers.io())
      .subscribe(result -> {
         textView.setText(result.getData());
      });

   mDisposables.add(disposable);
}

@Override
public void onStop() {
   super.onStop();

   if (!mDisposables.isDisposed()) {
      mDisposables.dispose();
   }
}

The above RxJava is working, however if an orientation change occurs the data is not returned because the Observer is unsubscribed. 上面的RxJava工作正常,但是,如果发生方向更改,则由于取消订阅Observer因此不会返回数据。

  1. Is the above implementation correct? 上面的实现正确吗?
  2. How should I subscribe without executing the request? 我应该如何订阅而不执行请求? Subscribe and wait for data change. 订阅并等待数据更改。

Another alternative would be EventBus but this is just Plan B. EventBus fits exactly my requirements, subscribe and wait for data change, but I want to evict boilerplate. 另一个选择是EventBus,但这只是PlanB。EventBus完全符合我的要求,订阅并等待数据更改,但我想逐出样板。

I have read other articles by using Fragment 's setRetainInstance(true) but what if I need to use it from an Activity ? 我已经通过使用FragmentsetRetainInstance(true)阅读了其他文章,但是如果我需要在Activity使用它,该怎么办? What if I don't want to retain the state of the Fragment ? 如果我不想保留Fragment的状态怎么办? People suggested to use MVVM or MVP architecture, but I don't have the time to refactor the entire project. 人们建议使用MVVMMVP架构,但是我没有时间重构整个项目。

I will suggest you move to MVVM. 我建议您转到MVVM。 With your presented code, it is not that hard. 使用您提供的代码,这并不难。 Here is a sample code of how it will look like 这是一个看起来像的示例代码

Your ModelView 您的ModelView

public class MyViewModel extends ViewModel {
    private CompositeDisposable mDisposables = new CompositeDisposable();
    private MutableLiveData<Response> response;

    public LiveData<Response> getResponse() {
        if (response == null) {
            response = new MutableLiveData<Response>();
            loadData();
        }
        return response;
    }

    private void loadData() {
        Disposable disposable = API.getInstance().checkTicket(ticketId)
          .observeOn(AndroidSchedulers.mainThread())
          .subscribeOn(Schedulers.io())
          .subscribe(result -> {
             response.postValue(result.getData());
          });

       mDisposables.add(disposable);
    }

    void onCleared()
    {
        super.onCleared();
        mDisposables.clear(); //no more leaks. It takes care of lifecycle for you
    }
}

Your Activity 您的活动

public class MyActivity extends AppCompatActivity {
    public void onCreate(Bundle savedInstanceState) {

        MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
        model.getResponse().observe(this, response -> {
            // update UI
            textView.setText(response); //response = Response object from Live data
        });
    }
}

If you don't want to handle configuration changes and want to cache data from observables, you can use BehaviorSubjects and a hot observable. 如果您不想处理配置更改,并且不想从可观察对象缓存数据,则可以使用BehaviorSubjects和热可观察对象。 This will allow you to get the most recent item that the observable published. 这将使您获得可观察到的最新出版物。

Other than that, I suggest you use the ViewModel from the architecture components. 除此之外,我建议您使用架构组件中的ViewModel It will allow you to create a component that is bound to the activity but will not be affected by the lifecycle (except termination, obviously). 它将允许您创建绑定到活动但不受生命周期影响的组件(显然,终止除外)。 Surprisingly enough, ViewModelProviders are implemented as fragments with setRetainInstance(true) . 令人惊讶的是,ViewModelProviders通过setRetainInstance(true)实现为片段。 You don't have to completely refactor the entire app. 您不必完全重构整个应用程序。 Just move the ones that you want to preserve during configuration changes. 只需在配置更改期间移动您要保留的内容即可。

You need to consider the logical scope of your network requests, and this is entirely separate from whether you're using RxJava. 您需要考虑网络请求的逻辑范围,这与是否使用RxJava完全分开。 Background tasks like network requests need to be owned by an Android component ( Application , Activity , etc.) with the appropriate lifetime. 诸如网络请求之类的后台任务需要由具有适当生命周期的Android组件( ApplicationActivity等)拥有。 The usual way to make activity-scoped background tasks survive a config change is to host them in a retained fragment . 使活动范围的后台任务在配置更改后仍然有效的通常方法是将它们托管在保留的片段中 You would still do that if you were using RxJava. 如果您使用的是RxJava,您仍然会这样做。

Android OS kills the "paused" activities from the stack Android OS杀死堆栈中的“暂停”活动

This doesn't happen unless something has changed in Android 8 or newer. 除非在Android 8或更高版本中进行了某些更改,否则不会发生这种情况。 The documentation suggests that the framework could destroy individual activities in the backstack, but currently it only destroys the entire task when it's in the background. 该文档建议该框架可以销毁后台堆栈中的单个活动,但是当前仅在后台运行时销毁整个任务。 Your app is correct and future-proof if and only if it works with the "don't keep activities" developer option on. 您的应用程序只有在启用了“不要继续进行活动”开发人员选项的情况下,才是正确且面向未来的应用程序。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM