使用JNI的原则
总之,只有当你必须在同一进程中调用本地代码时,再使用JNI。
1、JAVA程序和本地程序使用TCP/IP或者IPC进行交互。
2、当用JAVA程序连接本地数据库时,使用JDBC提供的API。
3、JAVA程序可以使用分布式对象技术,如JAVA IDL API。
JNIEnv
JNIEnv 概念 : 是一个线程相关的结构体, 该结构体代表了 Java 在本线程的运行环境 ;
JNIEnv 与 JavaVM :
注意区分这两个概念;
– JavaVM : JavaVM 是 Java虚拟机在 JNI 层的代表, JNI 全局只有一个;
– JNIEnv : JavaVM 在线程中的代表, 每个线程都有一个, JNI 中可能有很多个JNIEnv;
JNIEnv 作用 :
– 调用 Java 函数 : JNIEnv 代表 Java 运行环境, 可以使用 JNIEnv 调用 Java 中的代码;
– 操作 Java 对象 : Java 对象传入 JNI 层就是 Jobject 对象, 需要使用 JNIEnv 来操作这个 Java 对象;
JNIEnv 体系结构
线程相关 : JNIEnv 是线程相关的, 即 在 每个线程中 都有一个 JNIEnv 指针, 每个JNIEnv 都是线程专有的, 其它线程不能使用本线程中的 JNIEnv, 线程 A 不能调用 线程 B 的 JNIEnv;
JNI 例子:
- Java 层
private native String printJNI(String inputStr);
- 生成文件:com_android_HelloWorld.h
到classes一级目录:/HelloWorld/bin/classes/
执行如下命令:
javah com.android.HelloWorld
生成文件:com_android_HelloWorld.h
- 实现JNI原生函数源文件:
新建com_android_HelloWorld.c文件:
定义OnLoad
OnLoadJava_com_android_HelloWorld_printJNI
编译生成so库
编译com_android_HelloWorld.c成so库可以和app一起编译,也可以都单独编译。
参数转换
1. 如何将java传入的String参数转换为c的char*,然后使用?
java传入的String参数,在c文件中被jni转换为jstring的数据类型,
//在c文件中声明
char* test;
test = (char*)(*env)->GetStringUTFChars(env, jstring, NULL);
//注意:test使用完后,通知虚拟机平台相关代码无需再访问:
(*env)->ReleaseStringUTFChars(env, jstring, test);
2.将c中获取的一个char*的buffer传递给java?
这个char*如果是一般的字符串的话,作为string传回去就可以了。如果是含有’\0’的buffer,最好作为bytearray传出,因为可以制定copy的length,如果copy到string,可能到’\0’就截断了。
unsigned char buffer[256];
int i=0;
for(i=0;i<256;i++)
buffer[i] = i;
//new一个byte数组
jbyteArray array = (*env)->NewByteArray(env,256);
//拷贝
(*env)->SetByteArrayRegion(env, array, 0, 256, buffer);
3. char* 和jstring互转
GetStringUTFChars
NewStringUTF
JNIEXPORT jstring JNICALL Java_com_explorer_jni_SambaTreeNative_getDetailsBy
(JNIEnv *env, jobject jobj, jstring pc_server, jstring server_user, jstring server_passwd)
{
const char *pc = env->GetStringUTFChars(pc_server, NULL);
const char *user = env->GetStringUTFChars(server_user, NULL);
const char *passwd = env->GetStringUTFChars(server_passwd, NULL);
const char *details = smbtree::getPara(pc, user, passwd);
jstring jDetails = env->NewStringUTF(details);
return jDetails;
}
4. 传对象到JNI调用
jclass stu_cls = env->GetObjectClass(obj_stu); //或得Student类引用
jfieldID ageFieldID = env->GetFieldID(stucls,"age","I"); //获得得Student类的属性id
jfieldID nameFieldID = env->GetFieldID(stucls,"name","Ljava/lang/String;"); // 获得属性ID
jint age = env->GetIntField(objstu , ageFieldID); //获得属性值
jstring name = (jstring)env->GetObjectField(objstu , nameFieldID);//获得属性值
const char * c_name = env->GetStringUTFChars(name ,NULL);//转换成 char *
string str_name = c_name ;
env->ReleaseStringUTFChars(name,c_name); //释放引用
5. 回调Java层方法
jstring str = NULL;
jclass clz = env->FindClass("cc/androidos/jni/JniTest");
//获取clz的构造函数并生成一个对象
jmethodID ctor = env->GetMethodID(clz, "<init>", "()V");
jobject obj = env->NewObject(clz, ctor);
// 如果是数组类型,则在类型前加[,如整形数组int[] intArray,则对应类型为[I,整形数组String[] strArray对应为[Ljava/lang/String;
jmethodID mid = env->GetMethodID(clz, "sayHelloFromJava", "(Ljava/lang/String;II[I)I");
jint javaIndex = env->CallIntMethod(obj, mid, str1, index1, index2, testIntArray);
Java 类型 | 本地类型 | 描述 |
---|---|---|
boolean | jboolean | C/C++8位整型 |
byte | jbyte | C/C++带符号的8位整型 |
char | jchar | C/C++无符号的16位整型 (char 在java中是2个字节。java采用unicode,2个字节(16位)来表示一个字符) |
short | jshort | C/C++带符号的16位整型 |
int | jint | C/C++带符号的32位整型 |
long | jlong | C/C++带符号的64位整型e |
float | jfloat | C/C++32位浮点型 |
double | jdouble | C/C++64位浮点型 |
Object | jobject | 任何Java对象,或者没有对应java类型的对象 |
Class | jclass | Class对象 |
String | jstring | 字符串对象 |
Object[] | jobjectArray | 任何对象的数组 |
boolean[] | jbooleanArray | 布尔型数组 |
byte[] | jbyteArray | 比特型数组 |
char[] | jcharArray | 字符型数组 |
short[] | jshortArray | 短整型数组 |
int[] | jintArray | 整型数组 |
long[] | jlongArray | 长整型数组 |
float[] | jfloatArray | 浮点型数组 |
double[] | jdoubleArray | 双浮点型数组 |
Java 类型 | 符号 |
---|---|
boolean | Z |
byte | B |
char | C |
short | S |
int | I |
long | J |
float | F |
double | D |
void | V |
objects对象 | L类名 |
Arrays数组 | [数组类型 |
methods方法 | (参数类型)返回类型 |
String | Ljava/lang/String; |
int[ ] | [I |
float[ ] | [F |
Object[ ] | [Ljava/lang/Object; |
int [ ][ ] | 其描述符为[[I |
float[ ][ ] | 其描述符为[[F |
Java层方法 | JNI函数签名 |
---|---|
String test ( ) | Ljava/lang/String; |
int f (int i, Object object) | (ILjava/lang/Object;)I |
void set (byte[ ] bytes) | ([B)V |