NetworkOnMainThreadException 或 OnErrorNotImplementedException RxAndroid send/receive 数据 to/from 绑定服务

NetworkOnMainThreadException OR OnErrorNotImplementedException RxAndroid send/receive data to/from bound service

我正在使用此 answer 的更新版本来 link(绑定)一个 activity 到服务。

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()),但它产生了同样的错误。我也尝试在新线程上执行服务,但仍然出现同样的错误。

Service 中的第一个在主线程上运行

A service runs in the main thread of its hosting process; the service does not create its own thread and does not run in a separate process unless you specify otherwise. If your service is going to perform any CPU-intensive work or blocking operations, such as MP3 playback or networking, you should create a new thread within the service to complete that work. By using a separate thread, you can reduce the risk of Application Not Responding (ANR) errors, and the application's main thread can remain dedicated to user interaction with your activities. REFERENCE

因此,在所有情况下,直接在服务中进行 api 调用都会导致 NetworkOnMainThreadException

  1. 当你输入observeOn(AndroidSchedulers.mainThread())时,你一定会得到NetworkOnMainThreadException;上面指定的原因

  2. 当您输入 observeOn(Schedulers.newThread()) 时,服务中的 api 调用会导致 NetworkOnMainThreadException;但是由于您已经使用 Rx 它 returns 向其订阅者发送一条错误消息;但在你的情况下,你没有添加错误部分。

您使用过:

subscribe(responseCode -> updateUI());

为了防止应用程序崩溃,您必须使用

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

现在解决问题:

  1. 在服务中,确保在服务中的新线程上调用 API;

  1. 您还可以尝试使用对另一个 class(如 MVP 中的 Presenter)的引用来进行 API 调用,您可以在其中进行 API 调用并发送响应到 UI 直接使用 :

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