Skip to content
This repository has been archived by the owner on Jan 14, 2018. It is now read-only.

Starter guide

Stéphane Nicolas edited this page Jun 16, 2014 · 28 revisions

You wanna see what RoboSpice looks like in action? Great! This is the page you are looking for.

This document is a tutorial to implement RoboSpice using the Spring Android module. It will :

  • perform a REST request to a web service (e.g. GitHub API)
  • convert JSON results to POJOs
  • cache them on disk
  • let you control cache expiry
  • notify you app, on the UI thread, when result is ready.

Please, note that the snippets in this page are excerpts of the Spring Android sample of RoboSpice.

RoboSpice usage

Setting up your project

Using RoboSpice

To use RoboSpice in your application, there are 4 steps :

Use a pre-set SpiceService

Release 1.4.0 of RoboSpice allows to use pre-set SpiceServices instead of creating your own SpiceService. For instance if you want to use Spring Android to both parse the results of your JSON network requests into POJOs, and cache the resulting POJOs using JSON on disk, simply add the following line to your AndroidManifest.xml file :

<service
  android:name="com.octo.android.robospice.JacksonSpringAndroidSpiceService"
  android:exported="false" />

You can now jump directly to the section Spice your activity, or read below if you want to learn how to create a custom SpiceService.

Create a SpiceService

This part is the most difficult, but you only have to do it once to enable all requests to be processed. You will define a subclass of SpiceService and declare it in the AndroidManifest.xml file.

public class JsonSpiceService extends SpringAndroidSpiceService {

  @Override
  public CacheManager createCacheManager( Application application ) {
    CacheManager cacheManager = new CacheManager();
    JacksonObjectPersisterFactory jacksonObjectPersisterFactory = new JacksonObjectPersisterFactory( application );
    cacheManager.addPersister( jacksonObjectPersisterFactory );
    return cacheManager;
  }

  @Override
  public RestTemplate createRestTemplate() {
    RestTemplate restTemplate = new RestTemplate();
    //find more complete examples in RoboSpice Motivation app
    //to enable Gzip compression and setting request timeouts.

    // web services support json responses
    MappingJacksonHttpMessageConverter jsonConverter = new MappingJacksonHttpMessageConverter();
    FormHttpMessageConverter formHttpMessageConverter = new FormHttpMessageConverter();
    StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
    final List< HttpMessageConverter< ? >> listHttpMessageConverters = restTemplate.getMessageConverters();

    listHttpMessageConverters.add( jsonConverter );
    listHttpMessageConverters.add( formHttpMessageConverter );
    listHttpMessageConverters.add( stringHttpMessageConverter );
    restTemplate.setMessageConverters( listHttpMessageConverters );
    return restTemplate;
  }
}

Then add this to your AndroidManifest.xml file :

<service
  android:name="<your package>.JsonSpiceService"
  android:exported="false" />

Spice your activity or fragment

In your activity, or a base class if you use one (preferred), add the following :

//------------------------------------------------------------------------
//this block can be pushed up into a common base class for all activities
//------------------------------------------------------------------------

//if you use a pre-set service, 
//use JacksonSpringAndroidSpiceService.class instead of JsonSpiceService.class
protected SpiceManager spiceManager = new SpiceManager(JsonSpiceService.class);


@Override
protected void onStart() {
  super.onStart();
  spiceManager.start(this);
}

@Override
protected void onStop() {
  spiceManager.shouldStop();
  super.onStop();
}

//------------------------------------------------------------------------
//---------end of block that can fit in a common base class for all activities
//------------------------------------------------------------------------

private void performRequest(String user) {
  MainActivity.this.setProgressBarIndeterminateVisibility(true);

  FollowersRequest request = new FollowersRequest(user);
  lastRequestCacheKey = request.createCacheKey();

  spiceManager.execute(request, lastRequestCacheKey, DurationInMillis.ONE_MINUTE, new ListFollowersRequestListener());
}

In the example above, you can see that the third argument passed to the execute() method is the constant DurationInMillis.ONE_MINUTE. If you pass a long value here, then it represents the maximum accepted age in milliseconds of cached data that a SpiceManager will accept.

There is also a constant named DurationInMillis.ALWAYS_EXPIRED which means that the data in cache will always be considered expired, regardless of how recent it is. Thus, the SpiceRequest will always perform a network call, and you will never get the data in cache. Conversely you can use DurationInMillis.ALWAYS_RETURNED which indicates that the client never considers a data in cache as expired, regardless of how recent it is. Data in cache will always be returned, a network call will only be made once.

There are also constants that represent common time durations, such as DurationInMillis.ONE_HOUR and DurationInMillis.ONE_WEEK provided as helpers to write durations in milliseconds (i.e. 4 * DurationInMillis.ONE_MINUTE).

Create a SpiceRequest and a RequestListener

You will repeat this step for each different kind of request. Those steps are easy and intuitive :

//Create a request in its own Java file, it should not an inner class of a Context
public class FollowersRequest extends SpringAndroidSpiceRequest<FollowerList> {

  private String user;

  public FollowersRequest(String user) {
    super(FollowerList.class);
    this.user = user;
  }

  @Override
  public FollowerList loadDataFromNetwork() throws Exception {

    String url = String.format("https://api.github.com/users/%s/followers", user);

    return getRestTemplate().getForObject(url, FollowerList.class);
  }

  /**
   * This method generates a unique cache key for this request. In this case
   * our cache key depends just on the keyword.
   * @return
   */
  public String createCacheKey() {
      return "followers." + user;
  }
}

As an inner class of your Activity (or other context), add a RequestListener that will update your UI. Don't worry about memory leaks, RoboSpice manages your activity's life cycle.

//inner class of your spiced Activity
private class ListFollowersRequestListener implements RequestListener<FollowerList> {

  @Override
  public void onRequestFailure(SpiceException e) {
    //update your UI
  }

  @Override
  public void onRequestSuccess(FollowerList listFollowers) {
    //update your UI
  }
}

Both methods will be executed inside the UI thread, RoboSpice handles this for you.

####Notes on fragments life cycle

Fragments are a bit more difficult to handle in Android. You should take care of two additional things when using a SpiceManager within a Fragment :

  • check if SpiceManager.isStarted() before stopping it as the fragment can be stopped before being started in some cases.
  • in your listeners, check if the Fragment.isAdded() before performing any manipulations on views as the fragment views can be destroyed before onStop is called in a Fragment.

###Define POJO classes

In this example, we used the following POJOs to receive a list of Tweets using Json via Jackson :

//We use ArrayList as base class since the root element is a JSON array
public class FollowerList extends ArrayList<Follower> {
  private static final long serialVersionUID = 8192333539004718470L;
}
@JsonIgnoreProperties(ignoreUnknown = true)
public class Follower {

  private String login;

  public String getLogin() {
      return login;
  }

  public void setLogin(String login) {
      this.login = login;
  }
}

You're done! Launch your activity, plug the performRequest() method to a Button in your Activity, and enjoy!

To know more about the RoboSpice modules, refer to their respective article on the wiki.