【问题标题】:Retrofit2 Unauthenticated 401 error when using rxjava2Retrofit2 使用 rxjava2 时出现未验证的 401 错误
【发布时间】:2017-01-23 12:15:30
【问题描述】:

自从我集成了 RxJava2 以来,我在所有返回 Observable 的改造调用中都收到 401 未经身份验证的错误,我正在使用基本身份验证,我知道错误是由它引起的,但为什么会这样可以调试但不能发布。

在我看来,retrofit2 的 rxjava 适配器的配置有问题

堆栈跟踪:

com.jakewharton.retrofit2.adapter.rxjava2.HttpException: HTTP 401 Unauthorized
01-22 19:24:14.872 11502-11502/? W/System.err:     at com.jakewharton.retrofit2.adapter.rxjava2.BodyObservable$BodyObserver.onNext(BodyObservable.java:54)
01-22 19:24:14.872 11502-11502/? W/System.err:     at com.jakewharton.retrofit2.adapter.rxjava2.BodyObservable$BodyObserver.onNext(BodyObservable.java:37)
01-22 19:24:14.872 11502-11502/? W/System.err:     at com.jakewharton.retrofit2.adapter.rxjava2.CallObservable.subscribeActual(CallObservable.java:43)
01-22 19:24:14.872 11502-11502/? W/System.err:     at io.reactivex.Observable.subscribe(Observable.java:10514)
01-22 19:24:14.872 11502-11502/? W/System.err:     at com.jakewharton.retrofit2.adapter.rxjava2.BodyObservable.subscribeActual(BodyObservable.java:34)
01-22 19:24:14.872 11502-11502/? W/System.err:     at io.reactivex.Observable.subscribe(Observable.java:10514)
01-22 19:24:14.872 11502-11502/? W/System.err:     at io.reactivex.internal.operators.observable.ObservableDoOnLifecycle.subscribeActual(ObservableDoOnLifecycle.java:33)
01-22 19:24:14.872 11502-11502/? W/System.err:     at io.reactivex.Observable.subscribe(Observable.java:10514)
01-22 19:24:14.872 11502-11502/? W/System.err:     at io.reactivex.internal.operators.observable.ObservableDoOnEach.subscribeActual(ObservableDoOnEach.java:42)
01-22 19:24:14.872 11502-11502/? W/System.err:     at io.reactivex.Observable.subscribe(Observable.java:10514)
01-22 19:24:14.872 11502-11502/? W/System.err:     at io.reactivex.internal.operators.observable.ObservableObserveOn.subscribeActual(ObservableObserveOn.java:44)
01-22 19:24:14.872 11502-11502/? W/System.err:     at io.reactivex.Observable.subscribe(Observable.java:10514)
01-22 19:24:14.872 11502-11502/? W/System.err:     at io.reactivex.internal.operators.observable.ObservableSubscribeOn$1.run(ObservableSubscribeOn.java:39)
01-22 19:24:14.872 11502-11502/? W/System.err:     at io.reactivex.Scheduler$1.run(Scheduler.java:134)
01-22 19:24:14.872 11502-11502/? W/System.err:     at io.reactivex.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:59)
01-22 19:24:14.872 11502-11502/? W/System.err:     at io.reactivex.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:51)
01-22 19:24:14.872 11502-11502/? W/System.err:     at java.util.concurrent.FutureTask.run(FutureTask.java:237)
01-22 19:24:14.872 11502-11502/? W/System.err:     at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:152)
01-22 19:24:14.872 11502-11502/? W/System.err:     at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:265)
01-22 19:24:14.872 11502-11502/? W/System.err:     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1115)
01-22 19:24:14.872 11502-11502/? W/System.err:     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:590)
01-22 19:24:14.872 11502-11502/? W/System.err:     at java.lang.Thread.run(Thread.java:818)

Build.gradle:

::project::
apply plugin: 'com.android.application' //or apply plugin: 'java'
apply plugin: 'me.tatarka.retrolambda'
apply plugin: 'com.jakewharton.hugo'
apply plugin: 'android-apt'
def AAVersion = '4.1.0'

android {
    compileSdkVersion 25
    buildToolsVersion '25.0.2'

    defaultConfig {
        applicationId "com.jutt.example1"
        minSdkVersion 14
        targetSdkVersion 25
        versionCode 4
        versionName "1.0"
        multiDexEnabled false
    }
    buildTypes {
        release {
            minifyEnabled true
            shrinkResources false
            zipAlignEnabled true
            //debuggable false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    compileOptions {
        targetCompatibility 1.8
        sourceCompatibility 1.8
    }

}

apt {
    arguments {
        // you should set your package name here if you are using different application IDs
        // resourcePackageName "your.package.name"

        // You can set optional annotation processing options here, like these commented options:
        // logLevel 'INFO'
        // logFile '/var/log/aa.log'
    }
}

dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    testCompile 'junit:junit:4.12'
    apt "org.androidannotations:androidannotations:$AAVersion"
    compile "org.androidannotations:androidannotations-api:$AAVersion"
    // Retrofit & OkHttp
    // Because RxAndroid releases are few and far between, it is recommended you also
// explicitly depend on RxJava's latest version for bug fixes and new features.

// If you want to bind to Android-specific lifecycles

// If you want pre-written Activities and Fragments you can subclass as providers
    compile 'com.android.support:support-v4:25.1.0'
    compile 'com.android.support:multidex:1.0.1'
    compile 'com.android.support:appcompat-v7:25.1.0'
    compile 'com.squareup.retrofit2:retrofit:2.1.0'
    //compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
    compile 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0'
    compile 'com.squareup.okhttp3:logging-interceptor:3.4.2'
    compile 'com.squareup.retrofit2:converter-gson:2.1.0'
    compile 'com.afollestad.material-dialogs:core:0.9.0.2'
    compile 'com.github.ganfra:material-spinner:1.1.1'
    compile 'com.android.support:design:25.1.0'
    compile 'org.apache.commons:commons-lang3:3.4'
    compile 'com.wdullaer:materialdatetimepicker:2.5.0'
    //compile 'io.reactivex:rxandroid:1.2.1'
    //compile 'io.reactivex:rxjava:1.1.6'
    compile 'io.reactivex.rxjava2:rxjava:2.0.2'
    compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
    compile 'com.trello.rxlifecycle2:rxlifecycle:2.0.1'
}

::app::

// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.3.0-beta2'
        classpath 'com.jakewharton.hugo:hugo-plugin:1.2.1'
        classpath 'me.tatarka:gradle-retrolambda:3.3.1'

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
        // replace with the current version of the android-apt plugin
        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
    }
}


allprojects {
    repositories {
        jcenter()
        mavenCentral()
        mavenLocal()
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

程序:

# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/zulqurnainjutt/Library/Android/sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
#   http://developer.android.com/guide/developing/tools/proguard.html

# Add any project specific keep options here:

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
#   public *;
#}

-optimizationpasses 5
#-allowaccessmodification
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-dontskipnonpubliclibraryclassmembers
-dontpreverify
-verbose

#your package path where your gson models are stored
-keep class com.jutt.example1.model.** { *; }

# Retrofit, OkHttp, Gson
-keepattributes *Annotation*
-keepattributes Signature
-keep class com.squareup.okhttp.** { *; }
-keep interface com.squareup.okhttp.** { *; }
-dontwarn com.squareup.okhttp.**
-dontwarn rx.**
-dontwarn retrofit.**
-keep class retrofit.** { *; }
-keepclasseswithmembers class * {
    @retrofit.http.* <methods>;
}
-keep class sun.misc.Unsafe { *; }
-dontwarn java.nio.file.*
-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement

# OkHttp3
-keepattributes Signature
-keepattributes *Annotation*
-keep class okhttp3.** { *; }
-keep interface okhttp3.** { *; }
-dontwarn okhttp3.**

# Rxjava-promises

-keep class com.darylteo.rx.** { *; }

-dontwarn com.darylteo.rx.**

# RxJava 0.21

-keep class rx.schedulers.Schedulers {
    public static <methods>;
}
-keep class rx.schedulers.ImmediateScheduler {
    public <methods>;
}
-keep class rx.schedulers.TestScheduler {
    public <methods>;
}
-keep class rx.schedulers.Schedulers {
    public static ** test();
}

## Retrolambda specific rules ##

# as per official recommendation: https://github.com/evant/gradle-retrolambda#proguard
-dontwarn java.lang.invoke.*

-keep class rx.internal.util.unsafe.** {
    *;
}

错误所在的基本身份验证代码:

class Factory {
        private static SERVERAPI service;

        public static SERVERAPI getIstance(Context context, String base_url,String username,String password) {
            if (service == null) {

                OkHttpClient.Builder builder = new OkHttpClient().newBuilder();
                builder.readTimeout(2, TimeUnit.MINUTES);
                builder.connectTimeout(2, TimeUnit.MINUTES);
                builder.writeTimeout(2, TimeUnit.MINUTES);

                //builder.certificatePinner(new CertificatePinner.Builder().add("*.androidadvance.com", "sha256/RqzElicVPA6LkKm9HblOvNOUqWmD+4zNXcRb+WjcaAE=")
                //    .add("*.xxxxxx.com", "sha256/8Rw90Ej3Ttt8RRkrg+WYDS9n7IS03bk5bjP/UXPtaY8=")
                //    .add("*.xxxxxxx.com", "sha256/Ko8tivDrEjiY90yGasP6ZpBU4jwXvHqVvQI0GS3GNdA=")
                //    .add("*.xxxxxxx.com", "sha256/VjLZe/p3W/PJnd6lL8JVNBCGQBZynFLdZSTIqcO0SJ8=")
                //    .build());


                if (BuildConfig.DEBUG) {
                    HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
                    interceptor.setLevel(HttpLoggingInterceptor.Level.BASIC);
                    builder.addInterceptor(interceptor);

                    if(username != null) {
                        if(password == null){
                            password = "";
                        }
                        final String credential = Credentials.basic(username, password);
                        builder.addInterceptor(chain -> {
                            Request original = chain.request();

                            Request.Builder requestBuilder = original.newBuilder()
                                    .header("Authorization", credential);
                            requestBuilder.header("Accept", "application/json");
                            requestBuilder.method(original.method(), original.body());

                            Request request = requestBuilder.build();
                            return chain.proceed(request);
                        });
                    }
                }

                int cacheSize = 10 * 1024 * 1024; // 10 MiB
                Cache cache = new Cache(context.getCacheDir(), cacheSize);
                builder.cache(cache);
                Retrofit retrofit;
                //RxJavaCallAdapterFactory rxAdapter = RxJavaCallAdapterFactory.createWithScheduler(Schedulers.io());
                if (base_url == null) {
                    retrofit = new Retrofit.Builder()
                            .client(builder.build())
                            .addConverterFactory(GsonConverterFactory.create())
                            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                            .baseUrl(BASE_URL).build();
                } else {
                    retrofit = new Retrofit.Builder()
                            .client(builder.build())
                            .addConverterFactory(GsonConverterFactory.create())
                            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                            .baseUrl(base_url).build();
                }
                service = retrofit.create(SERVERAPI.class);
                return service;
            } else {
                return service;
            }
        }

        public static SERVERAPI getIstance(Context context) {
            return getIstance(context, null,null,null);
        }

        public static SERVERAPI getInstanceWithAuthBasic(Context context,String username,String password){ // im using this method
            return getIstance(context, null,username,password);
        }

        public static SERVERAPI getInstanceWithAuthBasic(Context context,String base_url,String username,String password){
            return getIstance(context, base_url,username,password);
        }
    }

我正在尝试解决这个问题超过 2 周,现在这是我在 rxjava issue 页面上的帖子,我之前与问题相关的问题是 here,我该如何解决这个问题并使其正常工作

【问题讨论】:

  • 你确定这不在服务器端吗?我从来没有遇到过 Retrofit 和 RxJava 的问题
  • 不,我确定,它作为调试 apk 绝对可以正常工作,但在发布 apk 中,所有使用基本身份验证的 retrofit2 调用都不起作用
  • 所有与凭据相关的代码都在 if (BuildConfig.DEBUG) { 块内。

标签: android basic-authentication retrofit2 rx-java2


【解决方案1】:

您的异常发生在您未在此处列出的类中。

无论如何,您需要分析Exception 的类型,它会出现在onError 处理程序中。使用 Retrofit2 Observables 处理有效的 HTTP 错误响应(无论 rxjava 版本如何)非常容易。

首先您需要知道的是,返回Obsrvable&lt;MyObject&gt;retrofit api 方法将传播onNext 仅2xx HTTP 响应。所有其他响应将在onError 处理程序中发送。这是如何处理它的代码(Kotlin 示例,只是为了给出和想法。Java 将):

retrofitApiService.suggest(
        token,
        query).subscribe ({ suggestions ->
    log.info("Got result: {}", suggestions)
}, { error ->
    //

    if(error is HttpException) {
        if(error.code() == 401) {
            // We've got HTTP 401 Unauthorized
        } else {

            log.error("HTTP Error: {} {}, {}",
                    error.response().code(),
                    error.response().message(),
                    error.response().errorBody().toString())
        }

    } else {
        // something really wrong happened
        // e.g.: invalid json or something else
        log.error("Error: ${error.message}", error)
    }
})

如果您希望在 onNext 中获得所有响应,那么您应该使用Observable&lt;ResponseBody&gt;。在这种情况下,所有有效的 HTTP 响应都将在 onNext 中结束,并且您的所有代码异常都将发送到 onError

【讨论】:

    【解决方案2】:

    在我的情况下,问题是错误的 ResponseCode。

    在正面情况下,它返回200 OK 响应代码,在负面情况下,它返回带有404 Not Found 的json 响应。我们知道 Rx 处理 onError 中的所有 4xx 响应。

    正面回应:

    否定回答:

    在这两种情况下,都应该是200 OK

    【讨论】:

      【解决方案3】:

      如果您使用的是 Kotlin + Coroutines,那么只需使用 trycatch 进行网络操作,例如,

      viewModelScope.launch(Dispatchers.IO) {
              try {
                  val userListResponseModel = apiEndPointsInterface.usersList()
                  returnusersList(userListResponseModel)
              } catch (e: Exception) {
                  e.printStackTrace()
              }
          }
      

      其中,Exception 是 kotlin 的类型,而不是 java.lang 的类型

      这将处理每个异常,例如,

      1. HttpException
      2. SocketTimeoutException
      3. 致命异常:DefaultDispatcher 等

      这是我的usersList() 函数

      @GET(AppConstants.APIEndPoints.HOME_CONTENT)
      suspend fun usersList(): UserListResponseModel
      

      【讨论】:

        猜你喜欢
        • 2017-05-09
        • 1970-01-01
        • 2016-07-02
        • 2021-09-16
        • 2014-10-06
        • 1970-01-01
        • 1970-01-01
        • 2015-11-28
        • 2012-04-24
        相关资源
        最近更新 更多