Reputation: 191
I'm using a Spring 3 MVC Server that is hosted on Windows Azure, and am using this to provide users with interaction both through a web browser and an Android device. All code is written in Java.
When I interact with the server through the browser, everything is OK, and when interacting with it using Android it works OK if the server is run as localhost. However, when I interact with the Azure hosted server using the Android client, I randomly (but always eventually) get a NullPointerException
, so sometimes the code runs fine but eventually this will show up. Exactly where I get the NullPointer
changes, but it is always during a HTTP call using RestTemplate
.
Another strange thing, when running the Android app in debug, the code will run to some arbitrary point in the code (still crashing at a RestTemplate
call), but the stack trace for the NullPointerException
will point to a call made earlier on.
The general code I make for a call is similar to the following:
private class ChangeDirectory extends AsyncTask< String, Void, Boolean >
{
@Override
protected Boolean doInBackground(String... directoryName) {
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add( new StringHttpMessageConverter() );
String url = "http://" + Constants.DNS +"/ui/android/cloud/change_directory";
restTemplate.put(url,
directoryName[0]);
return true;
}
}
And the stack trace looks like:
09-05 11:32:05.081: E/AndroidRuntime(535): Caused by: java.lang.NullPointerException
09-05 11:32:05.081: E/AndroidRuntime(535): at org.springframework.http.client.SimpleClientHttpResponse.getStatusCode(SimpleClientHttpResponse.java:62)
09-05 11:32:05.081: E/AndroidRuntime(535): at org.springframework.web.client.DefaultResponseErrorHandler.hasError(DefaultResponseErrorHandler.java:46)
09-05 11:32:05.081: E/AndroidRuntime(535): at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:476)
09-05 11:32:05.081: E/AndroidRuntime(535): at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:438)
09-05 11:32:05.081: E/AndroidRuntime(535): at org.springframework.web.client.RestTemplate.put(RestTemplate.java:364)
09-05 11:32:05.081: E/AndroidRuntime(535): at com.artmaps.im.activity.HomeActivity$ChangeDirectory.doInBackground(HomeActivity.java:192)
09-05 11:32:05.081: E/AndroidRuntime(535): at com.artmaps.im.activity.HomeActivity$ChangeDirectory.doInBackground(HomeActivity.java:1)
09-05 11:32:05.081: E/AndroidRuntime(535): at android.os.AsyncTask$2.call(AsyncTask.java:264)
09-05 11:32:05.081: E/AndroidRuntime(535): at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)
09-05 11:32:05.081: E/AndroidRuntime(535): ... 5 more
which points to the restTemplate.put(url, directoryName[0]);
call
I've tried putting in statements on the server side, and when these NullPointer
s are thrown it appears that the request isn't received by the server as the statements aren't reached.
Any help as to the cause of the NullPointerException
would be very much appreciated as i'm pretty confused by this, especially as sometimes it works and sometimes it doesn't
EDIT: After looking around some more it appears there is a bug in the Spring for Android framework, as per https://jira.springsource.org/browse/ANDROID-102
This helps a little, but still doesn't explain why it works sometimes and only fails when interacting with the server based in Azure
Upvotes: 3
Views: 7819
Reputation: 1277
I solved upgrading to 1.0.1 Spring Framework and using String.format with Locale for the URL and ClientHttpFactory.
private class ChangeDirectory extends AsyncTask< String, Void, Boolean >
{
@Override
protected Boolean doInBackground(String... directoryName) {
RestTemplate restTemplate = new RestTemplate(true); // default converters
restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory());
String url = String.format(Locale.getDefault(),"http://%s/ui/android/cloud/change_directory", Constants.DNS);
restTemplate.put(url, directoryName[0]);
return true;
}
}
I hope this works for you.
Upvotes: 0
Reputation: 5011
I had the same issue while implementing a custom ClientHttpRequestFactory
extending SimpleClientHttpRequestFactory
. I would also get random NullPointerExceptions.
The solution was to extend from HttpComponentsClientHttpRequestFactory
instead of SimpleClientHttpRequestFactory
and perform the necessary logic in the postProcessHttpRequest(HttpUriRequest httpRequest)
method.
Here's a quick example of a customized HttpComponentsClientHttpRequestFactory
accesing and setting the user-agent field in the header:
public class CustomHTTPRequestFactory extends HttpComponentsClientHttpRequestFactory {
public CustomHTTPRequestFactory() {
super();
}
@Override
protected void postProcessHttpRequest(HttpUriRequest httpRequest) {
httpRequest.setHeader("User-Agent", "my-user-agent-string");
super.postProcessHttpRequest(httpRequest);
}
}
Upvotes: 1
Reputation: 38168
If you are getting this error, chances are that you have a problem with Gzip compression : Here is work around, tested on ICS & JB, but that should work everywhere :
Inspired from http://android-developers.blogspot.fr/2011/09/androids-http-clients.html
RestTemplate restTemplate = new RestTemplate() {
@Override
protected ClientHttpRequest createRequest( URI url, HttpMethod method ) throws IOException {
ClientHttpRequest request = super.createRequest( url, method );
HttpHeaders headers = request.getHeaders();
headers.setAcceptEncoding( ContentCodingType.GZIP );
return request;
}
};
if ( Build.VERSION.SDK_INT < Build.VERSION_CODES.FROYO ) {
System.setProperty( "http.keepAlive", "false" );
}
try {
long httpCacheSize = 10 * 1024 * 1024; // 10 MiB
File httpCacheDir = new File( getCacheDir(), "http" );
Class.forName( "android.net.http.HttpResponseCache" ) //
.getMethod( "install", File.class, long.class )//
.invoke( null, httpCacheDir, httpCacheSize );
} catch ( Exception httpResponseCacheNotAvailable ) {
Ln.v( "Http cache disabled" );
}
Where Ln is RoboGuice Log library.
Upvotes: 1
Reputation: 191
Thought I'd post what I found in case anybody else gets stuck on this. It turned out to be somehow related to the bug I posted in the edit to my question.
I got around this by removing the Spring RestTemplate
calls and using DefaultHttpClient
instead.
E.g. changing this:
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add( new StringHttpMessageConverter() );
String url = "http://" + Constants.DNS +"/ui/android/cloud/change_directory";
restTemplate.put(url,
directoryName[0]);
return true;
to this:
String url = "http://" + Constants.DNS +"/ui/android/cloud/change_directory";
try {
HttpClient client = new DefaultHttpClient();
HttpPut putRequest = new HttpPut(url);
putRequest.setEntity(new StringEntity(directoryName[0]));
client.execute(putRequest);
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
Upvotes: 2