今天在看android硬件抽象层时需要用到Java的JNI调用,所以在网上找了些资料学习了一下,特别在这里记录一下。Java的JNI调用主要分为两种方式静态调用方式和动态调用方式。
静态调用方式:
首先确保你的pc上安装了jdk和g++,如果没有安装请安装后在试: 打开自己常用的目录,输入以下命令:
mkdir test
vim test/HelloWorld.java
package test;
public class HelloWorld
{
public static void main(String[] args)
{
System.loadLibrary("HelloWorld");
print_hello();
}
public static native final void print_hello();
}
其中最后声明的方法就是我们将要调用的方法,最后会在c++里实现,如果是声明的JNI方法一定要在方法前加上native关键字,否则系统无法识别到。
之后就可以调用java命令编译了。
javac test/HelloWorld.java
之后通过javah命令生成我们的头文件。实现该头文件定义的方法。
vim test/HelloWorld.cpp
#include "Hello.h"
#include <cstdio>
void Java_test_HelloWorld_print_1hello(JNIEnv *,jclass)
{
printf("helloworld asianux\n");
}
调用g++编译该头文件,生成一个动态库。
g++ test/HelloWorld.cpp -I /usr/lib/jvm/java-6-sun/include/ -I /usr/lib/jvm/java-6-sun/include/linux/ -fPIC --shared -o test/libHelloWorld.so
如果以上命令没有问题,那就可以运行了
java test.HelloWorld
但是一般都会出点问题,出错信息如下:
Exception in thread "main" java.lang.UnsatisfiedLinkError: no HelloWorld in java.library.path at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1738) at java.lang.Runtime.loadLibrary0(Runtime.java:823) at java.lang.System.loadLibrary(System.java:1028) at test.HelloWorld.main(HelloWorld.java:7)
这个时因为我们没有正确设置java的库路径导致的,执行以下命令设置该变量的值
export LD_LIBRARY_PATH=`pwd`/test
再次运行程序就可以看到正常输出了。
以上就是JNI中的静态调用。
动态调用:
动态调用相比与静态调用的主要区别有以下几点:
1.定义调用的JNINativeMethod
2.定义调用挂钩的函数
3.实现JNI_OnLoad函数
JNI_OnLoad是java jni技术的一个实现,每次java层加载System.loadLibrary之后,自动会查找改库一个叫JNI_OnLoad的函数,动态注册的时候,cpp可以通过实现JNI_OnLoad而完成jni的动态注册。
此处的代码和上面没什么差别,主要的差别在cpp文件的实现中,cpp的实现如下:
#include "Hello.h"
#include <cstdio>
static void android_print(JNIEnv *env,jclass clazz)
{
printf("hello asianux\n");
}
static JNINativeMethod gMethods [] =
{
{"print_hello","()V",(void *)android_print},
};
jint JNI_OnLoad(JavaVM *vm, void *reseved)
{
JNIEnv *env = NULL;
jint result = -1;
if(vm->GetEnv((void**)&env,JNI_VERSION_1_4) !=JNI_OK)
{
return -1;
}
char className[20] = {"dynamic/HelloWorld"};
jclass clazz = (env)->FindClass( (const char*)className);
if((env)->RegisterNatives(clazz,gMethods,1)<0)
{
return -1;
}
result = JNI_VERSION_1_4;
return result;
}
所不同的是这次调用时将不是直接查找print_hello方法的实现,而是查找该方法的映射函数。其中JNINativeMethod 的定义主要包含如下:
第一个字段是java层声明的方法也就是我们声明的print_hello方法
第二个字段相对来说就比较复杂一点了,其对应关系如下:
"()" 中的字符表示参数,后面的则代表返回值。例如"()V" 就表示void Func(); "(II)V" 表示 void Func(int, int); 具体的每一个字符的对应关系如下 字符 Java类型 C类型 V void void Z jboolean boolean I jint int J jlong long D jdouble double F jfloat float B jbyte byte C jchar char S jshort short 数组则以"["开始,用两个字符表示 [I jintArray int[] [F jfloatArray float[] [B jbyteArray byte[] [C jcharArray char[] [S jshortArray short[] [D jdoubleArray double[] [J jlongArray long[] [Z jbooleanArray boolean[]
上面的都是基本类型。如果Java函数的参数是class,则以"L"开头,以";"结尾,中间是用"/" 隔开的包及类名。
而其对应的C函数名的参数则为jobject. 一个例外是String类,其对应的类为jstring Ljava/lang/String; String jstring Ljava/net/Socket; Socket jobject
第三个参数就是我们最终调用函数的实现。按照静态调用的方式编译该程序,就可以看到运行结果。
对应到android的实现为:
java文件的实现:
package com.android.server;
import android.content.Context;
import android.os.IMemdevService;
import android.util.Slog;
import android.util.Log;
public class MemdevService extends IMemdevService.Stub
{
private static final String TAG = "MemdevService";
private int mPtr = 0;
MemdevService()
{
mPtr = init_native();
}
public void setVal(String val)
{
if(mPtr == 0)
{
return ;
}
setVal_native(mPtr,val);
}
public String getVal()
{
if(mPtr == 0)
{
return null;
}
String str = getVal_native(mPtr);
return str;
}
private static native int init_native();
private static native void setVal_native(int ptr,String val);
private static native String getVal_native(int ptr);
};
其中我们定义了三个方法,第一个方法返回一个init类型参数,第二个方法无返回值但是需要传递两个int类型的参数,第三个方法返回一个String类型的参数,带一个int类型的参数
其c++文件实现为:
#define LOG_TAG "MemdevServiceJNI"
#include "jni.h"
#include "JNIHelp.h"
#include "android_runtime/AndroidRuntime.h"
#include <utils/misc.h>
#include <utils/Log.h>
#include <hardware/hardware.h>
#include <hardware/memdev.h>
namespace android
{
static inline int memdev_device_open(const hw_module_t *module,struct memdev_device_t **device)
{
int fd = module->methods->open(module,MEMDEV_HARDWARE_MODULE_ID,(struct hw_device_t**)device);
return fd;
}
static void memdev_setVal(JNIEnv *env,jobject clazz,jint ptr,jstring value)
{
memdev_device_t *device = (memdev_device_t*)ptr;
LOGE("*****memdev_setVal ptr=%d\t value=%d\n",ptr,value);
if(!device)
{
LOGE("device memdev is not open");
return ;
}
const char* chars = env->GetStringUTFChars(value, NULL);
char *str = strdup(chars);
device->set_val(device,str);
}
static jstring memdev_getVal(JNIEnv *env,jobject clazz,jint ptr)
{
memdev_device_t *device = (memdev_device_t*)ptr;
if(!device)
{
LOGE("Device memdev is not open");
}
char *str;
int value = device->get_val(device,&str);
int strLen = strlen(str);
jstring jstr = env->NewStringUTF(str);
return jstr;
}
static jint memdev_init(JNIEnv *env,jclass clazz)
{
memdev_module_t *module;
memdev_device_t *device;
LOGI("Init HAL stub memdev......");
if(hw_get_module(MEMDEV_HARDWARE_MODULE_ID,(const struct hw_module_t **)&module) == 0);
{
LOGI("device with find ....");
if(memdev_device_open(&(module->common),&device) == 0)
{
LOGI("device memdev is open %d\n",device->fd);
return (jint)device;
}
LOGI("Failed to open device");
return 0;
}
}
static const JNINativeMethod method_table[] =
{
{"init_native","()I",(void*)memdev_init},
{"setVal_native","(ILjava/lang/String;)V",(void*)memdev_setVal},
{"getVal_native","(I)Ljava/lang/String;",(void*)memdev_getVal},
};
int register_android_server_MemdevService(JNIEnv *env)
{
return jniRegisterNativeMethods(env,"com/android/server/MemdevService",
method_table,NELEM(method_table));
}
};
在android源码树中编译无问题,正常通过。
参考自:http://blog.chinaunix.net/uid-10275706-id-3241480.html
浙公网安备 33010602011771号