I am using RxAndroid + Retrofit to make http request. Code looks like below:
Interceptor headerInterceptor = getHeaderInterceptor();
HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor();
httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(60, TimeUnit.SECONDS)
.writeTimeout(60, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.callTimeout(5, TimeUnit.SECONDS)
.addInterceptor(headerInterceptor)
.addInterceptor(httpLoggingInterceptor)
.build();
Gson gson = new GsonBuilder()
.setLenient()
.create();
sRetrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gson))
.client(client)
.build();
Use it like this:
ApiProvider.provideApi(MyApi.class)
.submit(id, mRequest)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
response -> {
Log.w("tag", "success");
},
throwable -> {
Log.w("tag", "error");
}
);
I set connectTimeout / readTimeout / writeTimeout to be 60 seconds, and set callTimeout to be 5 seconds.
I know this configuration may be not reasonable but I just want to get a timeout exception after 5 seconds, and the Log.w("tag", "error"); could be called.
However, I found this line will never be called for my testing. And if I set connectionTimeout to 1 second, then this line will be called immediately.
So what I should do if I want callTimeout to trigger the log error line?
As for as I can see, I think your problem maybe isn't from how many seconds you set for callTimeout(5, TimeUnit.SECONDS), I think your maybe Rx stream have already throw some errors, so stream just break you can get any response from here. However, you reset time seconds to 1s, and then you restart app, this time stream not break and you get error.
So simple re-test it again to make sure your stream won't break even before enter this subscribe.
And I have been test with some stupid implementations:
class MainActivity : AppCompatActivity() {
val delayInterceptor = object : Interceptor {
override fun intercept(chain: Interceptor.Chain): okhttp3.Response {
Thread.sleep(6000L)
return chain.proceed(chain.request())
}
}
val client: OkHttpClient = OkHttpClient.Builder()
.connectTimeout(60, TimeUnit.SECONDS)
.writeTimeout(60, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.callTimeout(5, TimeUnit.SECONDS)
.addInterceptor(delayInterceptor)
.build()
val retrofit: Retrofit = Retrofit.Builder()
.baseUrl("https://en.wikipedia.org/")
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build();
data class Wiki(
#SerializedName("type")
val type: String
)
interface WikiService {
#GET("api/rest_v1/page/random/summary")
fun getRandomSummary(): Single<Wiki>
}
#SuppressLint("CheckResult")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
retrofit.create(WikiService::class.java)
.getRandomSummary()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
Log.d("tag", "success")
}, {
Log.e("tag", "error")
})
}
}
I finally find the the cause.
I was using com.jakewharton.retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory, which had been deprecated per its readme: https://github.com/JakeWharton/retrofit2-rxjava2-adapter
This is now DEPRECATED!
Retrofit 2.2 and newer have a first-party call adapter for RxJava 2: https://github.com/square/retrofit/tree/master/retrofit-adapters/rxjava2
After switching to retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory, everything start working nicely.
And BTW for any guy who might be interested in what the differences are between them two? Attach the key info I found below:
Related
I'm using retrofit in my app and everything is ok except one thing. After disabling internet connection, enabling it back and making request to api (okhttp logger says that GET request was handled), response comes in a few minutes. And when i make next request, response comes immediately.
Also, for the slow response, okhttp logger says that it has been handled in ~50 milliseconds, but prints that log after a few minutes too.
In project i'm using koin, but i doubt it is the root of the problem. Anyway i defined retrofit, okhttpclient and interceptors as single.
And maybe it'll help, if i set read and write timeouts to, for example, 5 seconds in okhttp client i'll get timeout exception.
So what's happening and how to handle with that problem?
Retrofit:
Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.client(client) // my OkHttpClient instance
.build()
OkHttpClient:
OkHttpClient.Builder()
.addInterceptor(networkConnectionInterceptor)
.addInterceptor(loggingInterceptor)
.writeTimeout(5, TimeUnit.MINUTES)
.readTimeout(5, TimeUnit.MINUTES)
.build()
NetworkConnectionInterceptor and NoConnectivityException classes:
class NetworkConnectionInterceptor(private val context: Context) : Interceptor {
#Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
if (!isConnected) {
throw NoConnectivityException()
}
val builder: Request.Builder = chain.request().newBuilder()
return chain.proceed(builder.build())
}
val isConnected: Boolean
get() {
val connectivityManager =
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val netInfo = connectivityManager.activeNetworkInfo
return netInfo != null && netInfo.isConnected
}
}
class NoConnectivityException : IOException() {
override val message: String
get() = "No Internet Connection"
}
In API declaration I have this code:
#FormUrlEncoded
#POST("loginByPass/")
fun loginByPassword(#Field("login") login: String,
#Field("password") password: String,
#Field("phone") phone: String) : Observable<AuthResponse>
Retrofit object I create this way:
class API {
companion object {
fun getRetrofitAPI() : IAPI {
val interceptor = HttpLoggingInterceptor()
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY)
val client = OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.addInterceptor(interceptor)
.addNetworkInterceptor(interceptor)
.build()
val retrofit = Retrofit.Builder()
.baseUrl(SERVER_ADDRESS)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build()
return retrofit.create(IAPI::class.java)
}
}
}
And request:
api.loginByPassword(login, password, "")
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe({
Log.w("USER_DB", "CODE: " + it.code)
}, {
this.sayError(it.localizedMessage)
}).dispose()
And I have no response at all! In logs it seems like I haven't sent any request. Also I have seen sent packages - from emulator was sent nothing. I'm calling to server IP address by HTTP.
Could you give me piece of advice, what's the problem?
According to #DrawnRaccoon 's answer, .dispose() destroys the request, so I should use CompositeDisposable to collect all requests and dispose them in onDestroy() method.
i am using the following:
implementation "com.squareup.retrofit2:converter-gson:2.6.0"
fun getOkHTTPClient(time: Int, interceptor: Interceptor): OkHttpClient {
val client = OkHttpClient.Builder()
.addInterceptor(interceptor)
.connectTimeout(5, TimeUnit.SECONDS)
.writeTimeout(5, TimeUnit.SECONDS)
.readTimeout(5, TimeUnit.SECONDS)
.callTimeout(5, TimeUnit.SECONDS)
.retryOnConnectionFailure(false)
return client.build()
}
retrofit2.Retrofit.Builder()
.baseUrl("$url/")
.addConverterFactory(GsonConverterFactory.create())
.client(okHttpClient)
If i change the timeouts to 10 seconds, 20, 50, 500 etc.... the effect is the same. the call timeout is not obeying these rules.
Am i missing something?
Note: i am using Asynchronous requests if it helps in debugging the case
OkHttpClient client;
client = new OkHttpClient.Builder()
.connectTimeout(5, TimeUnit.SECONDS)
.writeTimeout(5, TimeUnit.SECONDS)
.readTimeout(5, TimeUnit.SECONDS)
.build();
Request request22 = new Request.Builder()
.url("http://www.goo.com/")
.build();
Utils.myLog("-begin-");
Response response = null;
try {
response = client.newCall(request22).execute();
if (response.isSuccessful()) {
Utils.myLog("-donw-");
}
} catch (Exception e) {
e.printStackTrace();
Utils.myLog("-error-" + e.toString());
}
This is my code, I have set timeout to 5 seconds, but it still taked 20 seconds to receive "error unknownhostexception " after "begin"? why my code is useless? I have looked the source code of OKHTTP, default timeout is 10 seconds(if I'm right), I'm confused.
Anyone can help, id really appreciated.
For now, OkHttp can't interrupt time-consuming DNS requests (see https://github.com/square/okhttp/issues/95), but you still can do something like this:
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(interceptor)
.readTimeout(15, TimeUnit.SECONDS)
.writeTimeout(15, TimeUnit.SECONDS)
.connectTimeout(15, TimeUnit.SECONDS)
.dns(hostname -> Single.fromCallable(
() -> Arrays.asList(InetAddress.getAllByName(hostname))
).timeout(15, TimeUnit.SECONDS)
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.computation())
.onErrorReturnItem(new ArrayList<>())
.blockingGet())
.build();
Kotlin
class CustomDns : Dns {
override fun lookup(hostname: String): List<InetAddress> {
return Single.fromCallable {
InetAddress.getAllByName(hostname)
}.timeout(15, TimeUnit.SECONDS)
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.computation())
.onErrorReturnItem(arrayOf())
.blockingGet().toList()
}
}
OkHttpClient.Builder()
.connectTimeout(15, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(15, TimeUnit.SECONDS)
.dns(CustomDns())
.build()
Im trying to log all the requests (with a network interceptor) using refrofit2, kotlin and logging-interceptor:
retrofit: "2.0.2"
okhttp3 : "3.2.0"
com.squareup.okhttp3:logging-interceptor 3.2.0
like:
val interceptor = HttpLoggingInterceptor()
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
val okHttpClient = OkHttpClient.Builder()
.addNetworkInterceptor(interceptor) // same for .addInterceptor(...)
.connectTimeout(30, TimeUnit.SECONDS) //Backend is really slow
.writeTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build()
sRestAdapter = Retrofit.Builder()
.client(okHttpClient)
.baseUrl(if (host.endsWith("/")) host else "$host/")
.addConverterFactory(GsonConverterFactory.create(gson()))
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build()
It just print:
D/OkHttp: --> GET url...
D/OkHttp: --> END GET
What is happening?
--------------- EDIT --------
Errors doing requests on Main Thread are not showing by the logger, so be careful.
private val interceptor = run {
val httpLoggingInterceptor = HttpLoggingInterceptor()
httpLoggingInterceptor.apply {
httpLoggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
}
}
private val okHttpClient = OkHttpClient.Builder()
.addNetworkInterceptor(interceptor) // same for .addInterceptor(...)
.connectTimeout(30, TimeUnit.SECONDS) //Backend is really slow
.writeTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build()
Instead of
val okHttpClient = OkHttpClient.Builder()
.addNetworkInterceptor(interceptor)
...
you should have something like:
val okHttpClient = OkHttpClient.Builder()
.addInterceptor(interceptor)
...
as the addNetworkInterceptor() plays with interceptors that observe a single network request and response, while addInterceptor() adds interceptor that observes the full span of each call: from the connection is established (if any) until after the response source is selected (either the origin server, cache, or both).
EDIT
Errors doing requests on Main Thread are not showing by the logger, so be careful
Doing networking on main thread is not an "ordinary" error. It will result in your app being killed by the system with NetworkOnMainThreadException and this will happen before interceptor would be given any chance to run.