While researching possible technologies for implementing a publish/subscribe-based task distribution system for long-running, semi-autonomous/intelligent agents (with Android as a supported platform), ZeroMQ emerged as a sensible choice of message transport, given the exploding complexity of AMQP (Pieter Hintjens' August 2008 steering committee missive sums up my misgivings).
ZeroMQ vs. JeroMQ
The ZeroMQ/jzmq Android build instructions look pretty onerous, particularly for pre-3.0 versions (due to a
libuuid dependency)—JeroMQ (a pure Java implemention)—seemed like a natural choice. I haven't benchmarked anything like a real-world use case, though there are some promising numbers published by the JeroMQ authors.
Since the JeroMQ and jzmq (0.2.0 series) API structures are functionally identical, there shouldn't be any real cost associated with making a choice for exploratory reasons (barring showstopping bugs). The code snippets below adumbrate a minimal working example of a ZMQ client/server in request/response mode, running in a single process (an Android application). There are a dearth of Android-specific ZMQ examples around—I've endeavoured to use Android-specific concurrency abstractions where they seem idiomatic.
Note that the purpose of this example/tutorial is to provide examples of how to issue a request and process a response using a trivial message format with ZMQ—while all of the work is happening in a single process, the server & client threads can be viewed as separate applications; trivial adaptations of the Hello World request/response example applications (hwclient & hwserver) which are ubiquitous in ZMQ tutorials.
Let's start with a Handler which takes an instance (conforming to a trivial interface) to which it dispatches message events, and a key name identifying the
String datum within the corresponding Message's Bundle which holds the message payload.
Predictably, the interface isn't particularly jazzy.
The long-running server, which blocks on reads and instantly responds with a reversed version of the message payload is defined as a Runnable, taking a Handler which is used to communicate the content of incoming messages to interested parties.
The client/request-side is implemented as an AsyncTask which re-does all of its setup when run.
AsyncTask.execute takes a
String to use as the message body, and the constructor, as above, takes a
Handler to which received messages are dispatched (after being bundled).
The UI is incredibly simple, and the layout XML doesn't bear repeating. The three pieces of information required to make the Activity code intelligible are:
text_messageis the EditText at the top.
button_send_messageis the Button beside it.
text_consoleis the large TextView console below.
Wiring Everything Together
- The static methods in
Utilaren't particularly useful - the implementations have been omitted to reduce clutter.
Activity-lifecycle contingencies have been made in the above code.
- From the AsyncTask documentation: “When first introduced, AsyncTasks were executed serially on a single background thread. Starting with DONUT, this was changed to a pool of threads allowing multiple tasks to operate in parallel. Starting with HONEYCOMB, tasks are executed on a single thread to avoid common application errors caused by parallel execution.” Both seem like strange choices to me. In any case, this example doesn't use
THREAD_POOL_EXECUTOR, so client tasks won't run concurrently.
- Your application's manifest (
AndroidManifest.xml) will have to request
android.permissions.INTERNETif you're using the TCP transport, as in this example.
MQTT makes sense on a couple of axes, though it may be a slightly curious choice given the heterogeneity of clients in this use-case (it'd be easier to defend if clients were exclusively Android, or mobile-only). It may well be the subject of a follow-up blog-post.