JavaVM

标准Java平台下,每一个Process可以产生很多JavaVM对象,但在Android平台上,每一个Process只能产生一个Dalvik VM对象,也就是说在Android进程中是通过一个虚拟器对象来服务所有Java和c/c++代码。

JavaVM使用

  • 在加载动态链接库的时候,JVM会调用JNI_OnLoad(JavaVM* jvm, void* reserved)(如果定义了该函数)。第一个参数会传入JavaVM指针。

  • 在native code中调用JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args)可以得到JavaVM指针。

两种情况下,都可以用全局变量,比如JavaVM* g_jvm来保存获得的指针以便在任意上下文中使用。

JNIEnv

JNIEnv是一个指针, 指向一个线程相关的结构, 线程相关结构指向JNI函数指针数组, 这个数组中存放了大量的JNI函数指针, 这些指针指向了具体的JNI函数。

JNIEnv作用:

  • 调用Java函数:JNIEnv代表Java运行环境, 可以使用JNIEnv调用Java中的代码;

  • 操作Java对象:Java对象传入JNI层就是Jobject对象, 需要使用JNIEnv来操作这个Java对象;

JNIEnv使用:

当本地c/c++代码想获得当前线程的JNIEnv时,可以使用两种方法:

  • vm->AttachCurrentThread(&env, 0)
  • vm->GetEnv((void**)&env, JNI_VERSION_1_6)

需要强调的是JNIEnv是跟线程相关的,最好还是不要缓存这个JNIEnv* 。

当创建的线程需要获取JNIEnv* 的时候,最好在刚创建的时候调用一次AttachCurrentThread,并且不要忘记线程结束的时候执行DettachCurrentThread。

实例方法&静态方法

实例方法:

public native String getStringFromNative();

原生实例方法通过第二个参数获取实例引用,是jobject类型:

JNIEXPORT jstring JNICALL Java_com_dean_testndk_JNIHelper_getStringFromNative
(JNIEnv *, jobject);

静态方法:

public static native String getStringFromNative();

静态方法没有与实例绑定,因此第二个参数是jclass类型:

JNIEXPORT jstring JNICALL Java_com_dean_testndk_JNIHelper_getStringFromNative
(JNIEnv *, jclass);

C/C++

原生代码中,c与c++调用JNI函数的语法不同:

C:

return (*env)->NewStringUTF(env, "Hello from JNI !");

C代码中,JNIEnv是指向JNIVativeInterface结构的指针,为了访问任何一个JNI函数,该指针需要首先被解引用。因为不了解JNI环境,所以需要将JNIEnv传递给调用者

C++:

return env->NewStringUTF("Hello from JNI !");

C++代码中,JNIEnv是C++类实例,JNI函数以成员函数的形式存在。因此不需要给调用着传递参数即可使用。

数据类型

Java中有两种数据类型:

基本数据类型:

boolean, byte, char, short, int, long, float, double

Java基本数据类型,可以直接与C/C++的相应基本数据类型映射

引用类型:

字符串类,数组类,以及其他类

与基本类型不同,引用类型对原生方法不透明,因此引用类型不能直接使用和修改,需要通过JNIEnv接口指针来调用JNI提供的API。

类&对象

获取类

  • FindClass:jclass FindClass(JNIEnv *env, const char *name);

    通过传入类的完全限定名(注意JNI这边类的完全限定名通过”/”分隔,而不是Java那边的”.”),即可得到一个对应的jclass对象。
jclass clz = (*env)->FindClass(env, "com/example/hello_jnicallback/JniHandler");
  • GetObjectClass: jclass GetObjectClass(JNIEnv *env, jobject obj);

    根据一个jobject对象得到这个对象的jclass对象。这事获取jclass的另一种方式。
jclass clz = (*env)->GetObjectClass(env, instance);

创建对象

  • jobject NewObject(JNIEnv *env, jclass clazz,

    jmethodID methodID, ...);

通过类,方法ID,对应参数来创建一个对象的实例:

jclass clz = (*env)->FindClass(env, "com/example/hello_jnicallback/JniHandler");

jmethodID jniHelperCtor = (*env)->GetMethodID(env, g_ctx.jniHelperClz, "<init>", "()V");

jobject handler = (*env)->NewObject(env, g_ctx.jniHelperClz, jniHelperCtor);

域&方法

在JNI开发中,经常会在Native中调用Java的域和方法。

Java的域和方法都有两类:

  • 实例域,静态域;
  • 实例方法,静态方法;

如下:

public class JavaClass {

    // 实例域
private String instanceFiled = "Instance Field";
// 静态域
private static String staticFiled = "Static Field"; // 实例方法
private String instanceMethod() {
return "Instance Method";
}
// 静态方法
private static String staticMethod() {
return "Static Method";
}
}

下面例子是获取实例域,方法的代码。静态域,静态方法使用基本相同,都是先获取描述它的ID,然后在通过ID调用相应方法。

void nativeCallJavaClass(JNIEnv *env, jobject instance) {

    jclass clazz = env->GetObjectClass(instance);

    // 获取实例域的FieldID
jfieldID instanceFieldId = env->GetFieldID(clazz, "instanceField", "Ljava/lang/String;");
// 获取实例域
jstring instanceField = (jstring) env->GetObjectField(instance, instanceFieldId); // 获取实例方法的MethodID
jmethodID instanceMethodId = env->GetMethodID(clazz, "staticMethod", "Ljava/lang/String;");
// 调用方法获得返回值
jstring instanceMethod = (jstring) env->CallObjectMethod(clazz, instanceMethodId); };

为了提升应用程序的性能,对于频繁使用的域和方法,可以缓存它们的ID方便下次使用。

描述符

域描述符

  • 基本类型的描述符
  • 引用类型的描述符
    • 引用类型:L + 该类型类描述符 + ;

      • String类型的域描述符为 Ljava/lang/String;
    • 数组:[ + 其类型的域描述符 + ;
      • int[] [I
      • float[] [F
      • String[] [Ljava/lang/String;
      • Object[] [Ljava/lang/Object;
    • 多维数组:n个[ +该类型的域描述符 , N代表的是几维数组。
      • int[][] [[I
      • float[][] [[F

类描述符

  • 类描述符:将完整的包名+类名中的.分隔符换成/分隔符

    • java.lang.String类的类描述符为:java/lang/String或者使用域描述符[Ljava/lang/String;
  • 数组类型的描述符:同域描述符

方法描述符

  • String test() Ljava/lang/String;
  • int f(int i, Object object) (ILjava/lang/Object;)I
  • void set(byte[ ] bytes) ([B)V

Javap

java在bin中提供了javap命令,用于查看一个类方法的签名

cd到.class对应路径,然后执行javap -s classname

例如:

cd /Users/DeanGuo/TestNDK/app/build/intermediates/classes/debug/com/dean/testndk/;

javap -s JNIHelper

Compiled from "JNIHelper.java"
public class com.dean.testndk.JNIHelper {
public com.dean.testndk.JNIHelper();
descriptor: ()V public static native java.lang.String getStringFromNative();
descriptor: ()Ljava/lang/String;
}
bogon:testndk DeanGuo$ javap -s JNIHelper
Warning: Binary file JNIHelper contains com.dean.testndk.JNIHelper
Compiled from "JNIHelper.java"
public class com.dean.testndk.JNIHelper {
public com.dean.testndk.JNIHelper();
descriptor: ()V public static native java.lang.String getStringFromNative();
descriptor: ()Ljava/lang/String;
}

局部&全局引用

局部引用

局部引用不能在后续的调用中呗缓存及重用,主要因为它们的使用期限仅限于原生方法,一旦原生函数返回,局部引用就被释放。也可以用void DeleteLocalRef(JNIEnv *env, jobject localRef);函数显示的释放。

全局引用

全局引用在原生方法的后续调用过程中依然有效,除非它们被原生代码显式释放。

  • 创建全局引用:
jclass globalClazz;
jclass localClazz = env->FindClass("java/lang/String");
globalClazz = (jclass) env->NewGlobalRef(localClazz);
  • 删除全局引用:
env->DeleteGlobalRef(globalClazz);

弱全局引用

与全局应用一样,弱全局引用在原生方法的后续调用过程中依然有效。不同的是,弱全局引用并不阻止潜在对象被垃圾回事。

  • 创建弱全局引用:
jclass weakGlobalClazz;
jclass localClazz = env->FindClass("java/lang/String");
weakGlobalClazz = (jclass) env->NewWeakGlobalRef(localClazz);
  • 有效性检验:
if (JNI_FALSE == env->IsSameObject(weakGlobalClazz, nullptr)) {
// 有效
} else {
// 对象被垃圾回收器回收, 不可使用
}
  • 删除弱全局引用:
env->DeleteGlobalRef(weakGlobalClazz);

LOG

NDK开发中JNI时,可以使用__android_log_print打印log信息。

使用步骤:

  • 引入#include <android/log.h>头文件
  • 加入Lib

    • gralde:在ndk标签下加入ldLibs "log"
    • Android.mk:加入LOCAL_LDLIBS+= -L$(SYSROOT)/usr/lib -llog
  • 使用__android_log_print(ANDROID_LOG_INFO, "JNITag","string From Java To C : %s", str);

封装:

#include <android/log.h>

// Android log function wrappers
static const char* kTAG = "testNDK";
#define LOGI(...) \
((void)__android_log_print(ANDROID_LOG_INFO, kTAG, __VA_ARGS__))
#define LOGW(...) \
((void)__android_log_print(ANDROID_LOG_WARN, kTAG, __VA_ARGS__))
#define LOGE(...) \
((void)__android_log_print(ANDROID_LOG_ERROR, kTAG, __VA_ARGS__)) // 使用如下:
LOGI("JNI_LOG");

标准库

如果想在NDK中使用c++STL库:

  • Application.mk:加入APP_STL := gnustl_static
  • gralde:在ndk标签下加入stl "gnustl_static"
  • system:使用默认最小的C++运行库,这样生成的应用体积小,内存占用小,但部分功能将无法支持
  • stlport_static:使用STLport作为静态库,这项是Android开发网极力推荐的
  • stlport_shared:STLport 作为动态库,这个可能产生兼容性和部分低版本的Android固件,目前不推荐使用。
  • gnustl_static:使用 GNU libstdc++ 作为静态库

文章链接:

JNI官方文档

NDK开发-零散知识点整理的更多相关文章

  1. ACM个人零散知识点整理

    ACM个人零散知识点整理 杂项: 1.输入输出外挂 //读入优化 int 整数 inline int read(){ int x=0,f=1; char ch=getchar(); while(ch& ...

  2. Android 零散知识点整理

    Android 零散知识点整理 为什么Android的更新试图操作必须在主线程中进行? 这是因为Android系统中的视图组件并不是线程安全的.通常应该让主线程负责创建.显示和更新UI,启动子线程,停 ...

  3. ios开发零散知识点总结

    1:当有导航栏的时候,子视图为UIScrollView,或是继承于UIScrollView的控件如UITableView,UICollectionView等,控制器会自动调用 self.automat ...

  4. NDK开发—基础知识实战Demo

    简介 前面写了几篇NDK相关的文章: NDK开发-简介&环境搭建(Eclipse,Android Studio) NDK开发-Android Studio+gradle-experimenta ...

  5. JSP页面开发知识点整理

    刚学JSP页面开发,把知识点整理一下. ----------------------------------------------------------------------- JSP语法htt ...

  6. .NET Web开发技术简单整理 转

    .NET Web开发技术简单整理 原文:http://www.cnblogs.com/SanMaoSpace/p/3157293.html 在最初学习一些编程语言.一些编程技术的时候,做的更多的是如何 ...

  7. 前端开发面试知识点大纲--摘自jackyWHJ

    前端开发面试知识点大纲:HTML&CSS:    对Web标准的理解.浏览器内核差异.兼容性.hack.CSS基本功:布局.盒子模型.选择器优先级及使用.HTML5.CSS3.移动端适应 Ja ...

  8. NDK开发总结

    NDK开发差不多结束了, 估计后面也不会再碰了诶, 想着还是写个总结什么的,以后捡起来也方便哈.既然是总结,我这里就不会谈具体的细节,只会记录下我觉得重要的东西, 所以这篇随笔不是为萌新学习新知识准备 ...

  9. JNI/NDK开发指南(开山篇)

    转载请注明出处:http://blog.csdn.net/xyang81/article/details/41759643 相信很多做过Java或Android开发的朋友经常会接触到JNI方面的技术, ...

随机推荐

  1. poj1236Network of Schools Tarjan裸题

    其实就是手打了个Tarjan的模板 输出的时候注意是入度为0的点的个数和max(入度0的个数,出度0的个数),在n=1时特判为0即可 ——以后图论要渐渐模板化,方便使用 #include <cs ...

  2. 基于dubbo框架下的RPC通讯协议性能测试

    一.前言 Dubbo RPC服务框架支持丰富的传输协议.序列化方式等通讯相关的配置和扩展.dubbo执行一次RPC请求的过程大致如下:消费者(Consumer)向注册中心(Registry)执行RPC ...

  3. jquery 练习笔记

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  4. 在Ubuntu16.04集群上手工部署Kubernetes(未完,陆续补充中)

    主机信息 主机 IP OS k8s-master 10.10.10.20192.168.0.20 Ubuntu Server 16.04 k8s-node1 10.10.10.21192.168.0. ...

  5. Hadoop 运行 yarn jar 单词统计问题解决

    测试单词统计时,运行yarn jar XX.jar 出现如下报错: Caused by: java.io.IOException: Initialization of all the collecto ...

  6. jquery中css获取颜色属性

    Jquery获取颜色的方法为: var color = $(元素).css("color"); alert(color); 可以看到color如这样的格式; 但是; 因为rgb(0 ...

  7. 初识The Battle of Polytopia

    1.首先了解了一下<文明5-美丽新文明>视频介绍网址:http://list.youku.com/albumlist/show?id=19481409&ascending=1&am ...

  8. Jquery实现静态切换tab

    1. <div id="tabs"> <ul> <li><a href=</a></li> <li>& ...

  9. 好用的Markdown编辑器一览

    Markdown 是一种简单的.轻量级的标记语法.用户可以使用诸如 * # 等简单的标记符号以最小的输入代价生成极富表现力的文档. Markdown具有很多优点: 写作中添加简单符号即完成排版,所见即 ...

  10. Flash+fms视频录制在项目中的实际应用

    Flash+fms视频录制在项目中的实际应用 前言:以下只是记录本人在项目中的应用,而flash+fms视频录制有多种实现方式,具体可根据实际情况而定! 1:古人云:工欲善其事,必先利其器,首先安装f ...