Tuesday, October 1, 2013

Mindroid - Android everywhere

Mindroid is an application framework that lets you create applications using a set of reusable components - just like Android. The name Mindroid has two different meanings. On one hand Mindroid is a minimal set of core Android classes and on the other hand these classes also form Android's mind (at least in my opinion). There are three versions of Mindroid focusing on different domains.

The feature-richest version is Mindroid.java. It is a complete application framework intended for machine-to-machine (M2M) communication projects and the Internet of Things. It is almost completely Android API-compliant with minor exceptions since Mindroid even runs on Java v1.4 (Java SE and Jave ME CDC) platforms like IBM's J9 Java virtual machine on Qualcomm's modem chipsets running an L4 microkernel OS. Mindroid.java also includes an application framework documentation. For the future, I plan to make Mindroid run on Java ME 8.

Another version of Mindroid is Mindroid.cpp. It is written in C++ using reference counting and other nice gimmicks. Currently, Mindroid.cpp is not really a complete application framework but focuses on Android's messaging and concurrency features. Nevertheless, it provides all messaging abstractions for implementing a lot of applications - the Android Transporter Player is one such example. At the moment Mindroid.cpp runs on POSIX operating systems like Linux, Android or QNX Neutrino RTOS and it uses dynamic memory allocations.

The third version is Mindroid.ecpp. This deeply embedded version of Mindroid is similar to Mindroid.cpp but it runs on tiny embedded operating systems like CMSIS-RTOS for ARM Cortex-M processors and it also runs on AUTOSAR-OS for various embedded processors. Such deeply embedded environments often lack dynamic memory allocation which is also not required by Mindroid.ecpp.

Why?

One of the main reasons for building Mindroid is the fantastic Actor implementation of Google's Android operating system. This actor model is the core building block of Android's whole software architecture. In all versions of Mindroid (and Android) an Actor is implemented by the Looper and Handler classes. A Looper has a thread and a message queue. The threads blocks on the message queue waiting for something to do. To put something into a message queue there is the Handler class. Each Handler also has a handleMessage method for processing messages. An example of this scenario is given in the documentation of the Looper class. Each Handler is attached to exactly one message queue (and thus to one Looper) at creation time. A message object always refers to its Handler that will process it later. Now think of software components in terms of Handlers. Software components (Handlers) talk to each other by passing messages. For example, you can run each Handler on its own Looper (thread) if you like. But you are also allowed to put multiple Handlers on the same Looper. (Android is also able to put multiple Handlers in different operating system processes that communicate with each other using the Binder IPC.) This behavior (software architecture) is changeable at will without a need to change the inner workings of a software component.

How?

Here are "Hello World" examples (using two Handlers) for all versions of Mindroid. One Handler prints "Hello " and the other one "World!" once in a second resulting in "Hello World!" outputs.

Mindroid.java
public class HelloWorld extends Service {
  private static final String LOG_TAG = "HelloWorld";
  private static final int SAY_HELLO = 0;
  private static final int SAY_WORLD = 1;
 
  private Handler mHelloHandler = new Handler() {
    public void handleMessage(Message message) {
      switch (message.what) {
      case SAY_HELLO:
        System.out.print("Hello ");
        mWorldHandler.obtainMessage(SAY_WORLD)
          .sendToTarget();
        break;
      }
    }
  };
 
  private Handler mWorldHandler = new Handler() {
    public void handleMessage(Message message) {
      switch (message.what) {
      case SAY_WORLD:
        System.out.println("World!");
        Message hello = mHelloHandler
          .obtainMessage(SAY_HELLO);
        mHelloHandler.sendMessageDelayed(hello, 1000);
        break;
      }
    }
  };

  public void onCreate() {
    mHelloHandler.obtainMessage(SAY_HELLO)
      .sendToTarget();
  }

  public void onDestroy() {
    mHelloHandler.removeMessages(SAY_HELLO);
    mWorldHandler.removeMessages(SAY_WORLD);
  }

  public IBinder onBind(Intent intent) {
    return null;
  }
}
Mindroid.cpp
static const int SAY_HELLO = 0;
static const int SAY_WORLD = 1;

class HelloHandler : public Handler {
public:
  HelloHandler(const sp<Handler>& worldHandler) :
      mWorldHandler(worldHandler) {
  }

  virtual void handleMessage(const sp<Message>& msg) {
    switch (msg->what) {
    case SAY_HELLO:
      printf("Hello ");
      sp<Message> message = mWorldHandler
        ->obtainMessage(SAY_WORLD);
      message->metaData()->putObject("Handler", this);
      message->sendToTarget();
      break;
    }
  }

private:
  sp<Handler> mWorldHandler;
};

class WorldHandler : public Handler {
public:
  WorldHandler() {
  }

  virtual void handleMessage(const sp<Message>& msg) {
    switch (msg->what) {
    case SAY_WORLD:
      printf("World!\n");
      sp<Handler> helloHandler = msg->metaData()
        ->getObject<Handler>("Handler");
      sp<Message> message = helloHandler
        ->obtainMessage(SAY_HELLO);
      helloHandler->sendMessageDelayed(message, 1000);
      break;
    }
  }
};

int main() {
  Looper::prepare();
  sp<Handler> worldHandler = new WorldHandler();
  sp<Handler> helloHandler = 
    new HelloHandler(worldHandler);
  helloHandler->obtainMessage(SAY_HELLO)
    ->sendToTarget();
  Looper::loop();

  return 0;
}
Mindroid.ecpp
static const int SAY_HELLO = 0;
static const int SAY_WORLD = 1;

class HelloHandler : public Handler {
public:
  HelloHandler(Handler& worldHandler) :
      mWorldHandler(worldHandler) {
  }

  virtual void handleMessage(const Message& msg) {
    switch (msg.what) {
    case SAY_HELLO:
      printf("Hello ");
      mWorldHandler.obtainMessage(mMessage,
          SAY_WORLD);
      mMessage.obj = this;
      mMessage.sendToTarget();
      break;
    }
  }

private:
  Handler& mWorldHandler;
  Message mMessage;
};

class WorldHandler : public Handler {
public:
  WorldHandler() {
  }

  virtual void handleMessage(const Message& msg) {
    switch (msg.what) {
    case SAY_WORLD:
      printf("World!\n");
      Handler* helloHandler = (Handler*) msg.obj;
      helloHandler->obtainMessage(mMessage,
          SAY_HELLO);
      helloHandler->sendMessageDelayed(mMessage,
          1000);
      break;
    }
  }

private:
  Message mMessage;
};

static uint8_t sHelloHandler[sizeof(HelloHandler)];
static uint8_t sWorldHandler[sizeof(WorldHandler)];

int main() {
  Message message;

  Looper::prepare();
  Handler* worldHandler =
    new (sHelloHandler) WorldHandler();
  Handler* helloHandler =
    new (sWorldHandler) HelloHandler(*worldHandler);
  helloHandler->obtainMessage(message, SAY_HELLO);
  message.sendToTarget();
  Looper::loop();

  return 0;
}

For more information on Mindroid please consult the Mindroid.java documentation and the examples provided with the application frameworks on GitHub.

No comments:

Post a Comment