Android: Robospice with OkHttp
What is Robospice?
Taken from the projects Github
RoboSpice is a modular android library that makes writing asynchronous long running tasks easy. It is specialized in network requests, supports caching and offers REST requests out-of-the box using extension modules.
Essentially is makes it easier to persist ongoing network connection overs Activity and fragment lifecycle events i.e. config changes like rotation. For apps that dont want to restart the request each time this can be a really nice architecture approach and is putting into practise the concepts outlined in the getting-older but great talk Google I/O 2010 - Android REST client applications
Note: Robospice is great and I have used in a lot of apps I have built. There seems to be quite a trend of moving towards RxJava approaches to network comms which seems to make it easier to work with async opps and relativly simple to allow re-connection to existing calls etc. This is something I am playing with at the moment and will probably write a post about in time.
What is OkHttp
OkHttp is another lib from the fantastic dev team at Square. It offers a stable replacement for the
UrlConnection and now pretty much deprecated
HttpClient. It allows you to stop worrying about platform version specific issues with both the the Android supplied http classes and have the same code running on all targeted platform versions. Equally cool is that it conforms to the
UrlConnection interfaces so its pretty easy to drop in if you are putting into an existing app.
How to use
I would advise reading the Robospice docs / wiki but below are a few notes to get you up and running quickly
Add to gradle build file
Create your own SpiceService subclass
In the Manifest
<service android:name=".remote.spice.MySpiceService" android:exported="false" />
Create a base Activity / Fragment
Making a non-cached call
You can just leave the empty
CacheManager and as long as you dont pass in a call id when calling
execute (i.e. use the two arg constructor instead) the cache will be bypassed.
Its important to note that your app will not receive callbacks after
SpiceManager.shouldStop() is called so if your locking the
SpiceManager lifecycle calls to
onStop your code should be able to handle a missed response (due to activity / fragment being in the background OR recreated)
In most cases you will want to be using cached calls / pick up the response to the last call made regardless of lifecycle events.
You may want to use caching in your http layer based upon http headers as opposed to in RoboSpice - which may be another reason to use non-caching execute() methods.
Making a Cached call
Good for reconnecting to pending requests / operations after lifecycle events i.e. activity restarted) and / or not hitting the network for saved data.
*A note on call types and ids
In my mind there are two general types of calls in regards to caching. “One-time” calls (like login / submitting something etc) where the response is specific to what args / data was sent and “updating” calls where you may be grabbing the newest set of data for something (say tweets).
One-time For the call ids in regard to caching I generally use generating
UUIDs for one-time calls (see code below) which would need to be in the savedStateBundle. Combining with an in-mem LRU cache of size 1 (see below) would be a sensible strategy here.
Updating For updating calls a static final cache id may make more sense (or the call URL). You may want to combine this with a persistent file cache.
Updating caching example
This can be pretty simple, basically by
- checking if the call is pending already (using a static final cache key)
- if not pending start request
- if is pending reconnect to request
The important code for the above would be something like
One-time caching example
For “one-time” calls this is made up of the below steps (explained below)
- Generate a unique
idfor the api call *
- persist and load that
idin your saves state bundle
- pass the call id and cache duration check into the
- Check if need to reconnect with existing/previous call
- Make sure the
CacheManageris setup correctly
Creating Your CacheManager
For each object / result that you want to cache you will need to set this up via the
CacheManager instance you create inside your SpiceService subclass.
You create/use an
ObjectPersister subclass for each. The main subclasses of this are
which in turn have many useful subclasses (like
null. This can happen when checking the cache using
SpiceManager.getFromCache()and the cache is empty - make sure to account for this. Solution - check or wrap the callback with your own to add a more descriptive callback.
- Default auto-retries will be 3. Disable per request with
- Be careful if placing the
stopcalls in the
onStopmethods thats you dont instigate the request before this point, as this can result in edge cases where the response is lost and the UI is not updated. You can mitigate this with a combo of checking for cached / executing calls before starting your api call in onStart.
- If dont specify a class to your cache manager the cache will not be checked and the normal callbacks wont be called - this can be with a silent log if you have disabled log so watch out.
- Offline operations - you will still place the code in the network method override so this wont by default work when the device has no network connection. See here for more.