android jni

javah -jni com.example.HelloWorld

com.example 是的你package

倘若路径为:    /home/user/com/example/HelloWorld.class

那么在/home/user执行javah 如果你在 /home/user/com/example/目录执行是找不到类的。


1.相关命令:

   将包含native函数的java源代码生成JNI的.h头文件:  javah  -jni com.xxx.test  

   注意:test是编译后的.class文件,要写完整路径,该命令在com的上层目录下进行。更完整的命令为: javah -classpath . -jni com.xxx.test  注意中间的点号

   编写实现上面.h文件的.cpp代码后,生成dll目标文件:g++ -Wl,--kill-at -shared -o test1.dll test2.cpp 

   注意:test1.dll为生成的目标DLL,test2为cpp源文件。红色部分不可少,表示生成不带@的函数.

  

  2.如何将java里的string类型转换为C里的char*类型:

复制代码
char* jstringTostring(JNIEnv* env, jstring jstr) //change type string into char*
{
    char* rtn = NULL;
    jclass clsstring = env->FindClass("java/lang/String");
    jstring strencode = env->NewStringUTF("utf-8");
    jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B");
    jbyteArray barr= (jbyteArray)env->CallObjectMethod(jstr, mid, strencode);
    jsize alen = env->GetArrayLength(barr);
    jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE);
    if (alen > 0)
    {
        rtn = (char*)malloc(alen + 1);
        memcpy(rtn, ba, alen);
        rtn[alen] = 0;
    }
    env->ReleaseByteArrayElements(barr, ba, 0);
    return rtn;
}
复制代码

  

  

  3.如何将C里的char*类型转换为java里的string类型:

复制代码
jstring stoJstring(JNIEnv* env, const char* pat)//change type char* into string
{
    jclass strClass = env->FindClass("Ljava/lang/String;");
    jmethodID ctorID = env->GetMethodID(strClass, "<init>", "([BLjava/lang/String;)V");
    jbyteArray bytes = env->NewByteArray(strlen(pat));
    env->SetByteArrayRegion(bytes, 0, strlen(pat), (jbyte*)pat);
    jstring encoding = env->NewStringUTF("utf-8");
    return (jstring)env->NewObject(strClass, ctorID, bytes, encoding);
}
复制代码

  

  4.JNI里如何返回arraylist类型:

复制代码
        jclass cls_ArrayList = env->FindClass("java/util/ArrayList");  
        jmethodID construct = env->GetMethodID(cls_ArrayList,"<init>","()V");//注意第二个参数为<init>,网上有些资料写着为空或者<init></init>是错的。  
        jobject obj_ArrayList = env->NewObject(cls_ArrayList,construct,"");  
        jmethodID arrayList_add = env->GetMethodID(cls_ArrayList,"add","(Ljava/lang/Object;)Z"); //获得arraylist的add()方法 
        //User Object  
        jclass cls_user = env->FindClass("User"); //注意User所在路径要写完整,该加包名的地方要加上 
        //none argument construct function  
        jmethodID construct_user = env->GetMethodID(cls_user,"<init>","()V");  
        //new a object  
        jobject obj_user = env->NewObject(cls_user,construct_user,"");  
        //get method id  
        /* 
        jmethodID user_setId = env->GetMethodID(cls_user,"setId","(J)V"); 
        jmethodID user_setUserName = env->GetMethodID(cls_user,"setUserName","(Ljava/lang/String;)V"); 
        jmethodID user_setMan = env->GetMethodID(cls_user,"setMan","(Z)V"); 
        jmethodID user_setAge = env->GetMethodID(cls_user,"setAge","(I)V"); 
        */  
        int i;  
        for(i=0;i<10;i++){  
        //new a object  
        jobject obj_user = env->NewObject(cls_user,construct_user,"");  
        //get field id  
        jfieldID user_id = env->GetFieldID(cls_user,"id","J");  
        jfieldID user_name = env->GetFieldID(cls_user,"userName","Ljava/lang/String;");  
        jfieldID user_isMan = env->GetFieldID(cls_user,"isMan","Z");  
        jfieldID user_age = env->GetFieldID(cls_user,"age","I");  
        env->SetLongField(obj_user,user_id,i);  
        env->SetObjectField(obj_user,user_name,env->NewStringUTF("CC"));  
        env->SetBooleanField(obj_user,user_isMan,1);  
        env->SetIntField(obj_user,user_age,21);  
        env->CallObjectMethod(obj_ArrayList,arrayList_add,obj_user);  
        }  
        return obj_ArrayList;  
复制代码

  

  5.对于碰到以下错误:

复制代码
# A fatal error has been detected by the Java Runtime Environment:
#
# EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x7c92100b, pid=596, tid=2080
#
# JRE version: 6.0_21-b07
# Java VM: Java HotSpot(TM) Client VM (17.0-b17 mixed mode, sharing windows-x86 )
# Problematic frame:
# C [ntdll.dll+0x100b]
#
# If you would like to submit a bug report, please visit:
# http://java.sun.com/webapps/bugreport/crash.jsp
# The crash happened outside the Java Virtual Machine in native code.
# See problematic frame for where to report the bug.
复制代码

  一般都是由于你所写的JNI转换后的CPP实现代码有问题。可以关注下红色部分,若pc=0x00000000,则表示你要实现的native函数里传入了空指针void*或null。

  而如果你传入的参数还有结构体变量,那么这里的空值还要考虑结构体里的部分成员是否也为空,JNI里的函数是不允许传入空值的,这点网上的资料里都没有提,我也在这里卡了很久。但是你必须要传入空值,而且还要调用更下一层的DLL里的函数时,那就会报错,这时该怎么办呢?此时你可以在它们中间再加一过渡层(CPP文件)实现间接调用。  

 

  6.最后注意下JNI里的资源释放问题,若使用资源过多没有及时释放,也会有可能会报上面的错误。

  如:env->ReleaseStringUTFChars(xxx, xxx);

 

JNI 使用java对象

 

有关Android JNI开发中比较强大和有用的功能就是从JNI层创建、构造Java的类或执行Java层的方法获取属性等操作。

    一、类的相关操作

    1. jclass FindClass(JNIEnv *env, const char *name);  查找类

    该函数可能做过Java开发的不会陌生,这个是JNI层的实现,需要注意的是第二个参数为const char*类型的,我们如果从Java从层传入unicode编码的jstring类型需要使用GetStringUTFChars函数转换成utf8的const char*,如果成功返回这个Java类的对象jclass,相关的异常可能有

     (1. ClassFormatError 类的数据格式无效
     (2. ClassCircularityError 该类或接口是自身的超类或超接口
     (3. NoClassDefFoundError 没有找到指定名称的类或接口 
     (4. OOM内存不足错误,即OutOfMemoryError 

     2. jclass GetSuperclass(JNIEnv *env, jclass clazz);  获取父类或者说超类  

     该函数的第二个参数为jclass类,我们调用时传入的是子类,否则返回将是NULL

     3. jboolean IsAssignableFrom(JNIEnv *env, jclass clazz1,jclass clazz2);  判断class1对象能否安全的强制转换为class2对象 

     如果可以将返回 JNI_TRUE,JNI_TRUE的定义值为1,否则返回JNI_FALSE即0 ,这里Android123详细说明下哪些情况可能返回真:

     (1  这两个类参数引用同一个 Java 类
     (2  第一个类是第二个类的子类
     (3  第二个类是第一个类的某个接口

     4.  jclass GetObjectClass(JNIEnv *env, jobject obj); 通过对象获取这个类

     该函数比较简单,唯一注意的是对象不能为NULL,否则获取的class肯定返回也为NULL。

    5.  jboolean IsInstanceOf(JNIEnv *env, jobject obj,jclass clazz); 判断对象是否为某个类的实例

   这个函数是JNI层的实现,相信大家都不陌生,Android开发网提醒大家需要注意的是返回值可能产生异议,就是如果传入的第二个参数为NULL对象,NULL对象可以强制转换为各种类,所以这种情况也将会返回JNI_TRUE,所以一定判断传入的对象是否为空。

   6. jboolean IsSameObject(JNIEnv *env, jobject ref1,jobject ref2);  判断两个对象是否引用同一个类

   需要注意的是如果两个对象均为空,返回的值也会是JNI_TRUE所以使用时判断对象为空。

   二、调用Java方法

   首先说下有关签名sig相关的比如 "Ljava/lang/String;"

   1. jmethodID GetMethodID(JNIEnv *env, jclass clazz,const char *name, const char *sig);  获取一个Java方法的ID

     这个函数将返回非静态类或接口实例方法的方法 ID。这个方法可以是某个clazz 的超类中定义,也可从clazz 继承,最后一个参数为签名,最后两个参数是const char*类型,是utf8类型。需要注意的是Android123提醒大家执行GetMethodID()函数将导致未初始化的类初始化,如果要获得构造函数的方法ID,使用 <init> 作为方法名,同时将 void (V) 作为返回类型,如果找不到指定的ID将返回NULL,同时异常可能有:

    (1  NoSuchMethodError 找不到指定的Java方法。
    (2  ExceptionInInitializerError 如果由于异常而导致类初始化程序失败
    (3  OutOfMemoryError 内存不足

  2 . NativeType CallXXXMethod (JNIEnv *env, jobject obj,jmethodID methodID, va_list args); 调用XXX类型的Java方法

     执行Java类中某个方法,需要注意的是这个里的java类是非静态的,由于Java的方法的类型比较多,所以该函数可能有以下几种形式,如CallObjectMethod,CallBooleanMethod,CallByteMethod,CallCharMethod,CallShortMethod,CallIntMethod,CallLongMethod,CallFloatMethod,CallDoubleMethod和CallVoidMethod,需要注意的是,该函数的第三个参数为通过GetMethodID函数获取的方法ID,最后一个参数为这个方法的参数表,最后的va_list宏可以通过搜索获取具体的使用方法,这里Android开发网不再赘述。

  3.NativeType CallNonvirtualXXXMethod (JNIEnv *env, jobject obj,jclass clazz, jmethodID methodID, jvalue *args);

    CallNonvirtualXXXMethod函数和上面的CallXXXMethod 不同之处是多了一个jclass参数,CallXXXMethod是根据对象来调用方法,而CallNonvirtualXXXMethod是根据类的实例调用,区别在这点。

   上面的三个均为非静态类的获取,执行调用,需要实例化这个类才可以执行,下面的为静态调用。

   4.  jmethodID GetStaticMethodID(JNIEnv *env, jclass clazz,const char *name, const char *sig);

   5.  NativeType CallStaticXXXMethod(JNIEnv *env, jclass clazz,jmethodID methodID, ...);    

   三、访问Java对象的域

    Java对象的域或者说字段、属性(Field) 类似方法的执行

    1. jfieldID GetFieldID(JNIEnv *env, jclass clazz,const char *name, const char *sig);  获取实例对象的域ID

    需要注意的是,非静态的实例化后的对象,可能产生的异常有

   (1 NoSuchFieldError  找不到指定的域
   (2 ExceptionInInitializerError 因为异常而导致类初始化失败
   (3 OutOfMemoryError内存不足。

    2. NativeType GetXXXField(JNIEnv *env, jobject obj,jfieldID fieldID);

    类似GetXXXMethod函数,可能有的类型有 GetObjectField,GetBooleanField,GetByteField,GetCharField,GetShortField,GetIntField,GetLongField,GetFloatField,GetDoubleField。

   3. void SetXXXField(JNIEnv *env, jobject obj, jfieldID fieldID,NativeType value);

    Java的域可以赋值的,可能有的类型有 SetObjectField,SetBooleanField,SetByteField,SetCharField,SetShortField,SetIntField,SetLongField,SetFloatField,SetDoubleField。

    上面3种情况均为非静态对象的域,对于不需要实例化对象的域,可以直接使用下面的。

   4. jfieldID GetStaticFieldID(JNIEnv *env, jclass clazz,const char *name, const char *sig);

   5. NativeType GetStaticXXXField(JNIEnv *env, jclass clazz,jfieldID fieldID);

   6. void SetStaticXXXField(JNIEnv *env, jclass clazz,jfieldID fieldID, NativeType value);

  四、实例代码,Android123给网友准备了一个例子,帮助大家实战Android JNI开发,大家可以移植到Android NDK环境中执行,网友可以访问 Android JNI开发代码 (Android JNI实例代码(一))

  最后有关Android JNI最后的终极内容,Android开发网主要说明下JVM和JNI的全局引用相关内容,比如本地全局引用LocalGlobalRef,弱全局引用WeakGlobalRef,JNI中线程处理的高级方法比如AttachCurrentThread,以及JNI中的NIO的相关特性将在明天继续讲解,更多的有关Android平台NDK开发内容可以查看我们 Android NDK开发技巧系列文章。

 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值