Firebase isn't reporting crashes related to OkHttp - android

I was migrating my code from Google analytics to Firebase, following problem I'm facing
Some of the custom events show correct value while others not although code used are same in all cases. Can provide code if required.
Update : Above is solved, I was sending large data so its just omitted them.
Prior to using OkHttp(using Android network library and Asynctask) firebase shows correct line number in crash reports but not after using OkHttp, I can confirm that I have uploaded correct mapping file as other non OKHttp related crashes are reported correctly.
So my concern is not exception but from where it is thrown?
Firebase crash report before OKhttp
Exception java.net.SocketTimeoutException: connect timed out
java.net.PlainSocketImpl.socketConnect (PlainSocketImpl.java)
java.net.AbstractPlainSocketImpl.doConnect (AbstractPlainSocketImpl.java:334)
java.net.AbstractPlainSocketImpl.connectToAddress (AbstractPlainSocketImpl.java:196)
java.net.AbstractPlainSocketImpl.connect (AbstractPlainSocketImpl.java:178)
java.net.SocksSocketImpl.connect (SocksSocketImpl.java:356)
java.net.Socket.connect (Socket.java:586)
com.android.okhttp.internal.Platform.connectSocket (Platform.java:113)
com.android.okhttp.Connection.connectSocket (Connection.java:1432)
com.android.okhttp.Connection.connect (Connection.java:1390)
com.android.okhttp.Connection.connectAndSetOwner (Connection.java:1667)
com.android.okhttp.OkHttpClient$1.connectAndSetOwner (OkHttpClient.java:133)
com.android.okhttp.internal.http.HttpEngine.connect (HttpEngine.java:466)
com.android.okhttp.internal.http.HttpEngine.sendRequest (HttpEngine.java:371)
com.android.okhttp.internal.huc.HttpURLConnectionImpl.execute (HttpURLConnectionImpl.java:503)
com.android.okhttp.internal.huc.HttpURLConnectionImpl.getResponse (HttpURLConnectionImpl.java:438)
com.android.okhttp.internal.huc.HttpURLConnectionImpl.getResponseCode (HttpURLConnectionImpl.java:567)
com.package.MyClass$4.doInBackground (MyClass.java:168)
com.package.MyClass$4.doInBackground (MyClass.java:161)
android.os.AsyncTask$2.call (AsyncTask.java:304)
java.util.concurrent.FutureTask.run (FutureTask.java:237)
android.os.AsyncTask$SerialExecutor$1.run (AsyncTask.java:243)
java.util.concurrent.ThreadPoolExecutor.runWorker (ThreadPoolExecutor.java:1133)
java.util.concurrent.ThreadPoolExecutor$Worker.run (ThreadPoolExecutor.java:607)
java.lang.Thread.run (Thread.java:762)
Firebase crash report after OkHttp
Exception java.net.SocketTimeoutException: connect timed out
java.net.PlainSocketImpl.socketConnect (PlainSocketImpl.java)
java.net.AbstractPlainSocketImpl.doConnect (AbstractPlainSocketImpl.java:334)
java.net.AbstractPlainSocketImpl.connectToAddress (AbstractPlainSocketImpl.java:196)
java.net.AbstractPlainSocketImpl.connect (AbstractPlainSocketImpl.java:178)
java.net.SocksSocketImpl.connect (SocksSocketImpl.java:356)
java.net.Socket.connect (Socket.java:586)
okhttp3.internal.platform.AndroidPlatform.connectSocket (AndroidPlatform.java:69)
okhttp3.internal.connection.RealConnection.connectSocket (RealConnection.java:238)
okhttp3.internal.connection.RealConnection.connect (RealConnection.java:158)
okhttp3.internal.connection.StreamAllocation.findConnection (StreamAllocation.java:256)
okhttp3.internal.connection.StreamAllocation.findHealthyConnection (StreamAllocation.java:134)
okhttp3.internal.connection.StreamAllocation.newStream (StreamAllocation.java:113)
okhttp3.internal.connection.ConnectInterceptor.intercept (ConnectInterceptor.java:42)
okhttp3.internal.http.RealInterceptorChain.proceed (RealInterceptorChain.java:147)
okhttp3.internal.http.RealInterceptorChain.proceed (RealInterceptorChain.java:121)
okhttp3.internal.cache.CacheInterceptor.intercept (CacheInterceptor.java:93)
okhttp3.internal.http.RealInterceptorChain.proceed (RealInterceptorChain.java:147)
okhttp3.internal.http.RealInterceptorChain.proceed (RealInterceptorChain.java:121)
okhttp3.internal.http.BridgeInterceptor.intercept (BridgeInterceptor.java:93)
okhttp3.internal.http.RealInterceptorChain.proceed (RealInterceptorChain.java:147)
okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept (RetryAndFollowUpInterceptor.java:125)
okhttp3.internal.http.RealInterceptorChain.proceed (RealInterceptorChain.java:147)
okhttp3.internal.http.RealInterceptorChain.proceed (RealInterceptorChain.java:121)
okhttp3.RealCall.getResponseWithInterceptorChain (RealCall.java:200)
okhttp3.RealCall$AsyncCall.execute (RealCall.java:147)
okhttp3.internal.NamedRunnable.run (NamedRunnable.java:32)
java.util.concurrent.ThreadPoolExecutor.runWorker (ThreadPoolExecutor.java:1133)
java.util.concurrent.ThreadPoolExecutor$Worker.run (ThreadPoolExecutor.java:607)
java.lang.Thread.run (Thread.java:761)
As requested code for OkHttp implementation
Request request = new Request.Builder().url(Uri.parse(serviceUrl).buildUpon().appendPath("test").toString())
.build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(#NonNull Call call, #NonNull IOException e) {
FirebaseCrash.report(e);
}
#Override
public void onResponse(#NonNull Call call, #NonNull final Response response) throws IOException {
}

After initializing crashlytics in your base application with following code
(Note this part is missing in Google's official documentation)
FirebaseApp.initializeApp(getApplicationContext());
FirebaseCrashlytics.getInstance().setCrashlyticsCollectionEnabled(true);
Call following function
FirebaseCrashlytics.getInstance().sendUnsentReports();
In this way you, on your next application start, unsent reports will be uploaded
You can use this link for crashlytics integration
https://firebase.google.com/docs/crashlytics/get-started-new-sdk?platform=android&authuser=0

To your OkHttpClient add :
client.connectTimeoutMillis(20000); // 20000 means 20 seconds give time here in milliseconds
or you can also add it to your client builder as:
clientBuilder.connectTimeout(60, TimeUnit.SECONDS);
This happens because when response of server reached to you, your request timed out. Give timeout what you think is feasible for your server to respond.
or you can also may be do with that too but I am not sure about that:
Request request = new Request.Builder().wait(long millisecondshere).url(Uri.parse(serviceUrl).buildUpon().appendPath("test").toString())
.build();

This is Timeout issue,
There are two possibilities,
have you checked and tested your connection.
better don't set any connection timeout,if you are setting chose maximum time, because it throws an error,if server didn't response within given time..
Preventing SocketTimeoutException is beyond our limit...One way to effectively handle it is to define a connection timeout and later handle it by using a try catch block.... hope this will help anyone in future who are facing the same issue.
You can set time out for HttpUrlConnection like,
HttpUrlConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(8000);

Related

okHttp always timeout when POST a large body

I use okHttp do a POST request, it works when the fields String.length less than about 500.
code like this, a regular way to use okHttp:
Request.Builder builder = new Request.Builder();
builder.url(getHostname() + getUrl());
builder.post(new FormBody.Builder.add("key", "the large string").build())
Call call = mOkHttpClient.newCall(builder.build());
call.enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
#Override
public void onResponse(Call call, Response response) throws IOException {
String output = response.body().string();
Log.d("okHttp response", output);
}
});
but when this params field length > 500, like 1k, not sure the exactly value, it throws java.net.SocketTimeoutException: timeout:
java.net.SocketTimeoutException: timeout
at okio.SocketAsyncTimeout.newTimeoutException(JvmOkio.kt:143)
at okio.AsyncTimeout.access$newTimeoutException(AsyncTimeout.kt:162)
at okio.AsyncTimeout$source$1.read(AsyncTimeout.kt:335)
at okio.RealBufferedSource.indexOf(RealBufferedSource.kt:427)
at okio.RealBufferedSource.readUtf8LineStrict(RealBufferedSource.kt:320)
at okhttp3.internal.http1.HeadersReader.readLine(HeadersReader.kt:29)
at okhttp3.internal.http1.Http1ExchangeCodec.readResponseHeaders(Http1ExchangeCodec.kt:178)
at okhttp3.internal.connection.Exchange.readResponseHeaders(Exchange.kt:106)
at okhttp3.internal.http.CallServerInterceptor.intercept(CallServerInterceptor.kt:79)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.kt:34)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.kt:95)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.kt:83)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.kt:76)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
at okhttp3.internal.connection.RealCall.getResponseWithInterceptorChain$okhttp(RealCall.kt:201)
at okhttp3.internal.connection.RealCall$AsyncCall.run(RealCall.kt:517)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
at java.lang.Thread.run(Thread.java:764)
Caused by: java.net.SocketException: Socket closed
at java.net.SocketInputStream.read(SocketInputStream.java:203)
at java.net.SocketInputStream.read(SocketInputStream.java:139)
at okio.InputStreamSource.read(JvmOkio.kt:90)
at okio.AsyncTimeout$source$1.read(AsyncTimeout.kt:129)
... 20 more
whether I set timeout:
int TIMEOUT = 60;
mOkHttpClient = new OkHttpClient.Builder()
.connectTimeout(TIMEOUT, TimeUnit.SECONDS)
.readTimeout(TIMEOUT,TimeUnit.SECONDS)
.writeTimeout(TIMEOUT, TimeUnit.SECONDS)
.build();
It always throw SocketTimeoutException after the TIMEOUT I set. It's like it not even send data just wait for timeout.
I tried:
1.Extended TIMEOUT setting value, but it just keep posting and throw timeoutException until the end of time.
2.Tested in postman use the same large data, it uploaded and tooks 1s. The field's length is not too long, just about 8k.
3.Updated the okhttp version from 3.12.0 to the last 4.9.1 now.
But not solved this issue.
I continued to test the program and found something new:
This problem only appears on the Android Pad. When I test with my mobile phone, the same data does not timeout.
1.I tested on Huawei P30 pro(android 10) it works, and not works on Samsung Galaxy Tab active pro(android11) and Xiaomi PAD4(android8.1),Im not sure if this only occurs on pad, or it's just a coincidence.
2.I tested with my Samsung pad and set up a proxy with Charles. When I used the proxy to access the network and want to capture the content data, the program was works. If the proxy was cancelled, the problem reappeared.
The issue has been resolved. it is a network configuration problem. After some comparative tests, I tried hotspot, or cellular network, or change another WiFi. This POST API can succeed. Strangely, there are still differences between mobile phones and PAD under the same WiFi. However, I still don't know whether it is the problem of configuration of router's or pad's WiFi.

Firestore operations (add(), set()) not working

I am trying to add user details to Firestore db, but can't write data in it.
Actually none of the listeners are triggered neither onSuccess() nor onFailure().
here is my code.
Map<String,Object> userlol = new HashMap<>();
userlol.put("name",name);
userlol.put("email",email);
userlol.put("uid",currentUser.getUid());
Log.d(TAG, "we are here");
CollectionReference dc = db.collection("users");
DocumentReference dd = dc.document(currentUser.getUid());
dd.set(userlol)
.addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Toast.makeText(SignupActivity.this, "User Added" ,
Toast.LENGTH_SHORT).show();
Log.d(TAG,"User added to database");
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Toast.makeText(SignupActivity.this, "ERROR" +e.toString(),
Toast.LENGTH_SHORT).show();
Log.d(TAG, e.toString());
}
});
There is no toast nor the Log in my logcat.I can see
D/logging: we are here
This log and logs after this method.
There is no issue with rules as onFailure() is also not working.
I have searched every possible thing but nothing worked.
The only way I can get neither of the callbacks to trigger is when there is no network connection on my device. In that case the behavior makes sense, since the task is only considered completed when the data has been committed (or rejected) on the server.
To easily see if the Firestore client indeed can't connect to the server, enable debug logging:
FirebaseFirestore.setLoggingEnabled(true);
I see log entries like this after doing so:
11-12 07:56:21.366 10034-10066/com.firebase.firestorestackoverflow I/Firestore: (0.6.6-dev) [zzetk]: (b6322ac) Stream closed with status: zzcd{code=UNAVAILABLE, description=null, cause=java.net.UnknownHostException: Unable to resolve host "firestore.googleapis.com": No address associated with hostname
at java.net.InetAddress.lookupHostByName(InetAddress.java:470)
at java.net.InetAddress.getAllByNameImpl(InetAddress.java:252)
at java.net.InetAddress.getAllByName(InetAddress.java:215)
at io.grpc.internal.zzbj$zzb.zztu(Unknown Source)
at io.grpc.internal.zzbk.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
at java.lang.Thread.run(Thread.java:818)
Caused by: android.system.GaiException: android_getaddrinfo failed: EAI_NODATA (No address associated with hostname)
at libcore.io.Posix.android_getaddrinfo(Native Method)
at libcore.io.ForwardingOs.android_getaddrinfo(ForwardingOs.java:55)
at java.net.InetAddress.lookupHostByName(InetAddress.java:451)
at java.net.InetAddress.getAllByNameImpl(InetAddress.java:252) 
at java.net.InetAddress.getAllByName(InetAddress.java:215) 
at io.grpc.internal.zzbj$zzb.zztu(Unknown Source) 
at io.grpc.internal.zzbk.run(Unknown Source) 
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113) 
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588) 
at java.lang.Thread.run(Thread.java:818) 
}.
well for anyone having that problem in the future just Uninstall your application and reinstall it, as savage as it seams it works(y)
I had the same problem.
First of all turning on the logs helped a lot as #Frank van Puffelen suggested.
In my case I got "The Cloud Firestore API is not available for Datastore Mode projects".
So I went to GCP and created manually a collection by changing the db to a native one. And then I had an option on the gui.
Now the error changed to "FirebaseFirestoreException: permission_denied: missing or insufficient permissions"
So I changed the permissions under the "rules" tab in firestore.
And that fixed it for me :)
I guess the problem was the permissions from the beginning, but I can't tell for sure now.
I had the same error. In my case, I was creating a user and automatically logging him/her out, for them to log manually.
I removed the sign out, as data appears not to be written if there is no user logged in.
Do you have a user signed in when the data is written? Creating a user signs him/her in automatically.
Hope it helps!
It appears that just like after adding new permissions in the manifest file, the app has to be reinstalled to register the changes. I tried reinstalling the app after adding the firestore connection and everything worked fine. voila!

Sometime not get response from OneSignal when i try to get deviceId in Android

I am using "OneSignal.idsAvailable()" method to get deviceId. Sometimes I get a response but sometimes not. It throws following exception:
OneSignalRestClient: null Error thrown from network stack.
java.io.InterruptedIOException: thread interrupted
at com.android.okhttp.okio.Timeout.throwIfReached(Timeout.java:145)
at com.android.okhttp.okio.Okio$1.write(Okio.java:78)
at com.android.okhttp.okio.AsyncTimeout$1.write(AsyncTimeout.java:155)
at com.android.okhttp.okio.RealBufferedSink.flush(RealBufferedSink.java:221)
at com.android.okhttp.internal.http.HttpConnection.flush(HttpConnection.java:141)
at com.android.okhttp.internal.http.HttpTransport.finishRequest(HttpTransport.java:60)
at com.android.okhttp.internal.http.HttpEngine.readNetworkResponse(HttpEngine.java:1147)
at com.android.okhttp.internal.http.HttpEngine.readResponse(HttpEngine.java:980)
at com.android.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:482)
at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:418)
at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getResponseCode(HttpURLConnectionImpl.java:540)
at com.android.okhttp.internal.huc.DelegatingHttpsURLConnection.getResponseCode(DelegatingHttpsURLConnection.java:105)
at com.android.okhttp.internal.huc.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:25)
at com.onesignal.OneSignalRestClient.startHTTPConnection(OneSignalRestClient.java:146)
at com.onesignal.OneSignalRestClient.access$100(OneSignalRestClient.java:38)
at com.onesignal.OneSignalRestClient$4.run(OneSignalRestClient.java:94)
at java.lang.Thread.run(Thread.java:818)
I think due to network issue its reaching to the timeout. You should when you request the device ids, check internet connection first and then request the device id.

How can I set connection timeout for OkHttpClient? 2017

It is easy to say it's duplicate but it isn't.
I read many post about how to set the connection timeout in android but the post are 4-7 years old and I think that we all need an update about this topic because those methods are deprecated or no longer exist.
So the question is how can I set my connection timeout when I am waiting for a response from the server?
final Response response = httpClient.newCall(request).execute();
if (response.isSuccessful()) {
//success
} else {
//unsuccessful
}
If you create your OkHttpClient through an OkHttpClient.Builder, there are connectTimeout(), readTimeout(), and writeTimeout() methods that you can call for the various timeout options.
If you need to override them for a specific HTTP request, call newBuilder() on your OkHttpClient. That gives you an OkHttpClient.Builder with the same settings as you used originally. You can override those as needed, and create a temporary OkHttpClient from the new Builder, using that for this one-off call.

Spring Rest Template usage causes EOFException

I'm receiving java.io.EOFException's when using Spring REST template on Android.
The stacktrace cause reads like this:
Caused by: java.io.EOFException
at libcore.io.Streams.readAsciiLine(Streams.java:203)
at libcore.net.http.HttpEngine.readResponseHeaders(HttpEngine.java:560)
at libcore.net.http.HttpEngine.readResponse(HttpEngine.java:813)
at libcore.net.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:274)
at libcore.net.http.HttpURLConnectionImpl.getResponseCode(HttpURLConnectionImpl.java:486)
at org.springframework.http.client.SimpleClientHttpResponse.getRawStatusCode(SimpleClientHttpResponse.java:49)
at org.springframework.http.client.SimpleClientHttpResponse.getStatusCode(SimpleClientHttpResponse.java:55)
at org.springframework.http.client.BufferingClientHttpResponseWrapper.getStatusCode(BufferingClientHttpResponseWrapper.java:47)
at com.company.util.LoggingClientHttpRequestInterceptor.intercept(LoggingClientHttpRequestInterceptor.java:33)
at org.springframework.http.client.InterceptingClientHttpRequest$RequestExecution.execute(InterceptingClientHttpRequest.java:81)
at com.company.api.interceptor.AuthTokenInterceptor.intercept(AuthTokenInterceptor.java:51)
at org.springframework.http.client.InterceptingClientHttpRequest$RequestExecution.execute(InterceptingClientHttpRequest.java:81)
at org.springframework.http.client.InterceptingClientHttpRequest.executeInternal(InterceptingClientHttpRequest.java:67)
at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:46)
at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:63)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:475)
... 14 more
Another similar stacktrace:
org.springframework.web.client.ResourceAccessException: I/O error: null; nested exception is java.io.EOFException
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:490)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:438)
at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:414)
at com.company.api.ApiClient_.logLoginAttempt(ApiClient_.java:299)
at com.company.security.CompanyAuthenticationService$2.onCreateCall(CompanyAuthenticationService.java:206)
at com.company.api.SafeApiCall.doInBackground(SafeApiCall.java:49)
at com.company.api.SafeApiCall.doInBackground(SafeApiCall.java:22)
at android.os.AsyncTask$2.call(AsyncTask.java:287)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)
at java.util.concurrent.FutureTask.run(FutureTask.java:137)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:230)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
at java.lang.Thread.run(Thread.java:856)
Caused by: java.io.EOFException
at libcore.io.Streams.readAsciiLine(Streams.java:203)
at libcore.net.http.HttpEngine.readResponseHeaders(HttpEngine.java:560)
at libcore.net.http.HttpEngine.readResponse(HttpEngine.java:813)
at libcore.net.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:274)
at libcore.net.http.HttpURLConnectionImpl.getResponseCode(HttpURLConnectionImpl.java:486)
at org.springframework.http.client.SimpleClientHttpResponse.getRawStatusCode(SimpleClientHttpResponse.java:49)
at org.springframework.http.client.SimpleClientHttpResponse.getStatusCode(SimpleClientHttpResponse.java:55)
at org.springframework.http.client.BufferingClientHttpResponseWrapper.getStatusCode(BufferingClientHttpResponseWrapper.java:47)
at org.springframework.web.client.DefaultResponseErrorHandler.hasError(DefaultResponseErrorHandler.java:46)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:476)
... 13 more
This is all happening on Android 4.1.2, installed on my Xoom tablet.
The problem appears and disappears. It's not triggered by long requests either. The server part is running on a machine within the local network. When I try to run the API Calls through curl, it works just fine.
AuthTokenInterceptor:
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] data, ClientHttpRequestExecution execution) throws IOException {
HttpHeaders headers = request.getHeaders();
if (!StringUtils.isEmpty(mAuthToken)) {
headers.add((mIsOAuth ? "Authorization" : "authToken"), (mIsOAuth ? "Bearer " : "") + mAuthToken);
}
return execution.execute(request, data);
}
LoggingClientHttpRequestInterceptor:
/** {#inheritDoc} */
#Override
public ClientHttpResponse intercept(HttpRequest httpRequest, byte[] bytes, ClientHttpRequestExecution clientHttpRequestExecution) throws IOException {
Log.d(TAG, "To : " + httpRequest.getURI());
Log.d(TAG, "Method : " + httpRequest.getMethod().name());
Log.d(TAG, "Data : " + new String(bytes));
for (Object key : httpRequest.getHeaders().keySet()) {
Log.d(TAG, "Header <" + key + ">: " + httpRequest.getHeaders().get(key));
}
final ClientHttpResponse response = clientHttpRequestExecution.execute(httpRequest, bytes);
if (response != null) {
Log.d(TAG, "Response: " + response.getStatusCode());
if (response.getBody() != null) {
Log.d(TAG, "Response: " + convertStreamToString(response.getBody()));
}
} else {
Log.d(TAG, "Response: " + response);
}
return response;
}
The Rest Template is configured like this:
final RestTemplate template = new RestTemplate(false);
template.getMessageConverters().add(new MappingJacksonHttpMessageConverter());
template.setRequestFactory(new BufferingClientHttpRequestFactory(template.getRequestFactory()));
ApiUtils.addAuthTokenHeaderToRestTemplate(template, mAuthToken, false);
ApiUtils.addRequestLoggingToRestTemplate(template);
The API call in question that crashed here is described in the Android annotations based interface:
#Post("/user/memberships")
#Accept(MediaType.APPLICATION_JSON)
CompanyApiResponse saveGroupMembership(UserGroupMembership membership) throws RestClientException;
Things I've tried:
Removed LoggingInterceptor
Called all API calls by CURL
Removed call BufferingClientHttpRequestFactory - Helped a little but the error still occurs.
Tested it on Android 2.3 - the error cannot be reproduced
I've been reading various forums posts, the EOF exception seems to appear if URLs are incorrect, which I double checked in this case.
Also of note, once the EOF Exception occurs, the call not even reaches the server side.
Where would be a good point to continue the search for a fix? Is this a Android 4.1 inconvenience?
While debugging this issue, I also found https://jira.springsource.org/browse/ANDROID-102 which prevented me from seeing the real error (EOF) before.
Update: Just found http://code.google.com/p/google-http-java-client/issues/detail?id=116 - it might be related.
The fix is also outlined in https://codereview.appspot.com/6225045/ - so it might've been merged for 4.1.
This one bit me as well, running Jelly Bean 4.2. After researching, it seems that it's happening because of a combination of Keep-Alive being set and using the standard J2SE HTTP Client, which I believe is HttpURLConnection.
There are 2 solutions that I can confirm are correct.
1) Switch off Keep-Alive.
For me, the solution given in Sebastian's answer, System.setProperty("http.keepAlive", "false"); didn't work. I had to use
HttpHeaders headers = new HttpHeaders();
headers.set("Connection", "Close");
and send those headers in an HttpEntity in the RestTemplate.
As mentioned, this solution could have an impact on performance
2) Change the HTTP Client.
In Spring for Android (tested on 1.0.1.RELEASE, but could be in earlier releases too) the default HTTP Client for a RestTemplate instance is determined by the version of Android on the device. API 9 or newer uses HttpURLConnection, older uses HTTPClient. To explicitly set the client to the old one, use
restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory());
More info can be found here: http://static.springsource.org/spring-android/docs/1.0.1.RELEASE/reference/htmlsingle/#d4e34
I'm not sure what impact this will have on performance, but I guess it's more performant than an app that doesn't work.
Anyway, hope that helps someone. I just wasted a week wild-goose-chasing this one down.
http://code.google.com/p/google-http-java-client/issues/detail?id=116 contains a workaround in the latest comment:
This is defenetly somehow connected with keepAlive connections.
When I use: System.setProperty("http.keepAlive", "false"); problems
disappears.
But from my understanding keep alive connections are greatly increase
performance so it is better not to disable them.
Im also awere that keep alive should be disabled for old versions, but
my device is Jelly Bean.
Once applied the error disappeared.
Seems it's not entirely related to Spring, but a JB problem.
Recently I faced this issue and will able to resolved this issue after setting headers with following piece of code :
headers.set("Accept-Language", "en-US,en;q=0.8");
RestTemplate restTemplate = new RestTemplate();
((SimpleClientHttpRequestFactory)restTemplate.getRequestFactory()).setOutputStreaming(false);
restTemplate.postForObject......

Categories

Resources