我正在使用此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(() -> updateUI())
,但它会产生相同的错误。我还尝试在新线程上执行服务,但仍然是相同的错误。
答案 0 :(得分:1)
Service
中的第一个在主线程上运行
服务在其托管过程的主线程中运行;服务 不创建自己的线程,也不在单独的进程中运行 除非您另外指定。如果您的服务要执行任何 CPU密集型工作或阻止操作,例如MP3播放或 网络,您应该在服务中创建一个新线程来 完成这项工作。通过使用单独的线程,您可以减少 应用程序无响应(ANR)错误的风险,以及应用程序的 主线程可以保持专用于用户与您的交互 活动。 REFERENCE
因此,在所有情况下直接在Service中进行api调用都会导致NetworkOnMainThreadException
。
放置observeOn(AndroidSchedulers.mainThread())
时,您肯定会拥有NetworkOnMainThreadException
;上面指定的原因
当您放置observeOn(Schedulers.newThread())
时,服务中的api调用会导致NetworkOnMainThreadException
;但由于您使用过Rx
,因此会向其订阅者返回一条错误消息;但是对于您而言,您尚未添加错误部分。
您使用过:
subscribe(responseCode -> updateUI());
为防止应用崩溃,您必须使用
subscribe(responseCode -> updateUI(), error -> error.printStackTrace());
现在要解决此问题:
OR
您还可以尝试通过引用另一个类(例如MVP中的Presenter)来进行API调用,在该类中您可以通过以下方式进行API调用并将响应直接发送到UI:
service.observeResponse()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(responseCode -> view.updateUI(), error -> view.displayError())