在 onDoNext 和 doOnCompleted 方法中难以设置视图

Difficulty setting view in onDoNext and doOnCompleted methods

我正在学习 RxAndroid,并从我项目中的一个非常简单的示例开始。即获取用户个人资料图像 URL 可观察到的响应。

下面是获取字符串URL值的代码,我用Picasso加载图片URL。当我使用 运行onUiThread 加载图像时代码工作正常,但是当我不使用它时它会抛出错误;它说方法调用应该在主线程中。

总是在后台线程上执行 doOnNext 和 doOnCompleted 运行?

picUrlSubscription = getUrlAsObservable()
                         .map(responseBodyResponse -> {
                             Log.d(TAG, "map ");
                             try {
                                 if (responseBodyResponse.isSuccessful()) {
                                     observableStr = responseBodyResponse.body().string();
                                     return observableStr;
                                 } else {
                                     observableStr = "Bad_url";
                                     return observableStr;
                                 }

                             } catch (IOException e) {
                                 e.printStackTrace();
                             }
                             return null;
                         })
                         .doOnNext(s -> {
                             Log.d(TAG, "next ");
                             try {
                                 if (s != null && !s.equalsIgnoreCase("Bad_url")) {
                                     observableStr = new JSONObject(s).getString("url");

                                 } else
                                     runOnUiThread(() -> profileCircle.setImageResource(R.drawable.profil));

                             } catch (JSONException e) {
                                 e.printStackTrace();
                                 runOnUiThread(() -> profileCircle.setImageResource(R.drawable.profil));
                             }
                         })
                         .doOnCompleted(() -> {
                             Log.d(TAG, "completed ");
                             runOnUiThread(() -> Picasso.with(this).load(observableStr).into(profileCircle));
                         })
                         .onErrorReturn(throwable -> {
                             Log.d(TAG, "error "+ throwable.getMessage());
                             observableStr = "bad_url";
                             runOnUiThread(() -> profileCircle.setImageResource(R.drawable.profil));
                             return observableStr;
                         })
                         .subscribeOn(Schedulers.io())
                         .observeOn(AndroidSchedulers.mainThread())
                         .subscribe();

否,doOnNext()doOnCompleted() 运行 在指定的线程上通知其观察者。如果未指定线程,它们将 运行 在 observable 正在运行的同一线程上。

由于您指定了 subscribeOn(Schedulers.io()),可观察对象将在 io 线程上运行。根据 documentation; subscribeOn() 运算符指定可观察对象将在哪个线程上运行,无论它出现在链中的哪个位置,这与 observeOn() 运算符不同:

ObserveOn, on the other hand, affects the thread that the Observable will use below where that operator appears.

因此,将您的 observeOn() 调用 放在 您的 doOnNext()doOnCompleted() 调用之前。

虽然这个问题得到了回答,但就 RX 实践而言,我发现此代码存在许多问题。

  1. 主要问题是错误处理 - 如果可能的话,您应该避免在运算符内部处理异常,而应该在订阅者中进行。在下面的代码中,我通过始终在订阅者的 onError 部分设置配置文件默认图像来从您的代码中删除了很多重复项。

  2. 我还鼓励您避免使用 doOnNext / doOnCompleted / doOnError - 如果您想添加一些副作用,这些运算符很好您的 RX 链(例如,调用您的分析跟踪)——而是使用订阅者——它的用途是什么。在订阅者中进行错误处理尤为重要 - 如果出现您没有想到的错误,它将防止您的应用程序崩溃。

  3. 运算符跳线。如果您希望在不同的线程上执行操作,请使用 subscribeOn/observeOn

这是包含我的建议的代码:

    picUrlSubscription = getUrlAsObservable()
            .map(responseBodyResponse -> {
                String jsonString = null;
                if (responseBodyResponse.isSuccessful()) {
                    try {
                        jsonString = responseBodyResponse.body().string();
                    } catch (IOException e) {
                        throw new RuntimeException("Failed decoding response!");
                    }
                } else {
                    throw new RuntimeException("Bad request URL!");
                }
                return jsonString;
            })
            .map(jsonString -> {
                String profileUrl = null;

                if (jsonString != null) {
                    profileUrl = new JSONObject(jsonString).getString("url");
                }

                if (profileUrl == null || profileUrl.isEmpty()) {
                    throw new RuntimeException("Profile URL missing in response!");
                }

                return profileUrl;

            })
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(profileUrl -> Picasso.with(this).load(profileUrl).into(profileCircle),
                       throwable -> {
                           Log.e("TAG", "Failed loading profile image!", throwable);
                           profileCircle.setImageResource(R.drawable.profil);
                       });