Android上层和C/C++层通信可以通过JNI实现,具体做法有两种:
1). 编写带有native方法的Java类;
Gpio.java 代码:
<span style="font-size:14px;"> package com.prowave.jnitest;
public class Gpio {</span>
<span style="font-size:14px;"> public String printJNI(String s) {
return native_printJNI(s);
}
...
private native String nativePrintJNI(String inputstr);
...
}</span>
2). 使用javah命令生成.h头文件;
自动生成头文件com_prowave_jnitest_Gpio.h(头文件的命名格式是javah根据java类的包名类名自动生成):
<span style="font-size:14px;"> #include <jni.h>
/* Header for class com_prowave_jnitest_Gpio */
#ifndef _Included_com_prowave_jnitest_Gpio
#define _Included_com_prowave_jnitest_Gpio
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_prowave_jnitest_Gpio
* Method: nativePrintJNI
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_prowave_jnitest_Gpio_nativePrintJNI
(JNIEnv *, jclass, jstring);
#ifdef __cplusplus
}
#endif
#endif</span>
方法名称Java_com_prowave_jnitest_Gpio_nativePrintJNI解释:Java_包名(层级关系用_分隔)_类名_方法名
3). 编写Gpio.c代码实现头文件中的方法
<span style="font-size:14px;"> #include <jni.h>
#include <stdio.h>
#include "com_prowave_jnitest_Gpio.h"
jstring JNICALL Java_com_prowave_jnitest_Gpio_nativePrintJNI(JNIEnv *env, jobject obj, jstring inputStr)
{
return (*env)->NewStringUTF(env, "Hello JNI! I am Native interface");
}</span>
4). 编写Android.mk文件,通过NDK编译生成so文件(NDK集成开发环境需要自行安装)
<span style="font-size:14px;"> LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := Gpio
LOCAL_SRC_FILES := Gpio.c
include $(BUILD_SHARED_LIBRARY)</span>
通过ndk-build编译最终生成libGpio.so文件,可供上层应用使用,比如调用Gpio.java的printJNI方法,最终获得C代码中Java_com_prowave_jnitest_Gpio_nativePrintJNI方法中返回的字符串。
注意事项:通过这种方式去实现JNI,Java代码中声明native方法时不能带下划线,比如nativePrintJNI方法不能写成native_printJNI,因为C/C++中的方法命名规则是“Java_包名(层级关系用_分隔)_类名_方法名”这种形式,以此来匹配Java类中的方法,下划线导致生成的头文件中方法名多出一个下划线,这样就匹配不上Java中定义的本地方法了。
生成头文件的方法有两个:
1). 用eclipse新建一个java项目或者android项目,在com/prowave/jnitest包下新建Gpio.java文件,添加上native方法,eclipse会自动编译java文件,生成Gpio.class文件在bin/class/com/prowave/jnitest/目录下。终端(window环境下可使用cygwin)进入bin同级目录,执行命令:javah -classpath bin/classes/ -d jni com.prowave.jnitest.Gpio,生成com_prowave_jnitest_Gpio.h头文件在jni目录。
-classpath:指定路径,这里只要指定com/prowave/jnitest这个包所在的路径就可以了,并不是指Gpio.class的路径,所以classpath的参数为bin/classes/;
-d jni:在当前路径生成一个jni文件夹;
com.prowave.jnitest.Gpio:java文件的包名类名。
2). 直接写一个xxx.java文件,通过javac xxx.java编译,生成xxx.class文件;新建文件目录,比如xxx.java文件里的包名是这样定义的:package com.prowave.jnitest;建立com/prowave/jnitest(这几个文件夹是层级关系),将xxx.class拷贝到最里面的文件夹jnitest;在当前目录打开终端,执行命令:javah -classpath . -d jni com.prowave.jnitest.Gpio,生成com_prowave_jnitest_Gpio.h。
1). 编写带有native方法的Java类;
Gpio.java 代码:
<span style="font-size:14px;"> package com.prowave.jnitest;
public class Gpio {
...
private native String native_printJNI(String inputstr);
private native String native_getChar();
...
}</span>
2). 定义C/C++文件,重写JNI_OnLoad()函数 (Gpio.c文件)<span style="font-size:14px;"> #include <jni.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 定义需要映射的Java类名称
#define JNIREG_CLASS "com/prowave/jnitest/Gpio"
/* java类中定义的本地方法的具体实现 */
jstring nativePrintJNI(JNIEnv *env, jobject obj, jstring inputStr)
{
const char *str = (const char *)(*env)->GetStringUTFChars( env,inputStr, JNI_FALSE );
(*env)->ReleaseStringUTFChars(env, inputStr, (const char *)str );
return (*env)->NewStringUTF(env, "Hello World! I am Native interface");
}
/* java类中定义的本地方法的具体实现 */
jstring printChar(JNIEnv *env, jobject obj)
{
return (*env)->NewStringUTF(env, "JNI Print Char Succ!");
}
/* 本地方法和java类中定义的native方法映射关系
* JNINativeMethod
* const char* name; java方法名称
* const char* signature; java方法签名
* void* fnPtr; c/c++的函数指针
*/
static JNINativeMethod methods[] = {
{ "native_printJNI", "(Ljava/lang/String;)Ljava/lang/String;", (void*)nativePrintJNI },
{ "native_getChar", "()Ljava/lang/String;", (void*)printChar },
};
/*
* Register several native methods for one class.
*/
static int registerNativeMethods(JNIEnv* env, const char* className,
JNINativeMethod* gMethods, int numMethods)
{
jclass clazz;
clazz = (*env)->FindClass(env, className);
if (clazz == NULL) {
return JNI_FALSE;
}
if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) != JNI_OK) {
return JNI_FALSE;
}
return JNI_TRUE;
}
/*
* Register native methods for all classes we know about.
*
* returns JNI_TRUE on success.
*/
static int registerNatives(JNIEnv* env)
{
if (!registerNativeMethods(env, JNIREG_CLASS,
methods, sizeof(methods) / sizeof(methods[0]))) {
return JNI_FALSE;
}
return JNI_TRUE;
}
/* This function will be call when the library first be load.
* You can do some init in the libray. return which version jni it support.
*/
jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
JNIEnv* env = NULL;
if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_6) != JNI_OK) {
return -1;
}
if (!registerNatives(env)) {
return -1;
}
return JNI_VERSION_1_6;
}</span>
3). 编写Android.mk文件,通过NDK编译生成so文件(NDK集成开发工具需要安装)。
<span style="font-size:14px;"> LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := Gpio
LOCAL_SRC_FILES := Gpio.c
include $(BUILD_SHARED_LIBRARY)</span>
通过这种方式注册的好处:
1>. 函数名称可以自定义,无须遵循特殊的命名格式
2>. 不需要通过javah生成头文件
3>. 将本地函数向VM进行登记,VM能更有效率的去找到registerNativeMethods。
4>. 可在执行期间进行抽换。由于gMethods[]是一个<名称,函数指针>对照表,在程序执行时,可多次呼叫registerNativeMethods()函数来更换本地函数之指针,而达到弹性抽换本地函数之目的。
附上案例源代码,下载地址:http://download.csdn.net/detail/visionliao/9532013