JNI相关概念的理解

阅读本文前,前先阅读 JNI-NDK 在AndroidStudio3.2.1版本集成方法(ndk-build方式),了解jni在AndroidStudio里的集成步骤php

概念

Java 原生接口 (JNI):JNI 是 Java 和 C++ 组件用以互相通讯的接口。html

理解JNI

先说说JNIEnv

如今说的是C里的JNIEnv,不是C++里的JNIEnv,有点区别,可是理解了C里的JNIEnv,就理解了C++里的JNIEnvjava

# include "jni_study_com_jnibsetpractice_Jni.h"

JNIEXPORT jstring JNICALL Java_jni_study_com_jnibsetpractice_Jni_sayHello
        (JNIEnv *env, jobject instance) {
    return (*env)->NewStringUTF(env, "Hello from C");
}
复制代码

上面的代码里第一个参数是JNIEnv,这是个什么东西,顾名思义jni环境,点进去跳到了 android

这个文件里

发现 JNIEnv是JNINativeInterface*的别名,JNINativeInterface是什么,发现他是一个结构体,里面列出了许多方法指针,至关于java里的一个类,类里定义了不少方法,这些方法在jni开发中很是重要c++

看看这个方法是否是很眼熟,NewStringUTFbash

(*env)->NewStringUTF(env, "Hello from C");调用了这个方法,制造了一个字符串返给了java,你会问不就是一个字符串吗,我直接"Hello from C"返回不就好了吗?不行的,你知道C里是没有过String类型的,C里的"xxx",java并不认识,这就须要jni (java native interface),即【java 与本地语言(C/C++)接口】来解决这个问题

看看这个方法,传入一个*env和char *,char*就是C里的字符串类型,返回了jstring(jstring是java里的String的等价物),这样就把C里的字符串转换成java能够是别的字符串,你说这个方法重要吗,他是结构体JNINativeInterface提供的。架构

除了这个方法,其余不少方法都很重要,他的做用基本上就是一些jni开发中,经常使用到的一些方法,为咱们在C和Java之间搭建了一座桥梁,让彼此互相沟通oracle

下图是JNINativeInterface结构体的一个图示 app

JNINativeInterface里全部方法的说明看这里: 官方文档ide

全部方法分为如下几类

看看NewStringUTF的文档

之后会用到不少方法,均可以在这里查询

JNI里的数据类型

在上面的方法里,看到了不少奇奇怪怪的数据类型 jboolean jstring...,他们与java、c是如何对应的

咱们在jin.h里还发现了这段代码

查看数据类型的官方文档 文档截图:

参考类型 JNI包含许多与不一样类型的Java对象相对应的引用类型。JNI引用类型按层次结构组织,如图3-1所示。

JNIENV在C与C++的区别

我直接复制了上篇博客的内容

  1. 建立实现头文件的.cpp源文件 接下来要写个c++代码,实现这个jni接口

˙注意这里新建的是c++代码,c++代码对应下面的代码

//引入刚才生成的头文件
#include "ndkold_study_com_ndkolddemo_Java2CJNI.h"

//复制头文件里的要实现的方法名及其参数
JNIEXPORT jstring JNICALL
Java_ndkold_study_com_ndkolddemo_Java2CJNI_java2C(JNIEnv *env, jobject instance) {
//    实现这个方法,返回一个字符串
    return env->NewStringUTF("Hello from C++");
}
复制代码
  1. 你也能够写个.c源文件,其对应代码为
//引入刚才生成的头文件
#include "ndkold_study_com_ndkolddemo_Java2CJNI.h"

//复制头文件里的要实现的方法名及其参数
JNIEXPORT jstring JNICALL
Java_ndkold_study_com_ndkolddemo_Java2CJNI_java2C(JNIEnv *env, jobject instance) {
//    实现这个方法,返回一个字符串
    return (*env)->NewStringUTF(env, "Hello from C");
    //注意这里是(*env),并且须要传递一个参数(env)
}
复制代码

说明:c与c++就这点区别,查看jni.h文件,发如今c里的JNIEnv是结构体指针JNINativeInterface*的别名,因此JNIEnv *env至关于二级指针,如今要调用JNINativeInterface*里的方法,要用(*env)->xxx

在c++里JNIEnv是_JNIEnv的别名,在_JNIEnv内部里有个属性为结构体指针JNINativeInterface*,而后他把全部c里的方法都从新定义了一下,定义方式就是经过JNINativeInterface*调了一遍全部c里的方法,并且把JNINativeInterface*的对象以this方式传递进去了,可见这里的JNIEnv *env是一个一级指针,因此经过env就能够直接调用对应的方法了(有点绕)

Android.mk文件

Android.mk是Android提供的一种makefile文件,用来指定诸如编译生成so库名、引用的头文件目录、须要编译的.c/.cpp文件和.a静态库文件等。要掌握jni,就必须熟练掌握Android.mk的语法规范。

参考自Android.mk用法详解

这个文件rebuild后 androidstudio会自动生成,看个人 上篇博客能够找到他的生成的路径,有小坑请注意

Application.mk文件

摘自安卓Application.mk文件的属性说明和基本写法

使用androidstudio开发,不须要这个文件了,能够在gradle里配置相关属性,详情见 此文

静态连接库(.a) 与 动态连接库(.so)

咱们编译成功的文件是一个.so文件,咱们的C代码就打包在了这个文件中,相似于java的jar包。C编译后的代码有两种格式文件静态连接库(.a) 与 动态连接库(.so) 简单说区别为:

  1. 静态连接库文件大,里面有你本身写的逻辑,也有引用的其余库函数,可是容易移植
  2. 动态连接库文件小,公用的代码,能够直接引用,肯定是不容易移植,由于so文件里的代码不是独立能够运行的

下面的截图来自C/C++ 静态连接库(.a) 与 动态连接库(.so),想了解更多,请点击查看

静态连接库(.a)

动态连接库(.so)

ABI(应用二进制接口)

咱们常常看到下面这个图,会生成多个libxxx.so文件,他们分别在不一样的armxxx下面

简单来讲,每一个手机里的cpu是不一样的,不一样的cpu对应支持不一样的ABI,咱们编译出来的so文件,在不一样ABI上是不能兼容的,因此咱们要针对不一样的ABI变移除不一样的so文件,这样不一样cpu的手机均可以调用到适合本身的so文件。

不一样的 Android 手机使用不一样的 CPU,而不一样的 CPU 支持不一样的指令集。CPU 与指令集的每种组合都有专属的应用二进制接口,即 ABI。ABI 能够很是精确地定义应用的机器代码在运行时如何与系统交互。您必须为应用要使用的每一个 CPU 架构指定 ABI。---摘自android官网-ABI 管理

常见的有armeabi、armeabi-v7a、arm64-v8a、x8六、x86_64,在abiFilters配置

defaultConfig {
        ...
        ndk {
            moduleName "Java2C"
            //so文件名,若是这里配置了so文件名字,
            //记得更改Android.mk里的
            //LOCAL_MODULE :字段为 LOCAL_MODULE := Java2C
            abiFilters "armeabi", "armeabi-v7a", "x86" 
            //指定so文件所支持的CPU类型
            //若是不写的话,会生成全部的CPU类型的so文件
        }
    }
复制代码

再补充几个概念

jni的类型签名介绍

jni的类型签名表示了一个特定的Java类型,这个类型能够是方法和类,也能够是数据类型。

以上截图均来自JNI开发最佳实践,想了解更多,请点击查看

javap -s 获取方法签名

固然你能够经过上面的规律,本身去写出这方法签名,可是本身写,可能会写错,那么咱们能够经过javap -s来获得方法签名

先看一下个人目录结构

个人Jni这个文件内容为

若是你找不到这个classes目录,先rebuild项目,并且不一样版本的Androidstudio,这个目录路径不必定同样(可是确定在build文件夹下),例如Android Studio环境下 java方法签名的获取,这篇文章里的classes路径就与个人版本不同

  1. 在命令行进入到classes cd /Users/apple/AndroidStudioProjects/GoodDemo/JniBsetPractice/app/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes
  2. 运行javap -s jni.study.com.jnibsetpractice.Jni 结果为

红色框框里的就是方法签名,你们最好这样获取,省的报错了不知道(Jni调试很麻烦,咱们要尽可能少写错,因此最好复制)

小结

至此,关于jni里的一些概念,都了解了一遍,接下来,要敲几个demo,看看具体如何使用这些知识点

相关文章
相关标签/搜索