I am working on network module where it should provide offline data where I am fetching previously stored JSON response from local db and building response and returning to the application. All this is happening in my okhttp Interceptor at application level. I know I could have checked for n/w availability with ConnectivityManager
Every time I tried with n/w disabled or changing server false server URL for testing, Debugger just disappears call to chain.proceed(request)
Please note I knew I would have landed in onFaluire of retrofit in my app for any internet error. but I don't want that. Instead I want that to be handled inside interceptor and build response and return.
Searched on net found one question similar to this.
Let me know if any clarity needed on this.
I know this is very old post, but for those like me that also encounter this problem here's what I found.
The chain.proceed(request) call will throw an exception so any codes below this call won't be executed.
So if you really want to execute a codes when an exception occurred you may wrap the chain.proceed() call with try-catch.
override fun intercept(chain: Interceptor.Chain): Response {
try {
val response = chain.proceed(chain.request())
return response
} catch (ex : Exception) {
// handle the exception if you need to
}
// your codes here.
}
Warning: You'll lose access to the response when an exception occurred so you need to return an alternative response because it's required. And codes below try-catch statement won't be executed if there's no exception thrown by the chain.proceed() call.
Related
I understand that this is how the interceptor works and a request from the application passes through the OkHttp core, via retrofit wrapper and OkHttpp core call to make an actual network request and the network response to the application interceptor response via the retrofit wrapper.
Is there a way to avoid calling the actual request from the application interceptor, as in, in some scenario in application interceptor check if the request URL is some string, then, in that case, do-not-call the actual network request and directly return the hard-coded response from the application interceptor?
You can return a new Response instead of calling chain.proceed() and it would stop the chain from moving forward. You can do it like this.
if(something)
return Response.Builder()
.code(200) //Or whatever you might later check from
.protocol(Protocol.HTTP_2) //or 1
.message("SUCCESS")
.body(ResponseBody.create(MediaType.get("application/json"), "{\"x\": 1}")) // your response
.request(chain.request())
.build()
I also recommend to define an annotation, and get it in your interceptor instead of checking for the URL.
request.tag(Invocation::class.java)?.method()?.getAnnotation(YourAnnotation::class.java)
Retrofit has so called "retrofit-mock", which is designed specifically for your task - mocking:
https://github.com/square/retrofit/tree/master/retrofit-mock
You can try it, maybe you will find it useful.
Example of usage:
https://github.com/square/retrofit/blob/master/samples/src/main/java/com/example/retrofit/SimpleMockService.java
You can create 2 implementations of your retrofit service - real and mocked. And provide one of them via DI depending on build flavor or application mode (demo mode or real http session).
Debuggers break-point does not hit when I do network request, even though network request is sent.
Calling from ViewModel
Here:
fun getData(): Single<ArrayList<Data>> {
return service.getData()
.map { jsonApiObject ->
val x: ArrayList<Data> = ArrayList() /*Breakpoint is here*/
return#map x
}
}
Well, the break-point should hit whenever I call this function, but it does not. When I log request with interceptor, I can see that the request link is correct and response code is 200 with correct data. What could cause this? I tried to rebuild project/invalidate cache.
Even if I had wrong gson converter configuration set up with retrofit2, theoretically I still should get till the break point and only when using gson builder object, only then get an error, right?
[![enter image description here][1]][1]
FOUND THE REASON:
Whenever I .addConverterFactory(
JSONConverterFactory.create(...::class.java)) debugger will stop inside map block, but if I do .addConverterFactory(GsonConverterFactory.create()) it will not. Why?
Try adding Debug.waitForDebugger() before the line where you want to have the breakpoint.
I have simple snippet code as below:
Sub sub = null;
try {
Response<Sub> response = MyRestApi.getInstance().getSub().execute();
sub = response.body(); // Does variable response is always non null?
} catch (IOException e) {
e.printStackTrace();
}
//
// ... further operations on sub
//
All that I want to know is, can I call safely .body() on responsein try body?
I've tried to preview my method information .getSub() by calling CTRL-Q in Android Studio but I got the only line
Inferred annotations: #org.jetbrains.annotations.NotNull
I believe it should be enough to convince me about that but I had to ask and be 100% sure.
It might be null, even when request is successful, as described on w3c:
"Response data Additional information may follow, in the format of a
MIME message body. The significance of the data depends on the status
code.
The Content-Type used for the data may be any Content-Type which the
client has expressed its ability to accept, or text/plain, or
text/html. That is, one can always assume that the client can handle
text/plain and text/html."
reference: https://www.w3.org/Protocols/HTTP/Response.html
In Retrofit, the body method is defined as Nullable
#Nullable public T body() The deserialized response body of a
successful response.
https://square.github.io/retrofit/2.x/retrofit/retrofit2/Response.html
It might be null, if the response not successful.
fun remove(data: String): Single<JSONApiObject> {
return service.remove(data)
.onErrorResumeNext(ErrorHandler(ErrorParser()))
}
Is onErrorResumeNext necessary, if I don't intend to do anything onError? This is a POST request.
No. But it recommendable to implement the onError or onErrorResumeNext in order to handle whenever your subscription goes wrong. Otherwise, your program will crash.
For example, in your case, if your POST request fails you can make know your user that is caused by a network disconnection, fields missing or if the server is down.
On android, I make server calls through retrofit and the server can sometimes return a 500 response.
Is there a reason why onError does not get invoked in the subscriber?
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Response<Void>>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
if (isViewAttached()) {
getView().onError(e);
}
}
#Override
public void onNext(Response<Void> response) {
response.code() <-- why would 500 here not get routed to the onError instead?
}
});
This depends part on your configuration and part on how you defined your call, but it will all boil down to one of 2 Observables.
If you look at the source code you can see that if your call returns a type of Response<Foo> Retrofit will internally create either a CallEnqueueObservable<Foo> or CallExecuteObservable for your call. Check it out in the adapt method. For RxJava 1 this is similar, but the observables are called differently. Anyway, internally the way things work are quite the same. The call is executed and onNext is called with a response instance.
If you take a look at how this works inside Retrofit's proxy mechanism, there will always be a response instance even if the response is an Http error. This means that calling onNext will still happen even if the response is an http error itself. You can have a look at the parseReponse method and as you see there's no exception thrown if the status code is 500.
Back to the observables, only when there's an exception will the subscriber's onError be called. Remember, if it's status code 500 there's no exception thrown.
To get your onError to fire for non 2XX http error codes there are different ways, but one possible way is to (if you can afford it) make your call return Observable<Foo> instead of Observable<Response<Foo>>.
This will make retrofit use internally different observables that will make sure to call your subscriber's onError when there's an http error as well as exceptions.
Only network errorsare thrown into onError (e.g. no internet connection).
Think of a 500er as a valid response from the server instead of an error case. Furthermore you want to use the error information the server provided (Body, status code, Headers, etc.). onError can't provide this (unless as an Exception).