Retrofit2 Unauthenticated 401 error when using rxjava2
Asked Answered
Y

3

6

Ever since i'v integrated RxJava2 , i am receiving 401 unauthenticated error in all retrofit calls that return Observable , i'm using basic authentication and i know error is due to it , but why it works on debug but not release.

In my opinion something is wrong with the configuration for rxjava adapters of retrofit2

Stack Trace:

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
}

progaurd:

# 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.** {
    *;
}

Basic Authentication code where error lies:

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);
        }
    }

I'm trying to resolve this issue for over a 2 week now here is my post at rxjava issue page and my previous same issue related question is here , how can i resolve this issue and make it working

Yungyunick answered 23/1, 2017 at 12:15 Comment(3)
Are you sure this is not in the server side? I never had this problem with Retrofit and RxJavaMassa
No i'm sure , its absolutely working fine as debug apk , but in release apk all retrofit2 calls that use basic authentication are not workingYungyunick
All of the code involved with credentials is inside the if (BuildConfig.DEBUG) { block.Looker
K
3

Your exception happens in the class which you had not listed here.

Anyway, you need to analyze the type of Exception which comes to onError handler. Handling of valid HTTP error responses with Retrofit2 Observables in (regardless of rxjava version) is quite easy.

First you need to know is that retrofit api method returning Obsrvable<MyObject> will propagate with onNext only 2xx HTTP responses. All other responses will be sent within onError handler. Here is the code how to handle it (Kotlin example, just to give and idea. Java will ):

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)
    }
})

If you wish to get all responses into onNext, then you should use Observable<ResponseBody>. In that case all valid HTTP responses will end up in onNext, and all your code exceptions will be sent to onError.

Kelbee answered 24/8, 2017 at 18:6 Comment(0)
C
0

In my case, the issue was the wrong ResponseCode.

In positive case it returns 200 OK response Code and for negative case it returns json response with 404 Not Found. We know that Rx handles all the 4xx responses in onError.

Positive response: enter image description here

Negative response: enter image description here

In both cases, It should be 200 OK.

Crissycrist answered 11/3, 2020 at 6:41 Comment(0)
Q
0

If you are using Kotlin + Coroutines then just use try and catch for network operations like,

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

Where, Exception is type of kotlin and not of java.lang

This will handle every exception like,

  1. HttpException
  2. SocketTimeoutException
  3. FATAL EXCEPTION: DefaultDispatcher etc

Here is my usersList() function

@GET(AppConstants.APIEndPoints.HOME_CONTENT)
suspend fun usersList(): UserListResponseModel
Quaver answered 8/6, 2020 at 15:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.