android JNI

原创 2016年08月31日 12:11:54
有关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,使用 作为方法名,同时将 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开发网主要说明下JVM和JNI的全局引用相关内容,比如本地全局引用LocalGlobalRef,弱全局引用WeakGlobalRef,JNI中线程处理的高级方法比如AttachCurrentThread,以及JNI中的NIO的相关特性将在明天继续讲解,更多的有关Android平台NDK开发内容可以查看我们 Android NDK开发技巧系列文章

二. JNI中的串处理函数
1.新建jstring
1 jstring NewString(const jchar *unicodeChars, jsize len);//创建Unicode格式的jstring串
2
3 jstring NewStringUTF(const char *bytes); //创建UTF-8格式的jstring串

2.获取字符串长度

jsize GetStringLength(jstring string);//Unicode

jsize GetStringUTFLength(jstring string);//UTF-8

3.jstring转化为C串及释放jstring串

/**

*如果生成串的一个副本,isCopy参数将被置为JNI_TRUE,否则置为

*NULL或者JNI_FALSE

*/

const jchar* GetStringChars(jstring string, jboolean *isCopy);

const char* GetStringUTFChars(jstring string, jboolean *isCopy );

注:这两个函数返回一个指向特定jstring中字符顺序的指针,该指针保持有效直到下面的函数被调用:

void ReleaseStringChars(jstring string, const jchar *chars);


void ReleaseStringUTFChars(jstring string, const char *utf);

GetStringRegion函数将串str的一个子串传送到一个字符缓存器。该子串在位置start处开始,在len-1处结束(这样传送的字符数就是len)。这将会抛出一个StringIndexOutOfBound***ception:
void GetStringRegion(jstring str, jsize start, jsize len, jchar *buf);

void GetStringUTFRegion(jstring str, jsize start, jsize len, char *buf);
GetStringCritical函数返回一个指向特定串中字符的指针。如果有必要,复制该字符,并且函数返回时将isCopy置为JNI_TRUE,否则置为NULL或JNI_FALSE。在调用该函数后,直至调 用ReleaseStringCritical之前,所使用的所有函数都无法使当前线程被阻塞:

const jchar* GetStringCritical(jstring string, jboolean *isCopy);


void ReleaseStringCritical(jstring string,const jchar *carray);


 1.如果要在本地CPP代码中访问java代码中的一个对象(某个class的实例)的某个域的值,需要经历以下三个步骤:
  步骤1)用FindClass()函数找到该java类(如android.os.Binder)的实例对象的引用:
  jclass clazz = env->FindClass(kBinderPathName) = env->FindClass("android.os.Binder")
  步骤2)用GetFieldID()函数获取到要访问的域(field: 实际上就是该java class中的某个成员变量的名字)的ID:
  gBinderOffsets.mObject = env->GetFieldID(clazz, "mObject", "I") // mObject为java class "Binder"里的一个成员变量
  -> 注意,这里将要访问的那个java对象的成员mObject的ID保存到了全局变量gBinderOffsets.mObject中,这样做的前提和优点如下:
  前提: android里面,每个java进程中只允许有一个java虚拟机(sun公司原始的java架构中,一个进程中可以有多个java虚拟机)
  优点: 除了第一次,以后每次要访问该java对象的成员mObject就非常快了(不用再去FindClass()和GetFieldID())
  步骤2)用GetMethodID()函数获取到要访问的方法(Method: 实际上就是该java class中的某个成员函数的名字)的ID:
  gBinderOffsets.mExecTransact = env->GetMethodID(clazz, "execTransact", "(IIII)Z") // execTransact为java class "Binder"里的一个成员函数
  -> 注意,这里将要访问的那个java对象的成员execTransact的ID保存到了全局变量gBinderOffsets.mExecTransact,这样做的前提和优点如下:
  前提: android里面,每个java进程中只允许有一个java虚拟机(sun公司原始的java架构中,一个进程中可以有多个java虚拟机)
  优点: 除了第一次,以后每次要访问该java对象的成员mExecTransact就非常快了(不用再去FindClass()和GetMethodID())
  步骤3)用类似于GetIntField()的函数获取到该java对象的那个域(即成员)的值:
  IBinder* target = (IBinder*)env->GetIntField(obj, 
  
gBinderProxyOffsets.mObject) // 获取java android.os.Binder类型对象里面的成员mObject的值 
  步骤3)用类似于CallBooleanMethod()的函数调用到该java对象的那个成员函数:
  jboolean res = env->CallBooleanMethod(mObject, gBinderOffsets.mExecTransact, code, (int32_t)&data, (int32_t)reply, flags)
 2.android java调用CPP函数: 原理 => 相当于java的那个class里面有的函数使用CPP代码来实现了
  1)通过结构JNINativeMethod描述java代码调用函数和CPP函数的对应关系:
  typedef struct {
  const char* name; // java代码调用CPP函数的入口
  const char* signature; // CPP函数的返回值
  void* fnPtr; // CPP的函数名
  } JNINativeMethod;
  => 例如: java代码通过IBinder.transact()来调用CPP的函数android_os_BinderProxy_transact()
  {"transact", "(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z", (void*)android_os_BinderProxy_transact},
  2)将CPP函数注册到java的某个class中: 使用函数AndroidRuntime::registerNativeMethods()来注册
  => 这之后,java代码就可以调用CPP函数了
  3)java代码调用CPP函数方法:
  IBinder.transact()
 3.andorid CPP调用java函数: 原理 => 相当于CPP代码找到java的那个class里面的函数的入口地址,然后在CPP代码中调用java代码
  1)通过结构JNINativeInterface描述CPP代码调用java函数的对应关系:
  CallStaticVoidMethod
  2)到java的那个class(如android.os.Binder)中找到java函数(如execTransact())的入口:
  jclass clazz = env->FindClass(kBinderPathName) // const char* const kBinderPathName = "android/os/Binder";
  gBinderOffsets.mExecTransact = env->GetMethodID(clazz, "execTransact", "(IIII)Z")
  3)在CPP代码中调用java函数:
  env->CallBooleanMethod(mObject, gBinderOffsets.mExecTransact, code, (int32_t)&data, (int32_t)reply, flags)

<script>window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];</script>
阅读(2008) | 评论(0) | 转发(0) |
0

上一篇:Android MediaPlayer API

下一篇:Android属性系统

给主人留下些什么吧!~~
评论热议
版权声明:本文为博主原创文章,转载请注明出处。

Android调用JNI的简单实例(附详细步骤)

最近想调用FFmepg库来做编解码的东西,首先就得学会使用JNI(JAVA Native Interface)调用。 下面做个简单的JNI调用实例,中间遇到的问题及解决过程省略一万字,查找到的资料也都...
  • EricFantastic
  • EricFantastic
  • 2015年10月21日 09:45
  • 4736

Android之从零开始JNI研发

本篇几乎没有涉及C/C++与java之间变量以及语法等等一些知识点,这些会在下篇来介绍。本人也是在学习探索过程中,如果有错误希望大家指出来。...
  • xiaohanluo
  • xiaohanluo
  • 2017年02月15日 14:45
  • 1814

Android JNI机制与应用

本文转自http://www.cnblogs.com/yc_sunniw ... /07/05/1771149.html1. 什么是 JNI     JNI是Java Native Interface...
  • jinchats
  • jinchats
  • 2011年06月20日 17:29
  • 3087

Android studio简单使用JNI实例

Android studio简单使用JNI实例 发现网上很多JNI的使用教程,也很详细,不过有的地方有些缺漏,导致很多小问题难以解决的,今天就来总结一下。   准备工作:下载NDK。...
  • wangliu1102
  • wangliu1102
  • 2017年12月28日 10:58
  • 196

Android中关于JNI 的学习(零)简单的例子,简单地入门

1)创建Java类文件,并定义Native方法,如JniTest类。 2)利用javac生成class文件,然后回到src目录,利用javah生成C/C++头文件,在这里要注意,javah命令要在包的...
  • foolsheep
  • foolsheep
  • 2014年05月06日 13:12
  • 2156

Android Studio Jni 调试

Jni是android提供的与其他语言交互的接口,常用的就是c或c++,之前写jni代码是非常痛苦的,一方面像写记事本一样没有各种函数提示,一方面无法调试,单靠打log效率非常低,所以之前都有点不想写...
  • u012292247
  • u012292247
  • 2016年11月16日 21:20
  • 4015

Android中JNI创建实例

参考文档: http://blog.sina.com.cn/s/blog_a11f64590101924l.html http://www.cnblogs.com/hoys/archive/2010/...
  • wikiday
  • wikiday
  • 2015年01月04日 22:18
  • 9055

Android Studio3.0开发JNI流程------JNI函数

JNI函数 本章为JNI函数提供参考信息。其中列出了全部JNI函数,同时也给出了JNI函数表的准确布局。注意:“必须”一词用于约束JNI编程人员。例如,当说明某个JNI函数必须接收非空对象时,就应确...
  • cloverjf
  • cloverjf
  • 2017年11月29日 16:47
  • 594

android使用JNI进程守护service

最近公司一个应用需要让应用启动后不能被360和一些清理内存工具杀死,然后搜索一些相关的资料确定了下面的方法进行进程的守护:使用jni fork出一个子进程用select方法检测管道是否可读,主体进程打...
  • csdn49532
  • csdn49532
  • 2015年12月25日 14:49
  • 2178

android JNI C代码对sdcard中文件的操作

- 所有操作除路径不同其它全部相同,如:fopen, fclose, fread ... - 别忘记把权限加上,如下:         - 路径:mnt/sdcard/  ...
  • ouyangtianhan
  • ouyangtianhan
  • 2015年08月05日 09:42
  • 2213
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:android JNI
举报原因:
原因补充:

(最多只允许输入30个字)