1-安卓底层--基础
先看看代码: java 调用 C/C++ 代码
1.TestJni.java
public class TestJni{
static{
System.loadLibrary("xxx"); //加载 libxxx.so 库文件
}
public native void hello(); //本地方法,在so文件里实现
public static void main (String [] args){
TestJni d = new TestJni();
d.hello(); //调用库文件里面的hello方法
}
}
2.编译java程序 javac TestJni.java
生成 TestJni.class
3.通过javah生成jni接口 javah TestJni
生成 TestJni.h
4.新建xxx.c 文件 然后实现接口
#include<jni.h>
JNIEXPORT void JNICALL Java_TestJni_hello(JNIEnv * env, jobject obj){
printf("hello world\n");
}
5.编译成so库文件 ( linux 动态库命名规则 lib + 库名 + .so window 库名+.dll )
gcc -shared -fPIC xxx.c -o libxxx.so -I /usr/lib/jvm/java-7-openjdk-amd64/include/
- -I 指定头文件的路径 -L 指定库的路径 -l更上名字 -lm -lsqlite3
6.指定动态库的路径 export LD_LIBRARY_PATH=:
7.运行java TestJni
* jni.h 在编译android源码时要安装jdk 5.0之前 直接下载甲骨文的jdk 5.0之后 要安装openjdk (sudo apt-get install openjdk+版本
)注意 android 和jdk的版本有对应关系(android 官网)
* Java_TestJni_hello 接口的名字 命名规则 Java_+类名_+本地方法名 接口的返回值和方法的返回值一致
* JNIEnv jni总管 他是一个函数指针数组的首地址 成员为函数指针 jobject java对象
第二种
第二种方式的jni实现
vi /usr/lib/jvm/java-7-openjdk-amd64/include/linux/jni.h
1. 完成入口函数
1944 JNIEXPORT jint JNICALL
1945 JNI_OnLoad(JavaVM *vm, void *reserved);
2. 在入口函数里面实现 一下三步
2.1 获得java虚拟机环境
jint (JNICALL *GetEnv)(JavaVM *vm, void **penv, jint version);
2.2 找到先关的类
jclass (JNICALL *FindClass)
226 (JNIEnv *env, const char *name);
* (*env)->FindClass(env, "java/lang/String")
2.3 注册
720 jint (JNICALL *RegisterNatives)
721 (JNIEnv *env, jclass clazz, const JNINativeMethod *methods,jint nMethods);
完成 JNINativeMethod 这个结构体 作用是本地方法和本地函数的映射关系
180 typedef struct {
181 char *name; //本地方法的名字
182 char *signature; // 本地方法的签名
183 void *fnPtr; // 相对应的本地函数
184 } JNINativeMethod;
函数签名通常是以下结构:
- 返回值 fun(参数1,参数2,参数3);
- 其对应的Jni方法签名格式为:(参数1参数2参数3) 返回值
- 注意:
- 函数名,在Jni中没有体现出来
- 参数列表相挨着,中间没有逗号,没有空格
- 返回值出现在()后面
- 如果参数是引用类型,那么参数应该为:L类型;
第一种jni 和第二种jni 实现方式有何不一样
第二种有入口函数 可以对jni 做一些初始化工作
第二种方式是通过 jninativemethod 这个结构体来匹配的
第一种是靠名字匹配的
实现接口文件
1 实现JNI_OnLoad 函数
2 是在入口函数里面获得jvm 环境变量 通过 GetEnv 这个函数
3 找类 findclass();
4 注册 RegisterNatives method 这个结构体
5 实现 method 这个结构体 让java的本地方法 和 jni的本地函数绑定在一起
写法
TestJni.java
public class TestJni{
static {
System.loadLibrary("native");
}
public native int hello(int i,char j);
public static void main (String [] args){
TestJni d = new TestJni();
d.hello(12,'r');
}
}
native.c
#include <jni.h>
jint Jhello(JNIEnv *env,jobject obj,jint i,jchar j){
printf("%d\t%c\n",i,j);
}
//函数数组 ,
//参数1 java 里写的本地方法名
//参数2 签名(看下面的图)头文件里自动生成
//参数3 调用的函数的指针
JNINativeMethod method[] = {
"hello","(IC)I",(void *)Jhello,
};
JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM *vm, void *reserved)
{
JNIEnv *env;
jclass cls;
if((*vm) -> GetEnv(vm,(void **)&env,JNI_VERSION_1_4))
return JNI_ERR;
cls = (*env) ->FindClass(env,"TestJni");
if(cls == NULL) return JNI_ERR;
//注册函数
(*env) -> RegisterNatives(env,cls,method,sizeof(method)/sizeof(JNINativeMethod));
return JNI_VERSION_1_4;
}
签名类型
两者之间的数据类型
java传数组
TestJni.java
public class TestJni{
static {
System.loadLibrary("native");
}
public native int hello(int []arr,int len);
public static void main (String [] args){
int []ibo = {12,13,14,15};
TestJni d = new TestJni();
System.out.println(d.hello(ibo,ibo.length));
}
}
native.c
#include <jni.h>
jint Jhello(JNIEnv *env,jobject obj,jintArray arr,jint num){
jint *carr;
jint i, sum = 0;
carr = (*env)->GetIntArrayElements(env, arr, NULL);
if (carr == NULL)return 0;
for (i=0; i<num; i++) sum += carr[i];
(*env)->ReleaseIntArrayElements(env, arr, carr, 0);
return sum;
}
JNINativeMethod method[] = {
(char *)"hello",(char *)"([II)I",(void *)Jhello,
};
JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM *jvm, void *reserved){
JNIEnv *env;
jclass cls;
if ((*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_2)) {
return JNI_ERR;
}
cls = (*env)->FindClass(env, "TestJni");
if (cls == NULL) return JNI_ERR;
(*env) -> RegisterNatives(env,cls,method,sizeof(method)/sizeof(JNINativeMethod));
return JNI_VERSION_1_2;
}