NetworkOnMainThreadException ИЛИ OnErrorNotImplementedException RxAndroid отправлять / получать данные в / из связанной службы - PullRequest
0 голосов
/ 07 марта 2019

Я использую обновленную версию этого ответа , чтобы связать (привязать) действие с услугой.

MyService.java

public class MyService extends Service {

   private LocalBinder binder = new LocalBinder();
   private Observable<Integer> responseObservable;
   private ObservableEmitter<Integer> responseObserver;

   public static boolean isRunning = false;

   @Nullable
   @Override
   public IBinder onBind(Intent intent) {
      return binder;
   }

   @Override
   public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
       GsonConverterFactory factory = GsonConverterFactory.create(new GsonBuilder()
            .setLenient()
            .create());

      HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
      interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);

      Client client = new Retrofit.Builder()
            .baseUrl(BASE_URL)
            .client(new OkHttpClient.Builder()
                 .addInterceptor(interceptor)
                 .build())
            .addConverterFactory(factory)
            .build()
            .create(Client.class);

      for (//some loop) {
          Response<Result> response = client.search(//some params here)
                           .execute();
          responseObserver.onNext(response.code());
      }
      return START_NOT_STICKY;
   }

   @Override
   public void onDestroy() {
      super.onDestroy();
      isRunning = false;
   }

   public Observable<Message> observeResponse() {
      if (responseObservable == null) {
         responseObservable = Observable.create(em -> responseObserver = em);
         responseObservable = responseObservable.share();
      }
      return responseObservable;
   }


   public class LocalBinder extends Binder {

      public DService getService() {
         return MyService.this;
      }
   }

}

MainActivity.java

public class MainActivityextends AppCompatActivity {

   private MyService service;
   private Disposable disposable;

   private ServiceConnection serviceConnection = new ServiceConnection() {
      @Override
      public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
         service = ((MyService.LocalBinder) iBinder).getService();
         disposable = service.observeResponse()
               .observeOn(Schedulers.newThread())
               .subscribe(responseCode -> updateUI()); //this must run on main thread
         startService(new Intent(MainActivity.this, MyService.class));
      }

      @Override
      public void onServiceDisconnected(ComponentName componentName) {
      }
   };

   @Override
   protected void onDestroy() {
      if (disposable != null)
         disposable.dispose();
      unbindService(serviceConnection);
      super.onDestroy();
   }

   @Override
   protected void onCreate(Bundle savedInstanceState) {
       //....

       Button start = findViewById(R.id.start);
       start.setOnClickListener(v -> {
           Intent intent = new Intent(this, MyService.class);
           bindService(intent, serviceConnection, BIND_AUTO_CREATE);
       });

       //....
   }
}

Если я использую observeOn(AndroidSchedulers.mainThread()), я получаю NetworkOnMainThreadException, а если я использую observeOn(Schedulers.newThread()), я получаю OnErrorNotImplementedException: Only the original thread that created a view hierarchy can touch its views.

Я знаю, что означают обе ошибки, в обычных случаях я могу легко их устранить, но здесь я ничего не делаю.

Мне нужно, чтобы сетевые запросы в службе выполнялись синхронно, потому что она находится вцикл, и я обрабатываю каждый результат запроса по порядку, асинхронные вызовы не вариант для меня.

Я пытался runOnUiThread(() -> updateUI()), но он выдает ту же ошибку.Я также пытался запустить службу в новом потоке, но все еще оставалась та же ошибка.

1 Ответ

1 голос
/ 07 марта 2019

First из Service работает в основном потоке

Сервис работает в главном потоке своего хостинга; сервис не создает свой собственный поток и не запускается в отдельном процессе если не указано иное. Если ваш сервис будет выполнять какие-либо Работа с интенсивным использованием процессора или операции блокировки, такие как воспроизведение MP3 или сеть, вы должны создать новый поток внутри службы, чтобы завершить эту работу. Используя отдельный поток, вы можете уменьшить риск ошибок приложения (ANR) и ошибок приложения Основной поток может оставаться посвященным взаимодействию пользователя с вашим деятельность. ССЫЛКА

Таким образом, выполнение API-вызовов в Сервисе напрямую вызовет NetworkOnMainThreadException во всех случаях.

  1. Когда вы ставите observeOn(AndroidSchedulers.mainThread()), вы обязательно должны иметь NetworkOnMainThreadException; указанная выше причина

  2. Когда вы вводите observeOn(Schedulers.newThread()), вызов API в сервисе вызывает NetworkOnMainThreadException; но так как вы использовали Rx, он возвращает сообщение об ошибке своему подписчику; но в вашем случае вы не добавили ошибочную часть.

Вы использовали:

subscribe(responseCode -> updateUI());

, чтобы предотвратить сбой приложения, вы должны использовать

subscribe(responseCode -> updateUI(), error -> error.printStackTrace());

Теперь, чтобы решить проблему:

  1. В сервисе, обязательно вызовите API в новом потоке в Сервисе;

OR

  1. Вы также можете попытаться сделать вызов API, используя ссылку на другой класс (например, Presenter в MVP), где вы делаете вызовы API и отправляете ответ в пользовательский интерфейс напрямую, используя:

       service.observeResponse()
           .subscribeOn(Schedulers.io())
           .observeOn(AndroidSchedulers.mainThread())
           .subscribe(responseCode -> view.updateUI(), error -> view.displayError())
    
...