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

Design of RoboSpice

stephanenicolas edited this page Jan 20, 2013 · 12 revisions

:with_toc_data

Overview

RoboSpice consists of different modules that are organized in layers, like an onion :

Overview of RoboSpice'sdesign

Module RoboSpice Cache

The very inner core of RoboSpice is made up of RoboSpice Cache. this module offers persistency to POJOs. It can simply save them in any format and load them from any format. Of course, this behavior is defined in the abstract class ObjectPersister but it is possible to extend it to support any kind of persistency.

ObjectPersister

We will see that extensions of RoboSpice offer various implementation of the class ObjectPersister. The RoboSpice Cache also provides a few of them, for saving & loading data as :

  • simple strings in text files
  • binary data using java.io.Inputstream
  • big binaries using java.io.Inputstream is alternative when you deal with larger data (e.g. bigger images), it will reduce memory consumption.

CacheManager

From a software design point of view, the RoboSpice Cache module implements the Chain Of Responsability design pattern :

  • The CacheManager holds an ordered list of ObjectPersister instances.
  • Every ObjectPersister instance knows the POJO class it can save and it will be responsible for saving / loading it.
  • When the CacheManager is asked to load/save some POJO, it will be passed 2 parameters :
    • a CacheKey
    • a POJO Class
  • With that information, the CacheManager will ask all ObjectPersisters whether they can handle the required class.
    • If so, the ObjectPersister will load/save it.
    • If no ObjectPersister inside the CacheManager can persist that Class, it will be logged.

ObjectPersisterFactory

Suppose you use REST requests : you are used to request different POJO classes. The results of the requests are all formatted using JSON. You want the CacheManager to save those data. But it would be quite painfull to add an ObjectPersister for each class of POJO you are interested in. MoreOver those persisters are gonna load & save the data exactly in the same way for each class...

ObjectPersisterFactory come to the rescue. They can create ObjectPersister instances on demand for a specific class. You can add ObjectPersisterFactory instances to the CacheManager Chain Of Responsability in the same way as an ObjectPersister as both classes implement a common interface.

You can control the set of classes an ObjectPersisterFactory can handle via its constructor. By default, every ObjectPersisterFactory instance will try to handle all classes and create an ObjectPersister for them.

Module RoboSpice Core

This module is the conceptual core of RoboSpice. This is the module that contains everything related to

  • executing requests
  • using an Android Service to process them
  • get the result or errors back in the main thread.

Long running background tasks, not only networking

Historically, RoboSpice was designed to handle network requests. Nevertheless, we quickly realized that it was indeed a good idea to execute every long running background task inside an Android Service. Networking is long (or potentially long) but RoboSpice offers a solution for long processing, long data access, etc. The advantages of using a Service remain exactly the same.

RoboSpice can be used in a totally network unaware mode. We provide a clear sample for this case in samples Section.

Networking by default, with permissions

By default, as we told, Robospice has been designed to ease network processing. Thus, every app using RoboSpice, except if it explicitly uses non default NetworkStateChecker (see above) will have to declare a few permissions in its AndroidManifest.xml file :

  • permission to use internet
  • permission to query the state of the network

Protocole agnosticism

RoboSpice Core Module doesn't rely on any network protocole. It has been designed to support all of them and is not tied to a single one. This is true for all TCP/IP layer. It's versatility depends on this feature. That's the reason why RoboSpice will never deal with HTTP error codes for instance.

Nevertheless, the extensions provide various mechanisms to deal with specific network protocols. They, indeed provide out-of-the-box support for advanced Object serialization/deserialization from/to the network like JSON, XML, etc.

SpiceRequest

A SpiceRequest is a basic RoboSpice object that performs the actual background task. Developers will subclass this class and override the loadDataFromNetwork method and "do their stuff" here.

SpiceRequest instances will be processed inside an Android Service. In other words, the loadDataFromNetwork will be called inside by Android Service. Request processing is multi-threaded on RoboSpice.

SpiceRequest is a generic class. You will have to parametrize it with the class of the result you want from loadDataFromNetwork. For instance a SpiceRequest that fetches tweets from Tweeter will be declared as a SpiceRequest.

The result returned by loadDataFromNetwork is stored in cache using the RoboSpice Cache module (see above). When an app executes a SpiceRequest, it can decide if hits from the cache are accepted or not, by passing a "maximum expiry date". If no hits is found in the cache, and is recent enough, then loadDataFromNetwork will be called again (and the result stored in cache).

SpiceRequest are identified inside RoboSpice by a "compound" key made of :

  • a cacheKey given at execution
  • the class of their result

SpiceRequest can be cancelled at any time. RoboSpice will drop them off as soon as possible (and notify their listeners of their cancellation, see below).

While RoboSpice processes SpiceRequest instances inside a Service, applications will receive the result of the loadDataFromNetwork method on the UI Thread.

RequestListener

RequestListener are basic RoboSpice objects that will get notified of the processing of a SpiceRequest. All notifications will occur on the UI Thread.

Typically, RequestListener are inner classes of the "launching context" : the Activity, Fragment (or Application or Service, etc.) that executes the SpiceRequest.

To create a RequestListener, create a new class that implements RequestListener and add it 2 methods :

  • one that will be called in case of success
  • the second in case of failure

Both methods, have arguments that will carry either the result of the SpiceRequest or an exception if a problem occurred.

RequestProgressListener

RequestProgressListener is a second interface that allows a RequestListener to get aware of the progress of the processing of a SpiceRequest.

SpiceService

Every application using Robospice has to declare (at least) a SpiceService in its AndroidManifest.xml file.

A SpiceService is an Android Service that can process any SpiceRequest instance. It owns a CacheManager, a NetworkStateChecker and a set of SpiceRequest instances to execute and their associated RequestListener instances.

It is responsible for :

  • queueing requests
  • processing them (using multi-threading). This involves
    • checking if the result of SpiceRequest is stored by its CacheManager
    • calling the loadFromNetwork of the SpiceRequest
    • saving the result of SpiceRequest via its CacheManager
  • notifying every RequestListener associated to the SpiceRequest

Extensions provide all setup SpiceService subclass that can be used out of-the-box to ease specific network data formats and cache them using these formats.

SpiceManager

SpiceManager is a basic RoboSpice Object that executes requests. SpiceManager instances are bound to "launching contexts" : every context (Activity or Fragment or etc.) that wants to execute SpiceRequests has to create its own SpiceManager and bind it to its life cycle.

The SpiceManager is aware of life cycle changes and, when the "launching context" gets destroyed, it will remove all RequestListener instances that would have notified the dying context. Thus SpiceManager instances have life cycles too.

From a software design point of view, SpiceManager instances are an asynchronous façade for using a SpiceService : they will bind to a SpiceService and ask it to effectively process every SpiceRequest and notify their RequestListeners. As binding to a service is asynchronous on Android, a SpiceManager will wait to be bound, queuing requests in the meantime, and pass them to the SpiceService when bound to it.

Due to the Architecture of Android, multiple SpiceManager instances can bind to a single shared SpiceService. If there is no SpiceService, a SpiceManager will start one when executing a first request.

Extensions

Most extensions provide both additional persistence capacities to RoboSpice Cache Module and helpers to handle a specific network exchange formats like JSON (or XML or protobuf etc.).

The goal of extensions is to enhance existing adopted technologies and provide them with both caching and the ideal context of execution : an Android Service.

We consider extensions as extensions, and they are not the core of RoboSpice. The RoboSpice team is open to add new extensions if you want to contribute some.

Spring Android module

This extension has it own wiki page.

This module is composed of :

  • classes that extend SpiceRequest and SpiceService, helpers to create Spring Android queries.
  • classes dedicated to persistence of Spring Android data formats.

Google Http Java Client module

This extension has it own wiki page.

This module is composed of :

  • classes that extend SpiceRequest and SpiceService, helpers to create Google Http Java Client queries.
  • classes dedicated to persistence of Google Http Java Client data formats.

Retrofit module

This extension has it own wiki page.

This module is composed of :

  • classes that extend SpiceRequest and SpiceService, helpers to create Retrofit queries.
  • classes dedicated to persistence of Retrofit data formats.

ORMLite module

This extension has it own wiki page.

This module is composed of :

  • classes dedicated to persistence of POJOs inside a database using ORMLite.

UI Spice List module

This extension has it own wiki page.

This module is composed of :

  • classes dedicated to display Android ListViews containing images loaded from the network.

Samples

Since release 1.4.0 of RoboSpice, we provide samples for all the core modules and extensions. Samples are located in the RoboSpice Samples repo on GitHub. They are standalone projects, as simple as possible to give examples of RoboSpice's modules usages.

We are open to adding new samples if you want to contribute one.

Samples work both with a maven/ADT configuration and an ant/ADT configuration.

Git architecture

RoboSpice uses 2 GitHub repositories :

  • one for core and extensions modules
  • the second one for the samples

This allows us to have a more modular permission approach for contributors and follows elegantly our maven architecture.

Maven architecture

All modules are composed of :

  • a parent artefact that manages dependencies and plugins specific to a module
  • a child artefact for the module himself (a jar)
  • a child artefact for testing the module

All parent artefacts have a common parent artefact that manages dependencies and plugins commons to modules.

Naming convention of the modules and artefact is strict since 1.4.0 and will not evolve for 1.X further releases. For historic reasons, the RoboSpice Core maven artefact is not called robospice-core but robospice.

Core and extensions modules can be built separately, with or without tests, and altogether.

Samples, on the other hands, don't constitute a whole but are strictly autonomous projects and maven artefacts (that should never get deployed to global maven repositories). Samples have been designed to demonstrate both RoboSpice programmatic usage and dependency management using both maven and ant.

Testing & Continuous Integration & Quality

RoboSpice has over 140 tests. The RoboSpice team is focused on testing and testability. Robustness is our main goal.

RoboSpice is under Continuous Integration at CloudBees. All builds are monitored under PMD + Findbugs + Checkstyle to ensure maximum quality.

We are open to new testing techniques, new tests and new quality analysis tools or peer reviews.