Android – after I updated to retrofit 2.0, I released the onnext call to onnext in different threads

When we used retrofit 1.9, my colleagues created the following courses

public class SomeApiCallAction {

  private Subscription subscription;
  private NoInternetConnectionInterface noInternetConnectionInterface;

  public interface NoInternetConnectionInterface {
      PublishSubject<Integer> noInternetConnection(Throwable throwable);
  }

  public void execute(Subscriber subscriber, NoInternetConnectionInterface noInternetConnectionInterface) {
      this.noInternetConnectionInterface = noInternetConnectionInterface;
      this.subscription = retrofit.someService().someApiCall()
          .subscribeOn(Schedulers.newThread())
          .observeOn(AndroidSchedulers.mainThread())
          .subscribe(subscriber)
          .retryWhen(retryFunction);
  }

  public void cancel() {
      if (this.subscription != null) {
          this.subscription.unsubscribe();
      }
  }             

  private Func1<Observable<? extends Throwable>, Observable<?>> retryFunction = new Func1<Observable<? extends Throwable>, Observable<?>>() {
      @Override
      public Observable<?> call(Observable<? extends Throwable> observable) {
          return observable.flatMap(new Func1<Throwable, Observable<?>>() {
              @Override
              public Observable<?> call(final Throwable throwable) {
                  if (noInternetConnectionInterface!= null && (throwable instanceof IOException || throwable instanceof SocketTimeoutException)) {
                      return noInternetConnectionInterface.noInternetConnection(throwable);
                  }else{
                      return Observable.error(throwable);
                  }
              }
          });
      }
}

Someapicallaction is just a simple class. It contains internal improved API calls, especially its retry function. The retry function will check whether the throwable is IOException or sockettimeoutexception. If so, it will call the interface so that we can provide users with a retry dialog box and ask them whether they want to retry the operation. Our usage is similar to the following code segment

public class SomeActivity implement NoInternetConnectionInterface {

    @OnClick(R.id.button)
    public void do(View v) {
        new SomeApiCallAction().execute(
            new Subscriber(),
            this
        )
    }

    @Override
    public PublishSubject<Integer> noInternetConnection(final Throwable throwable) {
        Log.i("Dev", Thread.currentThread() + " Error!");
        final PublishSubject<Integer> subject = PublishSubject.create();

        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                NoInternetDialogFragment dialog = NoInternetDialogFragment.newInstance();
                dialog.setNoInternetDialogFragmentListener(new NoInternetDialogFragmentListener{
                    @Override
                    public void onUserChoice(boolean retry, NoInternetDialogFragment dialog) {
                        Log.i("Dev", Thread.currentThread() + " Button Click!");
                        if (retry) {
                            subject.onNext(1);
                        } else {
                            subject.onError(throwable);
                        }

                        dialog.dismiss();

                    }
                });
                dialog.show(getSupportFragmentManager(), NoInternetDialogFragment.TAG);
            }
        });
        return subject;
    }
}

When we use retrofit 1.9.0, this implementation works perfectly. We test by turning on the flight mode, and then press the button to execute the API call

>The first execution failed, and I got an unknownhostexception in the retry function. > therefore, I called the interface (activity) to display the retry dialog box > I still press the retry button in flight mode to repeat execution > as expected, every execution after the user presses the retry button fails, I always get unknownhostexception in the retry function. > If I keep pressing the retry button, the retry dialog box will always be displayed until I turn off flight mode

But after we update our dependencies

'com.squareup.retrofit2:retrofit:2.0.2'
'com.squareup.retrofit2:adapter-rxjava:2.0.2'

We tried again, but this time the behavior changed,

>The first execution failed, and I got the same unknownhostexception as before in the retry function. > therefore, I called the interface (activity) to display the retry dialog box > I still press the retry button in flight mode to repeat execution > but this time, in the retry function, I got networkonmainthreadexception instead of receiving unknowhostexception like it. > therefore, the conditions did not match, the interface was not called, and only one retry dialog box was presented to the user

The following is the log of the above code

Thread[android_0,5,main] Error!
Thread[main,5,main] Button Click!

Do you know what this will lead to? Any suggestions and comments will be greatly appreciated

Note: the following are other dependencies that we have been using and may be related to. However, they have not been updated recently. These versions have been used since the beginning of this project

'com.jakewharton:butterknife:8.0.1'

'io.reactivex:rxandroid:1.1.0'
'io.reactivex:rxjava:1.1.0'

'com.google.dagger:dagger-compiler:2.0'
'com.google.dagger:dagger:2.0'
'javax.annotation:jsr250-api:1.0'

More information

I just reset my code back to when we used retrofit 1.9, and I found that printing logs was different

Thread[Retrofit-Idle,5,main] Error!
Thread[main,5,main] Button Click!

I'm not sure if this is related to the problem, but it's obvious that in 1.9.0, I called the interface in different threads as 2.0.0

Final editing

After reading the answer of @ John wowus and following the link he provided, I found that in retrofit 2, network calls are synchronized by default

There are two ways to solve my problem

1.) as suggested by @ John wowus, specify the thread for retryfunction

this.subscription = retrofit.someService().someApiCall()
  .subscribeOn(Schedulers.io())
  .observeOn(AndroidSchedulers.mainThread())
  .subscribe(subscriber)
  .retryWhen(retryFunction, Schedulers.io());

2.) when creating a transformation object, specify the thread when creating rxjavacalladapterfactory

retrofit = new Retrofit.Builder()
  .baseUrl(AppConfig.BASE_URL)
  .client(client)
  .addConverterFactory(GsonConverterFactory.create(getGson()))
  .addCallAdapterFactory(
     RxJavaCallAdapterFactory.createWithScheduler(
       Schedulers.from(threadExecutor)
     )
   )
  .build();

resolvent:

I think the problem is that when you re subscribe, you subscribe on the main thread because you use the default trampoline scheduler in retrywhen. Retro 1.9 handles the scheduling for you, so using subscribeon is meaningless. The problem discussion is here. In retro 2, I believe this has changed, so you should try something similar

this.subscription = retrofit.someService().someApiCall()
      .subscribeOn(Schedulers.io())
      .observeOn(AndroidSchedulers.mainThread())
      .subscribe(subscriber)
      .retryWhen(retryFunction, Schedulers.io());

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>