【问题标题】:NetworkOnMainThreadException OR OnErrorNotImplementedException RxAndroid send/receive data to/from bound serviceNetworkOnMainThreadException 或 OnErrorNotImplementedException RxAndroid 向绑定服务发送/接收数据
【发布时间】:2019-07-29 09:09:54
【问题描述】:

我正在使用此answer 的更新版本将活动链接(绑定)到服务。

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(() -&gt; updateUI()),但它产生了同样的错误。我也尝试在新线程上执行服务,但还是同样的错误。

【问题讨论】:

    标签: android retrofit rx-android synchronous okhttp3


    【解决方案1】:

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

    服务在其宿主进程的主线程中运行;服务 不创建自己的线程,也不在单独的进程中运行 除非您另有说明。如果您的服务将执行任何 CPU 密集型工作或阻塞操作,例如 MP3 播放或 网络,您应该在服务中创建一个新线程以 完成这项工作。通过使用单独的线程,您可以减少 应用程序无响应 (ANR) 错误的风险,以及应用程序的 主线程可以保持专用于用户与您的交互 活动。 REFERENCE

    因此,在任何情况下,直接在 Service 中进行 api 调用都会导致 NetworkOnMainThreadException

    1. 当您输入observeOn(AndroidSchedulers.mainThread()) 时,您肯定会拥有NetworkOnMainThreadException;上述原因

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

    您使用过:

    subscribe(responseCode -&gt; updateUI());

    为了防止应用崩溃,你必须使用

    subscribe(responseCode -&gt; updateUI(), error -&gt; error.printStackTrace());

    现在解决问题:

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

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

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

    【讨论】:

    • 我记得尝试从新线程调用 api,但它不起作用。现在它正在工作,也许我做错了什么。还是谢谢你:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多