我在其中一项活动中遇到了一个奇怪的问题。当从拍摄照片/视频回来时,在我的onActivityResult
中,我正在显示一个对话框,让用户为相机命名。一旦用户按下OK,我将onNext()
发送给具有复制文件的请求文件名的主题(并显示进度对话框)。
由于某种原因,即使我调用map()
,也会在主线程上调用执行副本的subscribeOn(Schedulers.io())
函数。
@Override
protected void onActivityResult(final int requestCode, int resultCode, Intent intent) {
...
final PublishSubject<String> subject = PublishSubject.create();`
mSubscription = subject
.subscribeOn(Schedulers.io())
.map(new Func1<String, String>() {
@Override
public String call(String fileName) {
Log.I.d(TAG,"map");
return doSomeIOHeavyFuncition();
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<String>() {
@Override
public void call(final String fullPath) {
Log.d(TAG,"onNext");
doSomethingOnUI(fullPath);
subject.onCompleted();
}
}, new Action1<Throwable>() {
@Override
public void call(Throwable throwable) {
...
}
}, new Action0() {
@Override
public void call() {
...
}
});
final AlertDialog dialog = new AlertDialog.Builder
....
.create()
.show();
dialog.getButton(DialogInterface.BUTTON_POSITIVE)
.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String someString = getStringFromDialog(dialog);
dialog.dismiss();
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(input.getWindowToken(), 0);
showProgressDialog();
subject.onNext(someString);
}
});
}
改变subscribeOn(Schedulers.io())
调用observeOn(Schedulers.io())
解决了这个问题。我仍然想知道为什么它不起作用......
subscribeOn
和observeOn
是最混乱的运营商。前者确保订阅副作用发生在指定的调度程序(线程)上,但这并不意味着值也将弹出该线程。
例如,如果您的Observer在订阅它时打开网络连接,您不希望它在主线程上运行,因此,您需要subscribeOn来指定该订阅的位置以及因此将创建网络连接。
当数据最终到达时,发送线程可以是任何东西,调度程序之一或后台普通旧线程。由于我们不知道或不喜欢该线程,我们希望将数据的观察移动到另一个线程。这就是observeOn的作用:确保后面的运算符将在指定的调度程序上执行onNext逻辑。 Android开发人员已经使用它将值的观察结果移回主线程。
但是很少解释的是当你想要在最终结果再次落在主线程上之前在主线程上进行一些额外的计算时会发生什么:使用多个observeOn
运算符:
source
.observeOn(Schedulers.computation())
.map(v -> heavyCalculation(v))
.observeOn(Schedulers.io())
.doOnNext(v -> { saveToDB(v); })
.observeOn(AndroidSchedulers.mainThread())
...