RxJava Android изменение ориентации и сетевой запрос - PullRequest
0 голосов
/ 04 июля 2018

Я новичок в RxJava, и мне нужно интегрировать его в существующий проект. Мне нужно провести рефакторинг существующего кода, добавив Observables для работы в сети (Socket IO).

В настоящее время, когда выполняется сетевой запрос (клиент -> сервер), к 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);
          }
      });
  }

}

Может вызываться с Activities и Fragments:

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

Приведенный выше код работает, но он действительно тесно связан с жизненным циклом пользовательского интерфейса и иногда вызывает утечку памяти, поскольку onDestroy() не вызывается (если вы переходите между действиями, а ОС Android убивает «приостановленные» действия из стека) или потому что анонимные внутренние классы (обратные вызовы), которые содержат ссылку на пользовательский интерфейс, и отныне мне нужно поддерживать изменение ориентации .

Это код, который я реализовал, используя RxJava:

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

Вот как он вызывается из интерфейса:

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

Вышеуказанное RxJava работает, однако, если происходит изменение ориентации, данные не возвращаются, поскольку Observer отписано.

  1. Корректна ли вышеприведенная реализация?
  2. Как мне подписаться, не выполняя запрос? Подпишитесь и ждите изменения данных.

Другой альтернативой будет EventBus , но это всего лишь план B. EventBus точно соответствует моим требованиям, подписывайтесь и ждите изменения данных, но я хочу исключить шаблон.

Я читал другие статьи, используя Fragment setRetainInstance(true), но что, если мне нужно использовать его с Activity? Что если я не хочу сохранять состояние Fragment? Люди предлагали использовать архитектуру MVVM или MVP, но у меня нет времени на рефакторинг всего проекта.

Ответы [ 3 ]

0 голосов
/ 04 июля 2018

Я предложу вам перейти на MVVM. С вашим представленным кодом это не так сложно. Вот пример кода, как это будет выглядеть

Ваш 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
    }
}

Ваша деятельность

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
        });
    }
}
0 голосов
/ 05 июля 2018

Вам необходимо учитывать логический объем ваших сетевых запросов, и это совершенно не зависит от того, используете ли вы RxJava. Фоновые задачи, такие как сетевые запросы, должны принадлежать компоненту Android (Application, Activity и т. Д.) С соответствующим временем жизни. Обычный способ заставить фоновые задачи с заданной активностью пережить изменение конфигурации - разместить их в сохраненном фрагменте . Вы бы все равно сделали это, если бы использовали RxJava.

ОС Android убивает «приостановленные» действия из стека

Этого не произойдет, если что-то не изменилось в Android 8 или новее. Документация предполагает, что фреймворк может уничтожать отдельные действия в backstack, но в настоящее время он уничтожает всю задачу только в фоновом режиме. Ваше приложение корректно и рассчитано на будущее, если и только если оно работает с опцией разработчика «Не держать действия».

0 голосов
/ 04 июля 2018

Если вы не хотите обрабатывать изменения конфигурации и хотите кэшировать данные из наблюдаемых, вы можете использовать BehaviorSubjects и горячую наблюдаемую. Это позволит вам получить самый последний элемент, опубликованный наблюдаемой.

Кроме этого, я предлагаю вам использовать ViewModel из компонентов архитектуры. Это позволит вам создать компонент, который связан с действием, но не будет затронут жизненным циклом (за исключением завершения, очевидно). Удивительно, но ViewModelProviders реализованы как фрагменты с setRetainInstance(true). Вам не нужно полностью проводить рефакторинг всего приложения. Просто переместите те, которые вы хотите сохранить во время изменений конфигурации.

...