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 ...
随机推荐
- 《mysql必知必会》学习_第16章_20180807_欢
第16章:创建高级联结. P106 select concat(RTrim(vend_name),'(',RTrim(vend_country),')') as vend_title from ven ...
- 循环结构-for,while,do-while
for,while,do-while >完整的循环应该包含以下四个部分: 初始化语句:初始化工作,在循环体开始之前执行 条件表达式:一个boolean表达式,决定是否执行循环体 循环体:反复执行 ...
- [转]决策树在Kaldi中如何使用
转自:http://blog.csdn.net/chenhoujiangsir/article/details/51613144 说明:本文是kaldi主页相关内容的翻译(http://kaldi-a ...
- 设计模式总结(Java)—— 单例模式
1. 定义 为了确保一个类有且仅有一个实例,而且自行实例化并向整个系统提供这个实例. 2. 使用场景 确保某个类有且只有一个对象的场景,避免产生多个对象消耗过多的资源,或者某种类型的对象只应该有且只有 ...
- 正确使用AES对称加密
正确使用AES对称加密 经常我看到项目中有人使用了对称加密算法,用来加密客户或项目传输中的部分数据.但我注意到开发 人员由于不熟悉原理,或者简单复制网上的代码示例,有导致代码存在安全风险. 我经常遇到 ...
- php省市联动实现
设计模式:ajax实现,数据库格式:id,name,parent_id 数据库: CREATE TABLE IF NOT EXISTS `city` ( `id` ) NOT NULL AUTO_IN ...
- H5拖动火狐自动打开新标签
写在前面的话:<H5拖动火狐自动打开新标签>原因是为什么百度很多了我就不细说,本文章只说我自己的解决方法... 自定义数据里写个链接,如果火狐自动打开就跳到这个链接 这个页面就写一句话:关 ...
- shell编程中的循环语句
while循环直接从文件中读取 while read line do command done < filename until循环 until 条件 do command done for循环 ...
- 关于微信JS SDK接口wx.previewImage预览接口的使用
然后后之前的项目,突然往微信上迁移了,一些微信的接口没怎么用过,比较陌生,这次的功能是想调用微信的接口,实现图片放大的功能, 就找到官方文档:http://qydev.weixin.qq.com/wi ...
- Python --代码风格检查 pep8
pip3 install pycodestyle pycodestyle .. Pep8编码规范 https://blog.csdn.net/ratsniper/article/details/789 ...