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.* ;
}
-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 ;
}
-keep class rx.schedulers.ImmediateScheduler {
public ;
}
-keep class rx.schedulers.TestScheduler {
public ;
}
-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
解决方案
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 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. In that case all valid HTTP responses will end up in onNext, and all your code exceptions will be sent to onError.