Android - How to Send Gzipped JSON in HTTP Request
If you have ever developed an Android application which sends and consumes large amount of JSON data without gzipping then this post is probably for you. This is a very simple optimization technique that you can implement which vastly reduces the network latency and also benefits the users as well as internet. The idea is to transfer less data over the network and once you do that, it improves the speed of your application and helps users by reducing their mobile data usage (i.e saves $$$ in fact).
The AndroidHttpClient module offers a way to achieve this, when you’re sending JSON requests you can gzip the content of the request params and add some necessary HTTP headers, which will provide a hint to your server that it has to decode the content before it is usable. Needless to say that this technique is not just limited to sending JSON, you can of course use it if you’re sending CSV or text file over the network to server.
I’ll show a way how this can be done using a Rails backend (that’s what I use), but I presume this should not be too difficult to implement in case you’re not using Rails. I’ll be using JSON as an example here:
To encode or decode the JSON content you can use the following utility module in Android:
publicclassUtil{/** * Converts an InputStream to String * * @param is * @return * @throws IOException */publicstaticStringstreamToString(InputStreamcontent)throwsIOException{byte[]buffer=newbyte[1024];intnumRead=0;ByteArrayOutputStreambaos=newByteArrayOutputStream();while((numRead=content.read(buffer))!=-1){baos.write(buffer,0,numRead);}content.close();returnnewString(baos.toByteArray());}/** * Compresses the content of the request parameters (as a string). Sets * appropriate HTTP headers also so that the server can decode it properly. * * @param context Context * @param content The string request params, ideally JSON string * @param postReq The HttpPost request object * */publicstaticvoidsetCompressedEntity(Contextcontext,Stringcontent,HttpPostpostReq){try{byte[]data=content.getBytes("UTF-8");// if the length of the data exceeds the minimum gzip size then only// gzip it else it's not required at allif(content.length()>AndroidHttpClient.getMinGzipSize(context.getContentResolver())){// set necessary headerspostReq.setHeader("Content-Encoding","gzip");}// Compressed entity itself checks for minimum gzip size// and if the content is shorter than that size then it// just returns a ByteArrayEntitypostReq.setEntity(AndroidHttpClient.getCompressedEntity(data,context.getContentResolver()));}catch(UnsupportedEncodingExceptione){e.printStackTrace();}catch(IOExceptione){e.printStackTrace();}}/** * Extracts the response content. If the server response is compressed, then * it transparently decompresses the content. In order to indicate to server * that you can consume JSON response, use the following code to add the "Accept" * header: * * AndroidHttpClient.modifyRequestToAcceptGzipResponse(HttpRequest request) * * @param response * HttpResponse Object * @return String content of the HttpResponse */publicstaticStringgetIfCompressed(HttpResponseresponse){if(response==null)returnnull;try{InputStreamis=AndroidHttpClient.getUngzippedContent(response.getEntity());returnstreamToString(is);}catch(IOExceptione){e.printStackTrace();}returnnull;}}
The HTTP header, which indicates to server that the request content is gzipped, is Content-Encoding: gzip. On the Rails side you can use the following Rack middleware to decode JSON requests (you can set your webserver for example nginx to do the encoding on JSON responses):
A Rack middleware to decode the gzipped JSON, thanks to this gist by relistan:
Now put this in somewhere in your Rails web application directory for example lib/middleware, just make sure that the file is loaded when Rails boots. To include the file in Rails’ autoload path, add it like this:
Once that is done, then you need to add this tiny little Rack app as a middleware, and the important trick is to add it before the ActionDispatch::ParamsParser middleware in Rails 3:
application.rb
123
# Handle Compressed Requests, this middleware makes gzip content # handling transparent to the Rails stackconfig.middleware.insert_beforeActionDispatch::ParamsParser,"CompressedRequests"
That’s all you need both on client and server to send and receive Gzipped content. If you’re sending JSON from client and you wan’t rails to interpret it as JSON, then don’t forget to add Content-Type: application/json on your HTTP Request header.
Happy coding! If you have anything to say feel free to contact me on Twitter @arnabc