Sunday, August 21, 2011

Android messaging and concurrency (for native code development)

Android's messaging and concurrency framework (together with the Binder IPC mechanism) forms the basis of all Android applications and services. The messaging and concurrency framework is mainly based on the Thread, LooperMessageMessageQueue and Handler classes. For convenience there is also the AsyncTask class and for inter-process communication there are some other classes like Binder, Parcel and Messenger.

A Looper is used to run a message loop for a thread. Threads by default do not have a message loop associated with them; to create one, call Looper.prepare() in the thread that is to run the loop, and then Looper.loop() to have it process messages until the loop is stopped. Most interaction with a message loop is through the Handler class. A Handler allows you to send and process Message and Runnable objects associated with a thread's MessageQueue. Each Handler instance is associated with a single thread and that thread's message queue. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it - from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue. Furthermore, one can bind as many Handlers as he or she wants to a single thread / message queue for message handling.
There are two main uses for a Handler. First a Handler allows you to enqueue an action to be performed on a different thread than your own and furthermore it also enables you to schedule messages and runnables to be executed at some point in the future.
The AsyncTask class enables proper and easy use of concurrency in a multithread environment. For more information about this class see Google's "Painless Threading" article. (Paragraph source: Google Android Developer Reference).

Since the Android messaging and concurrency classes are only available to Java developers, I rewrote (parts of) them in C++ for native code development. The native Android messaging and concurrency framework is available here under the Apache 2.0 license. The C++ classes not only work for Android but also for Linux and QNX Neutrino RTOS. I haven't done a Windows port up till now, but it should be pretty easy to do so (e.g. using Win32 Events and the WaitForSingleObject function).
For native code development I also added closures (from Google's protocol buffers project) and delegates. Here are some examples about how to use the native C++ classes:

Basic Android messaging and concurrency example:
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "android/os/Looper.h"
#include "android/os/Handler.h"
#include "android/os/Thread.h"
#include "android/os/Message.h"
#include "android/os/CondVar.h"
#include "android/os/Closure.h"

using namespace android::os;

template<class T /*extends Handler*/>
class LooperThread :
    public Thread
{
public:
    LooperThread() :
        mLooper(NULL),
        mHandler(NULL),
        mCondVar(mLock),
        mIsDone(false) {  
    }

    virtual ~LooperThread() {
    }

    virtual void run() {
        Looper::prepare();
        mLock.lock();
        mLooper = Looper::myLooper();
        mHandler = new T();
        mCondVar.notifyAll();
        mLock.unlock();
        Looper::loop();
        mLock.lock();
        mIsDone = true;
        mHandler->removeCallbacksAndMessages();        
        mLooper = NULL;
        mHandler = NULL;
        mLock.unlock();
    }

    Looper* getLooper() {
        AutoLock autoLock(mLock);
        if (!mIsDone && mLooper == NULL) {
            mCondVar.wait();
        }
        return mLooper;
    }

    sp<T> getHandler() {
        AutoLock autoLock(mLock);
        if (!mIsDone && mHandler == NULL) {
            mCondVar.wait();
        }
        return mHandler;
    }

private:
    Looper* mLooper;
    sp<T> mHandler;
    Lock mLock;
    CondVar mCondVar;
    bool mIsDone;
};

class ExampleHandler : public Handler
{
public:
    virtual void handleMessage(const sp<Message>& message) {
        printf("ExampleHandler::handleMessage with"
            " ID %d by Looper %p\n",
            message->what, Looper::myLooper());
    }
};

class Test
{
public:
    void test(int32_t value) {
        printf("Test::test() called with value"
            " %d\n", value);
    }
};

static LooperThread<ExampleHandler> sLooperThread;
static Test sTest;

int main() {
    sLooperThread.start();
    sp<Handler> handler = sLooperThread.getHandler();

    handler->obtainMessage(1234)->sendToTarget();
    handler->postDelayed(newRunnable(sTest,
        &Test::test, 42), 1000);

    Thread::sleep(2000);

    sLooperThread.getLooper()->quit();
    sLooperThread.join();

    return 0;
}


AsyncTask example:
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "android/os/Looper.h"
#include "android/os/Handler.h"
#include "android/os/Thread.h"
#include "android/os/Message.h"
#include "android/os/Closure.h"
#include "android/os/LooperThread.h"
#include "android/os/AsyncTask.h"

using namespace android::os;

class ExampleAsyncTask :
    public AsyncTask<int32_t, int32_t, int32_t>
{
public:
    virtual void onPreExecute() {
        printf("ExampleAsyncTask::onPreExecute"
            " on thread %d\n",
            (int32_t) pthread_self());
    }

    virtual int32_t doInBackground(int32_t param) {
        printf("ExampleAsyncTask::doInBackground"
            " on thread %d with param %d\n",
            (int32_t) pthread_self(), param);
        Thread::sleep(250);
        int32_t sum = 0;
        int32_t i;
        for (i = 0; i < param / 2; i++) {
            sum++;
        }
        publishProgress(sum);
        Thread::sleep(250);
        for (; i < param; i++) {
            sum++;
        }
        return sum;
    }

    virtual void onProgressUpdate(int32_t value) {
        printf("ExampleAsyncTask::onProgressUpdate"
            " on thread %d with value %d\n",
            (int32_t) pthread_self(), value);
    }

    virtual void onPostExecute(int32_t result) {
        printf("ExampleAsyncTask::onPostExecute"
            " on thread %d with result %d\n",
            (int32_t) pthread_self(), result);
    }

    virtual void onCancelled() {
        printf("ExampleAsyncTask::onCancelled"
            " on thread %d\n",
            (int32_t) pthread_self());
    }
};

class AsyncTaskStarter : public Runnable
{
    virtual void run() {
        // AsyncTasks must be executed by a
        // Looper thread because of the
        // callback message handling
        ExampleAsyncTask* at1 =
            new ExampleAsyncTask();
        at1->execute(1234567);

        ExampleAsyncTask* at2 =
            new ExampleAsyncTask();
        at2->executeOnExecutor(AsyncTaskBase::
            THREAD_POOL_EXECUTOR, 123);        
    }
};

static LooperThread<Handler> sLooperThread;

int main() {
    sLooperThread.start();
    sp<Handler> handler = sLooperThread.getHandler();

    handler->post(new AsyncTaskStarter());

    Thread::sleep(2000);

    sLooperThread.getLooper()->quit();
    sLooperThread.join();

    return 0;
}

Click here to see some more sample code that demonstrates the native Android messaging and concurrency framework usage.

Here is the link to the repository: Android messaging and concurrency framework for native code development

Here is the Android messaging and concurrency framework with automatic reference counting.

Here is another link to some Android native Binder IPC example code.

Update:
The Android messaging and concurrency framework is now named Mindroid and is hosted on GitHub.

2 comments:

  1. Tutorial on Android Threads, Handlers and AsyncTask for Android Java development: http://www.vogella.de/articles/AndroidPerformance/article.html

    ReplyDelete
  2. Hi,
    Can the "Looper" object and its functionalities be used in native code ?
    Say I want to make the "main thread of a new process created from a UNIX style of forking " a looper ?

    Thanks

    ReplyDelete

Note: Only a member of this blog may post a comment.