@Dlive

0x01 JNI介绍

JNI是Java Native Interface的缩写,JNI不是Android专有的东西,它是从Java继承而来,但是在Android中,JNI的作用和重要性大大增强。

JNI在Android中起着连接Java和C/C++层的作用,现在APP的许多重要的逻辑,算法以及和底层的交互功能都是通过JNI调用C/C++来实现。

简单来说,JNI提供了一种可以让Java代码调用C/C++代码的接口。

0x02 JNI中的类型/数据结构

JNI的类型/数据结构,以及函数的声明都放在jni.h的头文件中,这个头文件可以在NDK的platforms文件夹下android对应版本的对应架构文件夹下找到。比如

platforms\android-21\arch-arm64\usr\include\jni.h

一些比较重要的类型如下:

1. 基本类型(都是一些C类型的重命名)

2. 常见的对象类型

jobject 代表Java中的对象

jstring 代表Java中的String类型

jclass 代表Java中的类

jobjectArray/jbooleanArray 等都是数组类型

3. JNIEnv代表JNI环境的结构体

可以看到JNIEnv结构体中有大量函数指针,我们可以通过这个结构体来调用jni.h中声明的函数,之后会有代码来说明这结构体的作用

0x03 Native函数的静态注册与动态注册

前面的数据类型比较无聊,其实我们主要关注的还是Java native函数是怎么和C/C++关联起来的,关联起来后,C/C++函数的编写和正常的C/C++代码基本相同。

Native函数的注册有两种方式:静态注册和动态注册。

关于使用Android Studio编写Native代码的相关配置可以参考:

http://tools.android.com/tech-docs/new-build-system/gradle-experimental

1. 静态注册

静态注册较为简单,所以先来用一个例子来说明一下静态注册。

使用AndroidStdio新建项目,我们把项目命名为HelloNative

在项目中新建一个文件夹jni

在MainActiviy中声明两个native属性函数,第一个函数为static, AS提供alt+enter快捷键可以新建mainactivity并快速生成这两个函数对应的Native层函数框架

public static native String Hello1();
public native String Hello2(int vint);

在MainActivity开始处加载so库

static {
System.loadLibrary("mainactivity");
}

mainactivity.c:

#include <jni.h>

JNIEXPORT jstring JNICALL
Java_dlive_hellonative_MainActivity_Hello1(JNIEnv *env, jclass type) {
return (*env)->NewStringUTF(env, "natvie hello1");
} JNIEXPORT jobject JNICALL
Java_dlive_hellonative_MainActivity_Hello2(JNIEnv *env, jobject instance, jint vint) { // TODO
return (*env)->NewStringUTF(env, "native hello2");
}

mainactiviy.c中的函数就是我们要编写的native函数

Java_dlive_hellonative_MainActivity_Hello1对应Java中声明的Hello1函数

Java_dlive_hellonative_MainActivity_Hello2对应Java中声明的Hello2函数

这就是静态注册,即Native函数的函数名由Hello1的packagename和Hello1的函数名组成,Java层调用Hello1时,会调用mainactivity.so中对应的

Java_dlive_hellonative_MainActivity_Hello1函数。

这里还有几点要注意:

1. Java层函数对应的Native层函数的第一个参数为JNIEnv*

2. 被声明为static的函数Hello1对应的Native函数

Java_dlive_hellonative_MainActivity_Hello1 第二个参数为jclass,表示Hello1函数所在的类

3. 没有static声明的函数对应的Native函数的第二个参数为jobject,表示调用Hello2的对象

4. Native函数从第三个参数开始才对应Java层函数的参数

2. 动态注册

动态注册与静态注册的区别在于Native的函数名可以自定义,然后使用JNI提供的

RegisterNatives动态将Java层函数和Native层函数绑定起来即可。

动态注册主要依赖两个函数JNI_OnLoad和RegisterNatives

JNI_Onload会在so加载的时候自动被调用,在JNI_Onload中调用RegisterNatives将Java层函数和Native层函数关联起来。

MainActivity.java中native属性的函数:

public native String Hello3(MainActivity main);

在jni目录下新建hello.cpp和hello.h,在hello.cpp中实现native函数

jstring helloNative(JNIEnv* env, jobject jobj, jobject jobj1)
{
return env->NewStringUTF("hello native 3");
}

可以看到实现的函数名称为helloNative,而不是静态注册时很长的函数名。

const char* gClassName = "dlive/hellonative2/MainActivity";
//JNINativeMethod是一个结构体
JNINativeMethod gMethods[] = {
{"Hello3", "(Ldlive/hellonative2/MainActivity;)Ljava/lang/String;", (void*)helloNative},
};

gClassName : Java层类名

gMethods:{java层函数名,java层函数签名【(参数)返回值】,native层函数指针}

//reserved 保留参数
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void* reserved) {
JNIEnv* env = NULL;
// jint result = -1;
if(vm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK) {
return -1;
}
if (registerNativeMethods(env, gClassName, gMethods,
sizeof(gMethods) / sizeof(gMethods[0])) == JNI_FALSE)
{
return -1;
}
LOGD("So load success");
return JNI_VERSION_1_6;
}

registerNativeMethods是找了一个网上的实现,就是封装了一下RegisterNatives

int registerNativeMethods(JNIEnv* env, const char* className,
JNINativeMethod *gMethods, int numMethods) {
jclass clazz;
clazz = env->FindClass(className);
if(clazz == NULL) {
return JNI_FALSE;
}
if(env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
return JNI_FALSE;
}
return JNI_TRUE;
}

0x04 JNI中提供的函数

JNI中提供了哪些函数可以在jni.h中找到,这里只讲一下常用的几个函数。

讲解顺序是按照jni.h中声明的顺序排序的。

//根据packagename找到并返回java class

//比如 jclass stringClass = env -> FindClass("java/lang/String");

//jobjectArray array = env -> NewObjectArray(count, stringClass, NULL);

jclass FindClass(const char *name)

//生成一个Java对象

//clazz为FindClass的返回结果

//methodID指Java类的构造函数ID

//调用一个Java对象的方法或者存取一个Java对象的域变量前,要先知道这个方法或域变量的ID

//取得方法ID和域ID的函数:

//GetMethodID

//GetFieldID

//例子:

//jclass clazz = env->FindClass("android/content/Intent")

//jmethodID method = env->GetMethodID(clazz, "<init>", "Ljava/lang/String")

//jstring action = env -> NewStringUTF("android.intent.action.MAIN")

//jobject intent = env -> NewObject(clazz, method, action)

jobject NewObject(jclass clazz, jmethodID methodID, ...)

//clazz为FindClass返回值

//name为函数名字,如构造函数为 "<init>"

//sig为函数签名, 如Content类<init>的签名为,"Ljava/lang/String" (参数)

jmethodID GetMethodID(jclass clazz, const char *name, const char *sig)

//获取字符串内容,对应ReleaseStringUTFChars,该函数返回的指针要使用ReleaseStringUTFChars释放

const char* GetStringUTFChars(jstring str, jboolean *isCopy)

//创建clazz的对象数组,len为长度,init表示将数组元素初始化为什么一般为NULL

//jobjectArray array = env -> NewObjectArray(count, stringClass, NULL);S

jobjectArray NewObjectArray(jsize len, jclass clazz, jobject init)

//获取对象数组里的元素,第二个参数为数组下标

//如果一次只取一个元素,可以使用下面的函数,也不用释放内存,更加方便,参考《深入解析Android5.0系统》

jobject GetObjectArrayElement(jobjectArray array, jsize index)

//给对象数组元素赋值

// 例:

// for(int i=0; i< count; i++)

// {

// jstring str = env -> NewStringUTF("HELLO");

// if(str == NULL)

// {

// return NULL;

// }

// env -> SetObjectArrayElement(array, i, str);

// }

// return array;

void SetObjectArrayElement(jobjectArray array, jsize index,jobject val)

//返回int数组指针,第二个参数值是否拷贝一份数组出来,数组用完后要使用ReleaseIntArrayElements释放数组内存

jint * GetIntArrayElements(jintArray array, jboolean *isCopy)

Android JNI总结的更多相关文章

  1. [转载]—— Android JNI知识点

    Java Native Interface (JNI)标准是java平台的一部分,它允许Java代码和其他语言写的代码进行交互.JNI 是本地编程接口,它使得在 Java 虚拟机 (VM) 内部运行的 ...

  2. Android JNI 和 NDK

    1.Android NDK 一.NDK产生的背景 Android平台从诞生起,就已经支持C.C++开发.众所周知,Android的SDK基于Java实现,这意味着基于Android SDK进行开发的第 ...

  3. Android jni简便开发流程

    <Android jni helloworld>中介绍了开发jni helloworld的步骤,本文将介绍jni简便开发流程 ① 写java代码 native 声明本地方法 ② 添加本地支 ...

  4. Android jni系统变量、函数、接口定义汇总

    在做Android jni开发时,jni为我们提供了哪些函数.接口.变量,有时候一头雾水,今天就把jni.h中定义的所有内容列出来,供自己查阅: /* * Copyright (C) 2006 The ...

  5. android JNI调用(转)

    Android jni开发资料--NDK环境搭建 android开发人员注意了 谷歌改良了ndk的开发流程,对于Windows环境下NDK的开发,如果使用的NDK是r7之前的版本,必须要安装Cygwi ...

  6. Android JNI如何调用第三方库

    http://www.2cto.com/kf/201504/388764.html Android JNI找不到第三方库的解决方案 cannot load library 最近做一个jni项目,拿到的 ...

  7. Android JNI之JAVA与C++对象建立对称关联(JNI优化设计,确保JNI调用的稳定性)

    转载请声明:原文转自:http://www.cnblogs.com/xiezie/p/5930503.html Android JNI之JAVA与C++对象建立对称关联 1.JAVA对象持有C++对象 ...

  8. 利用gdb 调试android jni c动态库

    http://blog.dornea.nu/2015/07/01/debugging-android-native-shared-libraries/ Since I haven't done thi ...

  9. Android JNI使用方法

    经过几天的努力终于搞定了android JNI部分,下面将我的这个小程序和大家分享一下.android JNI是连接android Java部分和C/C++部分的纽带,完整使用JNI需要Java代码和 ...

  10. 【转】android JNI

    原文网址:http://jinguo.iteye.com/blog/696185 Java Native Interface (JNI)标准是java平台的一部分,它允许Java代码和其他语言写的代码 ...

随机推荐

  1. sql server 中隐藏掉无关数据库

    先贴上我实际测试的效果 方法一: Problem I have a SQL Server instance that has hundreds of databases.  Navigating th ...

  2. jQuery 事件方法

    事件方法触发器或添加一个函数到被选元素的事件处理程序. 下面的表格列出了所有用于处理事件的 jQuery 方法. 方法 描述 bind() 向匹配元素附加一个或更多事件处理器 blur() 触发.或将 ...

  3. BOM以及定时器

    一.BOM 1.操作浏览器的一些方法 (浏览器对象模型) 2.window是is中的顶级变量,是一个全局的变量,所有人都可以访问到它,基本 的方法和属性 (document,alert,console ...

  4. BZOJ3434 [Wc2014]时空穿梭

    本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000 作者博客:http://www.cnblogs.com/ljh2000-jump/ ...

  5. flex学习小结

    接触到flex一个多月了,今天做一个学习小结.如果有知识错误或者意见不同的地方.欢迎交流指教. 画外音:先说一下,我是怎么接触到flex布局的.对于正在学习的童鞋们,我建议大家没事可以逛逛网站,看看人 ...

  6. iOS XCode7制作.Framework动态库和.a静态库的总结

    一.开发SDK时的支持情况: OC语言制作动态库时,支持iOS8+:OC语言制作静态库,支持iOS7+. Swift语言制作动态库时,支持iOS8+;Swift不支持静态库. 对于SDK来说,支持情况 ...

  7. Theano Graph Structure

    Graph Structure Graph Definition theano's symbolic mathematical computation, which is composed of: A ...

  8. dict与list的in 操作的速度

    今天刷一道题,计算一串数字中其中两个数字相加等于目标值的题目,且取其中最早的两个数字(最后一个数字的位置靠前). 如[1,25,32,4,3,6,9,5]  targer:9  输出 [3,6]   ...

  9. excel批处理_判断一个名称是不是药品

    把药品名称导入到sheet1的A字段 # -*- coding: utf-8 -*-"""Created on Fri Dec  9 09:38:58 2016判断一个名 ...

  10. Nodejs事件引擎libuv源码剖析之:请求(request)结构的设计剖析

    声明:本文为原创博文,转载请注明出处.         在libuv中,请求(request)代表一个用户向libuv发出的指令,比如uv_connect_s就表示一个tcp的连接请求.uv_work ...