Jan 16, 2009

How to add a new module to Android

How to add a new module to Android
Article by Neil He

1. Java module
Because Android supports Java development directly, Java module could be add to application directly.
Refer to the following example:
The example is modified by HelloActivity provided by Android; add a class to this sample:
The new class named “abc”
package com.example.android.abc;

public class abc
{
       public int print(int i){return i;};
}

The HelloActivity code as following:
package com.example.android.helloactivity;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
import android.media.MediaPlayer;
import com.example.android.abc.abc;
/**
* A minimal "Hello, World!" application.
*/
public class HelloActivity extends Activity {
    public HelloActivity() {
    }

    /**
     * Called with the activity is first created.
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Set the layout for this activity.  You can find it
        // in res/layout/hello_activity.xml
        abc l_abc = new abc();
        if(l_abc.print(1) == 1)
        {
                TextView tv = new TextView(this);
               tv.setText("Hello, Android");
               setContentView(tv);
        }
    }
}
Note: The new class “abc” should be put to the directory HelloActivity/src/com/example/android/abc.
HelloActivity will compile it directly.

2. Dynamic  Library with C++
Actual, Android does not support C++ Application Development.
So, it need use JNI method to add a C++ Dynamic Library to Android.
The following procedure could show how to add a module to Android. The sample is add a new module to android framework
Step1:
Name the new module as “abc”.
Add a directory “abc” to Android/frameworks/base/ (You also could add to other location)
For compile the new module, the batch file (android/build/core/pathmap.mk) need be modified as following:
FRAMEWORKS_BASE_SUBDIRS := \
core \
graphics \
location \
media \
opengl \
sax \
services \
telephony \
wifi \
abc //Add for abc module compile
Step2:
Coding the C++ library, add a code directory to “abc”, write the code of library and the batch file for compiling, the code directory is named as “libabc” in following sample
“libabc” include three files:
test.h
The header file of the library, include library class definition. 
namespace android {
class NeilTest
{
public:
NeilTest(){};
~NeilTest(){};
int Neil(int i);
};
};
Note:
Because Android do not support standard std name space, it just support namespace android, and it do not support all function of glibc++, include string operation.
test.cpp
The implement of “NeilTest” class
#include "test.h"

using namespace android;
int NeilTest::Neil(int i)
{
return i;
}

Android.mk
The batch file of libabc
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

LOCAL_SRC_FILES:= \
test.cpp

LOCAL_SHARED_LIBRARIES := \
libui libcutils libutils

LOCAL_MODULE:= libabc
LOCAL_PRELINK_MODULE := false 

ifeq ($(TARGET_OS)-$(TARGET_SIMULATOR),linux-true)
LOCAL_LDLIBS += -ldl
endif

ifneq ($(TARGET_SIMULATOR),true)
LOCAL_SHARED_LIBRARIES += libdl
endif

LOCAL_C_INCLUDES := \
$(call include-path-for, graphics corecg)
#include $(LOCAL_PATH)/abc/common/coreapi/Android.mk
include $(BUILD_SHARED_LIBRARY)
Note: 
The module name is libabc
The src file include “test.cpp”
Step3:
Code Java interface for the new module.
This part need design a new java class for implement the function required.
In following sample, it is a class calling the function of libabc.
Create a java file “abc/java/android/abc/abc.java”
package android.abc;

public class abc
{
static
{
System.loadLibrary("abc_jni");
}
public native int print(int i);
}
The package name is android.abc
The class name is abc, include a native function “print” and load a library “abc_jni”.

Step4:
Finished the implement of C++ library and Java interface, need link them to implement Java interface calling C++ library.
Coding for JNI part for calling C++ library, the JNI part also implement with C++, however, the code of this part have some android rules.
First new a directory in “abc” as “jni”
“jni” include two files
android_abc_abc.cpp
For build this file, we could use following command to build a prototype and modify as android rules.
javac abc.java
javah abc
After this operation, we will get a header file of abc. However, it could not be used directly, it need be modified as android rules.
#define LOG_TAG "abc-JNI"
#include "jni.h"
#include "JNIHelp.h"
#include "android_runtime/AndroidRuntime.h"

#include "../libabc/test.h"
using namespace android;

static int
android_abc_abc_print(JNIEnv *env, jobject thiz, int i)
{
    NeilTest haha;
    return haha.Neil(i);
}
// ----------------------------------------------------------------------------

static JNINativeMethod gMethods[] = {
{"print",              "(I)I",                             (void *)android_abc_abc_print},
};
static const char* const kClassPathName = "android/abc/abc";
static int register_android_abc_abc(JNIEnv *env)
{
    jclass clazz;

    clazz = env->FindClass("android/abc/abc");
    if (clazz == NULL) {
        //LOGE("Can't find android/abc/abc");
        return -1;
    }
    return AndroidRuntime::registerNativeMethods(env,
                "android/abc/abc", gMethods, NELEM(gMethods));
}


jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
    JNIEnv* env = NULL;
    jint result = -1;

    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
        //LOGE("ERROR: GetEnv failed\n");
        goto bail;
    }
    assert(env != NULL);
    //neil add
  if (register_android_abc_abc(env) <>
        //LOGE("ERROR: register_android_abc_abc native registration failed\n");
        goto bail;
    }
    result = JNI_VERSION_1_4;

bail:
    return result;
}

Note:
Except the implement of the function “android_abc_abc_print”, it still should include the operation of register the function “jint JNI_OnLoad(JavaVM* vm, void* reserved)”
Android.mk
The bitch file.
ifneq ($(BUILD_WITHOUT_PV),true)
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

LOCAL_SRC_FILES:= \
android_abc_abc.cpp

LOCAL_SHARED_LIBRARIES := \
libopencoreplayer \
libopencoreauthor \
libandroid_runtime \
libnativehelper \
libabc

LOCAL_STATIC_LIBRARIES := 

LOCAL_C_INCLUDES += \
external/tremor/Tremor \
$(JNI_H_INCLUDE) \
$(call include-path-for, corecg graphics)

LOCAL_CFLAGS +=

LOCAL_LDLIBS := -lpthread

LOCAL_MODULE:= libabc_jni
LOCAL_PRELINK_MODULE := false 

include $(BUILD_SHARED_LIBRARY)

Note: the shared library should include “libabc”
After the 4 steps, the new module could be build with android building directly.
Just use the android make command, the new module could be compiled and register to android system.
An application could call the module with the java interface.
Import the class “android.abc.abc”