前言

由于网上关于JNI/NDK相关的知识点介绍的比较零散而且不具备参照性,所以写了这篇JNI/NDK笔记,便于作为随时查阅的工具类型的文章,本文主要的介绍了在平时项目中常用的命令、JNI数据类型、签名等,便于查阅相关资料。文末相关参考资料比较适合刚接触或者不熟悉Android NDK开发的朋友参阅。

常用命令

javac 编译java源文件生成.class文件

由于JNI对应的头文件由javah工具根据对应的.class文件生成,所以在进行JNI编程之前,写好Java代码后需要先编译,在使用javah生成对应的头文件

javah -jni自动生成头文件

举例说明:

  • 生成普通的JNI头文件

    javah -classpath path -jni -d outputdirpath com.mrljdx.JavaNativeCode
  • 在Java函数中包含Android相关的参数代码,则需要在classpath中添加android.jar包的绝对路径地址

    javah -classpath path:$ANDROID_HOME/path/android.jar -jni -d outputdirpath com.mrljdx.JavaNativeCodeWithAndroid

javap -s -p 查看函数签名

-s: 显示签名(只显示public类型的签名) -p:显示所有函数、成员变量的签名

举例说明:

javap -classpath pacakage_path_dir -s -p com.mrljdx.JavaCode

JNI数据类型和类型签名

数据类型

JNI的数据类型包括:基本类型引用类型。这一点和Java的语言特性一致,基本类型包括jboolean、jchar、jint、jlong、jbyte、jshort、jfloat、jdouble、void,与Java类型的对应关系如下:

JNI类型 Java类型 描述
jboolean boolean 无符号8位整型
jbyte byte 有符号8位整型
jchar char 无符号16位整型
jshort short 有符号16位整型
jint int 32位整型
jlong long 64位整型
jfloat float 32位整型
jdouble double 64位整型
void void 无类型

JNI中引用类型主要有类、对象和数组,这点也上符合Java的语法规范,对应的关系如下:

JNI 类型 Java引用类型 描述
jobject Object Object类型
jclass Class Class类型
jstring String String类型
jobjectArray Object[] 对象数组
jbooleanArray boolean[] boolean数组
jbyteArray byte[] byte数组
jcharArray char[] char数组
jshortArray short[] short数组
jintArray int[] int数组
jlongArray long[] long数组
jfloatArray float[] float数组
jdoubleArray double[] double数组
jthrowable Throwable Throwable

JNI类型签名

JNI的类型签名标识了一个特定的Java类型,这个类型可以是类和方法,也可以是数据类型。

  • 类型签名
    类的签名采用”L+包名+类名+;”标识,包名中将.替换为/即可。
    比如String类的签名:
    Ljava/lang/String;
    注意末尾的;属于签名的一部分。
    再比如Android中Context类的签名:
    Landroid/content/Context;
  • 基本数据类型签名
    基本数据类型的签名采用一系列大写字母来标识,如下:
Java类型 签名
boolean Z
byte B
char C
short S
int I
long J
float F
double D
void V

可以发现除了 long基本数据类型的签名为J之外其他的都比较容易辨识,估计是由于之前的类类型的签名开头为L+包名+类名+;设计者为了区分所以签名为J

  • 数组的类型签名
    数组的类型签名比起类类型和基本数据类型的要稍微复杂一点,不过还是很好理解的。对于数组来说,它的签名为[+类型签名,举例说明:
    String[] 数组类型对应的签名:
    [Ljava/lang/String;
    可以发现,就是在String的类签名前加了个[
    同理基本数据类型签名int[]的签名:
    [I
    注意这里基本类型后面是不带分号的。
    那么多维数组呢?可以类推,int[][] 的签名为[[I ,而String[][]的签名为[[Ljava/lang/String;
  • 方法的签名
    在JNI中会经常需要在C/C++代码中调用Java的函数,这时候就会用到方法的签名。方法的签名为(+参数类型签名+)+返回值类型签名,比如:
    方法: boolean login(String username,String password)的方法签名如下:
    (Ljava/lang/String;Ljava/lang/String)B 如果这里不理解的话,请再去看看之前关于基本类型,类类型的签名部分内容。

小技巧:使用 类似于javap -classpath pathdir -s -p com.sample.JavaCode 的 javap -s -p 命令也可以帮助查看一些类中各种方法和成员变量的签名。

JNI相关命名解释

  • 函数名的格式遵循规则:Java_包名_类名_方法名
  • JNIEXPORT、JNICALL、JNIEnv和jobject 都是JNI标准中所定义的类型或者宏
  • JNIEnv * : 指向JNI环境的指针,可以通过JNIEnv * 访问JNI提供的接口方法
  • JNIEXPORT、JNICALL:是jni.h中所定义的宏。

注:JNIEnv * 可以简单的理解为Java和C/C++ 之间相互调用的桥梁,我们可以通过JNIEnv * 调用C/C++定义的方法,也可以在C/C++中通过JNIEnv * 来调用Java类中的方法。下面将会讲到C/C++中调用Java的方法,注意JNIEnv *的作用。

在C/C++中调用Java方法

首先说明一点,在Android开发过程中使用NDK主要是为了提高代码的安全性,有些游戏公司可能是为了方便利用已有的C/C++开源库来进行平台移植,其实在性能提升方面,NDK的作用并不是很明显。所以有时候一些在Java中实现起来非常简单的代码放在JNI里面做会显得吃力不讨好,所以干脆就直接在JNI中调用Java的方法,我们只把加密和验证的一些逻辑写到JNI层就行了。
在JNI中调用Java方法流程如下:

  1. 在Java中定义一个静态方法供JNI调用,注意要是静态的。
  2. 在JNI中利用env来调用Java中定义的静态方法
  3. 调用声明好的静态方法

可能流程说的比较抽象,用代码简单说明一下:

  1. 定义静态方法:

    //对应包名:com.mrljdx.jni.HelloJNI
    public static void helloJava() {
    System.out.println("Hello JavaCode");
    }
  2. JNI声明静态方法:

    static void static_helloJava(JNIEnv *env){
    jclass clazz = env->FindClass("com/mrljdx/jni/HelloJNI");
    jmethodID mid = env->GetStaticMethodID(clazz, "helloJava", "()V");
    env->CallStaticVoidMethod(clazz, mid);
    }
  3. 调用声明好的静态方法:

    static_helloJava(env);

在AndroidStudio中NDK编程配置注意事项:

  1. 在项目的gradle.properties中添加ndk支持:

    android.useDeprecatedNdk=true
  2. 配置build.gradle看代码注释:

    defaultConfig {
    minSdkVersion 9
    targetSdkVersion 23
    versionCode 1
    versionName "1.0"
    //配置ndk 支持
    ndk {
    //编译的so库名称 libsecurity.so
    moduleName "security"
    //指定编译后的库支持的平台
    abiFilters "armeabi", "mips", "x86", "armeabi-v7a"
    //用于指定应用应该使用哪个标准库,此处添加c++库支持
    stl "stlport_static"
    }
    }
  3. 在AndroidStudio中写JNI代码有一个比较爽的地方,就是Android.mk系统会在编译时自动帮你生成,你只需要配置build.gradle就行了。注意jni相关代码需要放在src/main/jni目录下。如果对gradle配置不了解可以参考我的博客:Gradle实战及学习建议

小结

在我们做产品的时候,应该考虑该用JNI&NDK的时候就用,一切出发点是基于用户的体验和数据安全,我觉得在以下几种情况下建议使用NDK:

  1. 重用现有的代码,比如C/C++的代码在Android中的重用。
  2. 数据安全,比如将Http的请求加密和解密算法放在NDK中去实现,这样可以提高应用的安全。
  3. 提升性能,由于Android设备制造商在手机中给每个应用分配了可用的最大RAM,有时候为了性能考虑,可以通过Native代码向系统来“借”一些内存,尽量少的使用系统分配给应用的内存。(参考Infoq:Android内存优化)

参考文章及书籍

END

Android JNI&NDK编程小结及建议的更多相关文章

  1. Android jni/ndk编程五:jni异常处理

    在Java的编程中,我们经常会遇到各种的异常,也会处理各种的异常.处理异常在java中非常简单,我们通常会使用try-catch-finally来处理,也可以使用throw简单抛出一个异常.那么在jn ...

  2. Android jni/ndk编程二:jni数据类型转换(primitive,String,array)

    一.数据类型映射概述 从我们开始jni编程起,就不可能避开函数的参数与返回值的问题.java语言的数据类型和c/c++有很多不同的地方,所以我们必须考虑当在java层调用c/c++函数时,怎么正确的把 ...

  3. Android jni/ndk编程四:jni引用类型

    一.JNI引用类型 JNI支持三种类型的 opaque reference:local references, global references,和weak global references,下面 ...

  4. Android jni/ndk编程三:native访问java

    一.访问静态字段 Java层的field和method,不管它是public,还是package.private和protected,从 JNI都可以访问到,Java面向语言的封装性不见了. 静态字段 ...

  5. android Jni NDK开发环境搭建及其简单实例的编写

    android  Jni  NDK开发环境搭建及其简单实例的编写 由于工作需要,需要采用开发想要的JNI,由于之前没有接触过安卓的开发,所以更加网上的帖子,学习了下.遇到了些问题,然后总结下学习过程中 ...

  6. Android Studio NDK编程初探

    继上一篇学习了如何使用NDK编译FFMPEG后,接下来就是要学习如何在Android Studio中使用了. 经过参考和一系列的摸索,记录下具体步骤. 创建C++ Support的Android St ...

  7. Android之NDK编程(JNI)

    转自:http://www.cnblogs.com/xw022/archive/2011/08/18/2144621.html NDK编程入门--C回调JAVA方法   一.主要流程 1.  新建一个 ...

  8. Android Studio Ndk 编程

    如今开发Android程序基本都已经从Eclipse转到了Android Studio了, 近期项目需求, 须要用到ndk编程, 于是就折腾了一下. 开发环境 Android Studio 1.5.1 ...

  9. Android JNI(NDK)开发总结

    早就知道Java有个jni可以调用本地化代码,一直没有动力去研究它,现在公司想通过在Android中调用本地化代码来申请较多的内存以突破Android对单个进程的内存限制,这确实是可行的:我的Nexu ...

随机推荐

  1. laravel5.2总结--请求

        1 获取请求 1.1 获取请求的 URI 1>path 方法会返回请求的 URI.所以,如果接收到的请求目标是 http://domain.com/foo/bar,那么 path 方法就 ...

  2. dcpromo(server2012不支持)

    dcpromo 编辑 dcpromo命令是一个“开关”命令.如果Windows 2000 Server计算机是成员服务器,则 运行dcpromo命令会安装活动目录,将其升级为域控制器:如果Window ...

  3. 设计模式之第4章-装饰模式(Java实现)

    设计模式之第4章-装饰模式(Java实现) “怎么了,鱼哥?” “唉,别提了,网购了一件衣服,结果发现和商家描述的差太多了,有色差就算了,质量还不好,质量不好就算了,竟然大小也不行,说好的3个X,邮的 ...

  4. 连接Oracle 10g时ORA-12514:TNS:监听进程不能解析在连接描述符中给出的SERVICE_NAME错误的解决

    近日服务器断电,导致客户端连接ORACLE服务器时出现ORA-12514错误,在网上查得解决方法如下 解决方法: 1. 打开/network/admin/listener.ora文件,找到: SID_ ...

  5. PAT——乙级1032

    这些题也确实简单,但是我还是想做做,多熟悉一下C++,毕竟实践是检验真理的唯一标准,有很多小知识点自己做了才知道. 这个题是 1032 挖掘机技术哪家强 (20 point(s)) 为了用事实说明挖掘 ...

  6. 菜鸟之路——机器学习之Kmeans聚类个人理解及Python实现

    一些概念 相关系数:衡量两组数据相关性 决定系数:(R2值)大概意思就是这个回归方程能解释百分之多少的真实值. Kmeans聚类大致就是选择K个中心点.不断遍历更新中心点的位置.离哪个中心点近就属于哪 ...

  7. 软件包管理rpm_yum

    和文本相关的命令cat 正向显示文本tac 反向显示文本more 可以一步一步显示文本文件less 还可以往上看.几个快捷键:j(往下看), k (往上看), g(定位最上), G(定位最下), ct ...

  8. 【bzoj3930】[CQOI2015]选数 莫比乌斯反演+杜教筛

    题目描述 我们知道,从区间[L,H](L和H为整数)中选取N个整数,总共有(H-L+1)^N种方案.小z很好奇这样选出的数的最大公约数的规律,他决定对每种方案选出的N个整数都求一次最大公约数,以便进一 ...

  9. vmware esxi 查看网卡、Raid卡驱动

    vmware esxi 查看网卡.Raid卡驱动 http://blog.51cto.com/adamcrab/1942763 查看网卡 [root@localhost:~] esxcfg-nics  ...

  10. php函数总结

    1. isset($var) 变量存在且不为NULL,则返回TRUE 变量不存在或为NULL,则返回FALSE 2. empty($var) 若变量不存在或变量值为"".0.&qu ...