JNI和NDK基础
引言
JNI是Java Native Interface(Java本地接口),是为了方便Java调用C和C++等本地代码所封装的一层接口。
NDK是Android提供的一个工具集合,通过NDK可以在Android中更加方便地沟通JNI来访问本地代码。
两者之间的关系:JNI是实现目的,NDK是在Android实现JNI的手段。
使用NDK有如下好处:
- 提供代码的安全性
- 可以很方便的地使用目前已有的C/C++开源库
- 便于平台间的移植
- 提高程序在某些特定情形下的执行效率,但不能明显提升Android程序的性能
JNI基础
- JNI类型(基本类型和引用类型)
基本类型如下表格:
| 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类型 | 描述 |
|---|---|---|
| jobject | Object | Object类型 |
| jclass | Class | 类 |
| jstring | String | 字符串 |
| jobjectArray | Object[] | 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签名
类的签名,它采用“L+包名+类名+;”的形式,只需要将其中的.替换为/即可,例如:java.lang.String,它的签名为Ljava/lang/String;。
基本数据类型签名采用一系列大写字母来表示,如下:
| Java类型 | 签名 | Java类型 | 签名 |
|---|---|---|---|
| boolean | Z | long | J |
| byte | B | float | F |
| char | C | double | D |
| short | S | void | V |
| int | I |
数组签名根据基本类型和类的签名在前面加上“[”,例如 char[]数组,前面[C。对于多维数组,签名是n个[+类型签名,例如int[][],签名为[[I。
方法的签名为参数类型签名+返回类型签名,例如int fun(int i),签名为(I)I。
JNI开发流程
- 声明native方法
public class JniDes {
static {
System.loadLibrary("jni-des");
}
public native String encry(String value);
public native String decrypt(String value);
}
- Terminal命令生成JNI头文件
D:\test projects\JniDemo2>cd jni/src/main/java/com/fomin/demo/jni
D:\test projects\JniDemo2\jni\src\main\java\com\fomin\demo\jni>javac JniDes.java
D:\test projects\JniDemo2\jni\src\main\java>javah com.fomin.demo.jni.JniDes
javac命令生成class文件,根据这个文件使用javah生成JNI头文件,以下是生成文件内容:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_fomin_demo_jni_JniDes */
#ifndef _Included_com_fomin_demo_jni_JniDes
#define _Included_com_fomin_demo_jni_JniDes
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_fomin_demo_jni_JniDes
* Method: encry
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_fomin_demo_jni_JniDes_encry
(JNIEnv *, jobject, jstring);
/*
* Class: com_fomin_demo_jni_JniDes
* Method: decrypt
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_fomin_demo_jni_JniDes_decrypt
(JNIEnv *, jobject, jstring);
#ifdef __cplusplus
}
#endif
#endif
头文件解析:
Java_com_fomin_demo_jni_JniDes_encry:表示是一个函数名,命名规则Java_包名_native的方法名
JNIEnv *:一个指向JNI环境的指针
jobject:Java对象中的this
jstring:参数名称
JNIEXPORT 和JNICALL :JNI定义的宏
实现JNI方法
- c++实现方式
#include "JniDes.h"
#include <stdio.h>
JNIEXPORT jstring JNICALL Java_com_fomin_demo_jni_JniDes_encry
(JNIEnv *env, jobject thiz, jstring value){
// 加密逻辑
printf("加密开始\n");
return env->NewStringUTF("encry success");
}
JNIEXPORT jstring JNICALL Java_com_fomin_demo_jni_JniDes_decrypt
(JNIEnv *env, jobject thiz, jstring value){
// 解密逻辑
printf("解密开始\n");
return env->NewStringUTF("decrypt success");
}
* c实现方式
#include "JniDes.h"
#include <stdio.h>
JNIEXPORT jstring JNICALL Java_com_fomin_demo_jni_JniDes_encry
(JNIEnv *env, jobject thiz, jstring value){
// 加密逻辑
printf("加密开始\n");
return (*env)->NewStringUTF(env,"encry success");
}
JNIEXPORT jstring JNICALL Java_com_fomin_demo_jni_JniDes_decrypt
(JNIEnv *env, jobject thiz, jstring value){
// 解密逻辑
printf("解密开始\n");
return (*env)->NewStringUTF(env,"decrypt success");
}
c++和c实现方式类似,不同的是对env的操作,例如c++:env->,而c:(*env)->
NDK开发流程
- 配置NDK


- 创建Android.mk文件
LOCAL_PATH := $(call my-dir) #返回包含Android.mk的目录路径
include $(CLEAR_VARS) #变量由Build System提供。并指向一个指定的GNU Makefile,由它负责清理很多LOCAL_xxx
LOCAL_MODULE := jni-des #模块必须定义,以表示Android.mk中的每一个模块。名字必须唯一且不包含空格
LOCAL_SRC_FILES := JniTest.cpp #变量必须包含将要打包如模块的C/C++ 源码
include $(BUILD_SHARED_LIBRARY) #负责收集自从上次调用 include $(CLEAR_VARS) 后的所有LOCAL_XXX信息。并决定编译为什么
# BUILD_STATIC_LIBRARY:编译为静态库。
# BUILD_SHARED_LIBRARY :编译为动态库
# BUILD_EXECUTABLE:编译为Native C可执行程序
- 创建Application.mk
APP_ABI := armeabi armeabi-v7a x86 #默认情况下,ndk构建系统为armeabi,ABI生成二进制文件
APP_PLATFORM = android-9 #使用的ndk库函数版本号。一般和SDK的版本相对应,各个版本在NDK目录下的platforms文件夹中
- 配置gradle文件
- 配置so库存放目录
sourceSets {
main {
jniLibs.srcDirs = ['src/main/libs']
jni.srcDirs = []
}
}
- 配置ndk build task
def getNdkDir() { // 获取系统的ndk目录
if (System.env.ANDROID_NDK_HOME != null)
return System.env.ANDROID_NDK_HOME
else
throw new GradleException("NDK location not found. Define location with ndk.dir in the local.properties file or with an ANDROID_NDK_ROOT environment variable.")
}
def getNdkBuildCmd() { // 组合ndk文件
def ndkbuild = getNdkDir() + "\\ndk-build"
if (Os.isFamily(Os.FAMILY_WINDOWS))
ndkbuild += ".cmd"
return ndkbuild
}
task ndkBuild(type: Exec, description: 'Compile JNI source via NDK') {
commandLine getNdkBuildCmd(),//配置ndk的路径
'NDK_PROJECT_PATH=build/intermediates/ndk',//ndk默认的生成so的文件
'NDK_LIBS_OUT=src/main/jniLibs',//配置的我们想要生成的so文件所在的位置
'APP_BUILD_SCRIPT=src/main/jni/Android.mk',//指定项目以这个mk的方式
'NDK_APPLOCATION_MK=src/main/jni/Application.mk'//指定项目以这个mk的方式
}
tasks.withType(JavaCompile) {
compileTask -> compileTask.dependsOn ndkBuild //使用ndkBuild
}
- 生成so库


- 使用NDK方法
JniDes jniDes = new JniDes();
Log.d("MainActivity", jniDes.decrypt("test"));
Log.d("MainActivity", jniDes.encry("test"));
JNI调用Java方法
首先在JniDes类定义两个方法
private static void callStaticMethod(String str) {
System.out.format("JniDes::callStaticMethod called!-->str=%s\n", str);
}
private void callInstanceMethod(String str) {
System.out.format("JniDes::callInstanceMethod called!-->str=%s\n", str);
}
然后在JNI调用定义的静态方法
void callJavaStaticMethod(JNIEnv *env, jobject thiz){
jclass clazz=env->FindClass("com/fomin/demo/jni/JniDes")
if(clazz == null){
return;
}
jmethodID static_method= env->GetStaticMethodID(clazz,"callStaticMethod","(Ljava/lang/String;)V");
if (static_method == NULL) {
printf("找不到callStaticMethod静态方法");
return;
}
jstring str = env->NewStringUTF("我是静态方法");
env->CallStaticVoidMethod(clazz,static_method, str);
// 删除局部引用
env->DeleteLocalRef(clazz);
env->DeleteLocalRef(str);
}
或者JNI调用定义的非静态方法
void callJavaMethod(JNIEnv *env, jobject thiz){
jclass clazz = env->FindClass("com/fomin/demo/jni/JniDes");
if (clazz == NULL) {
printf("找不到JniDes这个类");
return;
}
// 获取类的默认构造方法ID
jmethodID mid_construct = env->GetMethodID(clazz, "<init>","()V");
if (mid_construct == NULL) {
printf("找不到默认的构造方法");
return;
}
// 查找实例方法的ID
jmethodID mid_instance = env->GetMethodID(clazz, "callInstanceMethod", "(Ljava/lang/String;)V");
if (mid_instance == NULL) {
return;
}
// 创建该类的实例
jobject jobj = env->NewObject(clazz,mid_construct);
if (jobj == NULL) {
printf("找不到callInstanceMethod方法");
return;
}
// 调用对象的实例方法
jstring str = env->NewStringUTF("我是实例方法");
env->CallVoidMethod(jobj,mid_instance,str);
// 删除局部引用
env->DeleteLocalRef(clazz);
env->DeleteLocalRef(jobj);
env->DeleteLocalRef(str);
}
JNI调用静态方法和非静态方法,非静态方法多了一步构造对象的过程。
JNI和NDK基础的更多相关文章
- [ 转载 ] Android JNI(一)——NDK与JNI基础
Android JNI(一)——NDK与JNI基础 隔壁老李头 关注 4.4 2018.05.09 17:15* 字数 5481 阅读 11468评论 8喜欢 140 本系列文章如下: Androi ...
- Android JNI(一)——NDK与JNI基础
本系列文章如下: Android JNI(一)——NDK与JNI基础 Android JNI学习(二)——实战JNI之“hello world” Android JNI学习(三)——Java与Nati ...
- Android JNI和NDK学习(04)--NDK调试方法(转)
本文转自:http://www.cnblogs.com/skywang12345/archive/2013/05/23/3092812.html 本文主要介绍在ndk中添加log的方法.然后,我们就可 ...
- JNI和NDK编程
Java JNI的本意是Java Native Interface(Java本地接口),它是为了方便Java调用C.C++等本地代码所封装的一层接口.通过Java JNI,用户可以调用C.C++所编写 ...
- JNI与NDK简介
最近稍微了解一下JNI和NDK. 网上各种教程给人一种二者不分的感觉, 经过自己运行代码, 将两者的关系理了一下. 就目前了解,JNI应该是java自带的一种调用c和c++等语言(native cod ...
- JNI和NDK的区别
http://blog.csdn.net/ithomer/article/details/6828830 NDK(Native Development Kit)“原生”也就是二进制 android常用 ...
- 【转】 Android 开发 之 JNI入门 - NDK从入门到精通
原文网址:http://blog.csdn.net/shulianghan/article/details/18964835 NDK项目源码地址 : -- 第一个JNI示例程序下载 : GitHub ...
- 【转】JNI和NDK的区别
原文网址:http://blog.csdn.net/ithomer/article/details/6828830 NDK(Native Development Kit)“原生”也就是二进制 andr ...
- 《Android开发艺术探索》读书笔记 (13) 第13章 综合技术、第14章 JNI和NDK编程、第15章 Android性能优化
第13章 综合技术 13.1 使用CrashHandler来获取应用的Crash信息 (1)应用发生Crash在所难免,但是如何采集crash信息以供后续开发处理这类问题呢?利用Thread类的set ...
随机推荐
- SWPU-ACM集训队周赛之组队赛(3-11) C题题解
点这里去看题 模拟,注意细节 #include<stdio.h> #include<string.h> int main() { ]; //q[]储存正负信息 scanf(&q ...
- TensorFlow 神经网络教程
TensorFlow 是一个用于机器学习应用程序的开源库.它是谷歌大脑的第二代系统,在取代了近源的 DistBelief 之后,被谷歌用于研究和生产应用.TensorFlow 提供了很多种语言接口,包 ...
- Java 实现将其他类型数据转换成 JSON 字符串工具类
这是网上一个大神实现的,具体出处已找不到,在这做个记录,方便以后使用. package com.wb.test; import java.beans.IntrospectionException; i ...
- Jedis 操作 Redis 工具类
配置类 pom.xml pom.xml 里配置依赖 <dependency> <groupId>redis.clients</groupId> <artifa ...
- 第二十三节:Java语言基础-详细讲解函数与数组
函数 函数在Java中称为方法,在其他语言中可能称为函数,函数,方法就是定义在类中具有特定功能的程序.函数,在Java中可称为方法. 函数的格式: 修饰符 返回值类型 函数名(参数类型 参数1, 参数 ...
- vue 自学笔记(4): 样式绑定与条件渲染
一:对象绑定 Vue 对于页面的样式加载也有独特的方式,按照 Vue 提供的方式,我们可以轻松的控制它们的呈现. 假使我们要实现点击 div 变色 Vue 提供的样式方案的本质是对元素节点进行属性的绑 ...
- tomcat-四种运行模式和三种部署模式(优化)
四中运行模式如下: 1-bio: 传统的Java I/O操作,同步且阻塞IO. 2-nio: JDK1.4开始支持,同步阻塞或同步非阻塞IO 3-aio(nio.2): JDK7开始支持,异步非阻塞I ...
- Spring 源码分析之 bean 实例化原理
本次主要想写spring bean的实例化相关的内容.创建spring bean 实例是spring bean 生命周期的第一阶段.bean 的生命周期主要有如下几个步骤: 创建bean的实例 给实例 ...
- [COI2007] Sabor
下面给出这道一脸不可做的题的鬼畜性质: 1)对于一个点来说,其归属状态是确定的:走不到.A党或B党 .(黑白格染色) 方便起见,将包含所有不可达的点的极小矩形向外扩展一圈,设为矩形M. 2)矩形M的最 ...
- [每天解决一问题系列 - 0008] 关于.net framework 路径最大长度的问题
问题描述: 有时候,在copy文件的时候,会提示目录长度太大,无法copy 解决方法: 可以使用Long Path Tool 解决问题 相关解释: http://blogs.msdn.com/b/bc ...