Feb 3, 2009

Android STL/STD support

Article by Neil.He
万恶的android在STL和STD的支持上给了C/C++工程师带来了很大的麻烦。
Android的C++支持中只包含少的可怜的std namespace内容(还把std改成的android),和几乎空白的STL(standard Tamplate Library)。
这样的情况造成的严重的后果是,如果你想要porting一个lib到android上,那这个lib最好是连std和stl都不依赖,这对于开发来说无意于一种折磨。
在开发中发现了一点点信息,可能有一定的用处。
如果你的lib依赖stl的一些函数和对象,那么最好是平心静气的找找android的代码里是不是有类似的功能代码,android也是人开发的,也是用C/C++开发的,那么也会遇到类似的问题,所以也会有类似的处理方法,怎么办,缺什么补什么呗。
我在开发中需要用到vector,string,stream等对象,寻寻觅觅,发现还真有,/android/frameworks/base/media/libdrm/mobile2/中有个src/util/ustl-1.0,里面包含了一些stl的支持,虽然简单了点,但是基本上还是够我使用了。
仔细找发现还有很多地方有类似的实现,例如opencore等等。
经过这样看起来是解决了问题,可是android设计大牛们,是否想过这样大家要开发一个lib就要自己实现一部分标准库的功能,那么带来的代码冗余是可想而知的,这样的话,准备一个完整的标准库还是有必要的吧。难道android以后真的强悍到根本不用做中间层的lib,这点我始终怀疑...

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”