文档章节

Android JNI 函数注册的两种方式(静态注册/动态注册)

shzwork
 shzwork
发布于 06/27 08:05
字数 1658
阅读 4
收藏 0

JNI/NDK

在Android开发中,由于种种原因我们需要调用C/C++代码, 这个时候就要用到Android开发者都听说过的JNI(Java Native Interface)了, 在调用JNI相关方法之前, 要对java中native关键字定义的方法进行注册, 注册方式有两种: 静态注册和动态注册, 两者优缺点如下:

  • 静态注册
    优点: 理解和使用方式简单, 属于傻瓜式操作, 使用相关工具按流程操作就行, 出错率低
    缺点: 当需要更改类名,包名或者方法时, 需要按照之前方法重新生成头文件, 灵活性不高
  • 动态注册
    优点: 灵活性高, 更改类名,包名或方法时, 只需对更改模块进行少量修改, 效率高
    缺点: 对新手来说稍微有点难理解, 同时会由于搞错签名, 方法, 导致注册失败

静态注册

此注册方法是初学者经常用到的, 比较常见, 这里简单说下流程,
1.编写一个java类,在里面加载对应的so库并且通过native关键字定义需要调用的函数

package com.example.wenzhe.myjni;
/**
 * Created by wenzhe on 16-1-27.
 */
public class JniTest {
public native int getRandomNum();
public native String getNativeString();

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

2.在命令行下输入 javac JniTest.java 生成JniTest.class文件
然后在src目录下通过 javah com.example.wenzhe.myjni.JniTest 生成 com_example_wenzhe_myjni_JniTest.h 头文件
3.将头文件拷贝到jni目录下(eclipse在src同级目录建立文件夹,Android studio 在java同级目录建立文件夹)
4.编写C/C++源代码 并把刚拷贝的头文件包含进去 ,复制头文件中函数的定义部分,并实现其中的你想要的功能

然后编写Android.mk Application.mk(Application.mk主要用来定义适应的平台,x86 arm等)

Android.mk如下:

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE := HelloJni
LOCAL_SRC_FILES := HelloJni.cpp

include $(BUILD_SHARED_LIBRARY)

Application.mk如下:

#支持标准C++特性
APP_STL := gnustl_static
APP_CPPFLAGS := -frtti -fexceptions
#支持的CPU架构
APP_ABI := armeabi-v7a
#Android 版本
APP_PLATFORM := android-22

include $(BUILD_SHARED_LIBRARY)

其中LOCAL_MODULE定义的名字就是生成的so库名字,so库前面都会有个lib前缀,上面生产的so应该为 libHelloJni.so

5.在命令行中进入jni目录,输入ndk-build 即可生产对应so库,会自动放在libs文件夹下 至此就可以运行程序了

动态注册

动态注册基本思想是在JNI_Onload()函数中通过JNI中提供的RegisterNatives()方法来将C/C++方法和java方法对应起来(注册), 我们在调用 System.loadLibrary的时候,会在C/C++文件中回调一个名为 JNI_OnLoad ()的函数,在这个函数中一般是做一些初始化相关操作, 我们可以在这个方法里面注册函数, 注册整体流程如下:

  1. 编写Java端的相关native方法
  2. 编写C/C++代码, 实现JNI_Onload()方法
  3. 将Java 方法和 C/C++方法通过签名信息一一对应起来
  4. 通过JavaVM获取JNIEnv, JNIEnv主要用于获取Java类和调用一些JNI提供的方法
  5. 使用类名和对应起来的方法作为参数, 调用JNI提供的函数RegisterNatives()注册方法

示例代码如下:

// jni头文件 
#include <jni.h>
 
#include <cassert>
#include <cstdlib>
#include <iostream>
using namespace std;
 
 
//native 方法实现
jint get_random_num(){
    return rand();
}
/*需要注册的函数列表,放在JNINativeMethod 类型的数组中,
以后如果需要增加函数,只需在这里添加就行了
参数:
1.java中用native关键字声明的函数名
2.签名(传进来参数类型和返回值类型的说明) 
3.C/C++中对应函数的函数名(地址)
*/
static JNINativeMethod getMethods[] = {
        {"getRandomNum","()I",(void*)get_random_num},
};
//此函数通过调用RegisterNatives方法来注册我们的函数
static int registerNativeMethods(JNIEnv* env, const char* className,JNINativeMethod* getMethods,int methodsNum){
    jclass clazz;
    //找到声明native方法的类
    clazz = env->FindClass(className);
    if(clazz == NULL){
        return JNI_FALSE;
    }
   //注册函数 参数:java类 所要注册的函数数组 注册函数的个数
    if(env->RegisterNatives(clazz,getMethods,methodsNum) < 0){
        return JNI_FALSE;
    }
    return JNI_TRUE;
}
 
static int registerNatives(JNIEnv* env){
    //指定类的路径,通过FindClass 方法来找到对应的类
    const char* className  = "com/example/wenzhe/myjni/JniTest";
    return registerNativeMethods(env,className,getMethods, sizeof(getMethods)/ sizeof(getMethods[0]));
}
//回调函数
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved){
    JNIEnv* env = NULL;
   //获取JNIEnv
    if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
        return -1;
    }
    assert(env != NULL);
    //注册函数 registerNatives ->registerNativeMethods ->env->RegisterNatives
    if(!registerNatives(env)){
        return -1;
    }
    //返回jni 的版本 
    return JNI_VERSION_1_6;
}

上面的代码就能实现动态注册JNI了 以后要增加函数只需在java文件中声明native方法,在C/C++文件中实现,
并在getMethods数组添加一个元素并指明对应关系,通过ndk-build 生成so库就可以运行了
其中JNI版本可以在jni.h头文件中去查看支持哪些版本,一般定义在文件最后几行

JNI 签名

动态注册中 JNINativeMethod 结构体中第二个参数需注意
括号内代表传入参数的签名符号,为空可以不写,括号外代表返回参数的签名符号,为空填写 V,对应关系入下表

签名符号 C/C++ java
V void void
Z jboolean boolean
I jint int
J jlong long
D jdouble double
F jfloat float
B jbyte byte
C jchar char
S jshort short
[Z jbooleanArray boolean[]
[I jintArray int[]
[J jlongArray long[]
[D jdoubleArray double[]
[F jfloatArray float[]
[B jbyteArray byte[]
[C jcharArray char[]
[S jshortArray short[]
L完整包名加类名; jobject class

举个例子:

传入的java参数有两个 分别是 int 和 long[] 函数返回值为 String 即函数的定义为:String getString(int a ,long[] b)
签名就应该是 :"(I[J)Ljava/lang/String;"(不要漏掉英文分号)
如果有内部类 则用 $ 来分隔 如:Landroid/os/FileUtils$FileStatus;

总结

当熟悉动态注册后, 动态注册无疑是注册函数的更好方式, 唯一要注意的是注册函数时, 需要额外小心, 别把类名,函数名和签名写错了, 不然loadLibraries时会导致应用Crash, 关于JNI部分知识, 我还会写一篇关于如何高效传递数据以及JNI开发过程中的一些坑的总结.



作者:smewise
链接:https://www.jianshu.com/p/1d6ec5068d05
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

本文转载自:https://www.jianshu.com/p/1d6ec5068d05

shzwork
粉丝 11
博文 581
码字总数 10198
作品 0
厦门
私信 提问
深入了解android平台的jni---注册native函数

注册native函数有两种方法:静态注册和动态注册。 1、静态注册方法 根据函数名找到对应的JNI函数:Java层调用函数时,会从对应的JNI中寻找该函数,如果没有就会报错,如果存在则会建立一个关联...

mfcai
2013/10/15
0
0
BroadcastReceiver的源码分析

android提供了广播机制,通过BroadcastReceiver可以在不同的进程间传递消息。类似于观察者模式,A应用通过注册广播表示A对消息subject感兴趣,当B应用发出subject类型的消息的时候,A应用就能...

JasmineBen
2018/05/18
0
0
Android Service 服务(二)—— BroadcastReceiver

一、 BroadcastReceiver简介 BroadcastReceiver,用于异步接收广播Intent,广播Intent是通过调用Context.sendBroadcast()发送、BroadcastReceiver()接收。 广播Intent的发送是通过调用Conte...

长平狐
2013/01/06
145
0
Android JNI学习(三)——Java与Native相互调用

本系列文章如下: Android JNI(一)——NDK与JNI基础 Android JNI学习(二)——实战JNI之“hello world” Android JNI学习(三)——Java与Native相互调用 Android JNI学习(四)——JNI的常用方法...

隔壁老李头
2018/05/09
0
0
Android学习之BroadcastReceiver总结

Android学习之BroadcastReceiver总结 代码下载:http://download.csdn.net/detail/nuptboyzhb/4482544 关于BroadcastReceiver的概述: ①广播接收器是一个专注于接收广播通知信息,并做出对应...

长平狐
2012/10/08
3.1K
0

没有更多内容

加载失败,请刷新页面

加载更多

Linux的基本命令

目录的操作命令(增删改查) 增: mkdir 目录名称; 查: ls 可以看到该目录下的所有的目录和文件 ls -a,可以看到该目录下的所有文件和目录,包括隐藏的 ls -l,可以看到该目录下的所有目录和...

凹凸凸
今天
2
0
在古老unix中增加新用户

Installing 4.3 BSD Quasijarus on SIMH 目标:要在4.3BSD中新增加用户dmr,指定目录/home/dmr,uid为10 gid=31(guest组,系统已建立) 4.3BSD还没有adduser或useradd 直接修改/etc/passwd...

wangxuwei
今天
2
0
Bootstrap(六)表单样式

基本样式 所有设置了 .form-control 类的 <input>、<textarea> 和 <select> 元素都将被默认设置宽度属性为 width: 100%;。 将 label 元素和前面提到的控件包裹在 .form-group 中可以获得最好...

ZeroBit
昨天
3
0
SSL 证书格式转换

SSL 证书格式转换 不同服务器情况下,需要不同的证书格式。 比如 pem 转 pfx。 pem在window 平台下可以导入,但是无法正常使用。 需要转换成pfx。 推荐在线转换工具,由中国数字证书网站提供...

DrChenXX
昨天
2
0
HAProxy

xx

Canaan_
昨天
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部