JNI基本使用

目录

(一)基本使用

1、Java层声明本地方法

2、Native层关联Java层本地方法

2.1 使用静态注册的方法关联Java层本地方法(不推荐)

2.2 使用动态注册的方法关联Java层本地方法 (推荐)

(二)数据类型

(一)基本使用

1、Java层声明本地方法

编写java文件

package com.example.jnitest;

//import android.util.Log;


public class Hello {
	
	public static native String helloFromNative(String name); 	// 1.声明native函数
 
	public static void main(String[] args) {
		String text = helloFromNative("yangxin");	// 3.调用native函数
		//LogUtils.i(TAG, "ttttttttttttt"+text);
	}
	
	static {
        // 2.加载实现了native函数的动态库,只需要写动态库的名字,
        // 这里名字叫HelloWorld,那么动态库就是libHelloWorld.so
		System.loadLibrary("HelloWorld");
	}
 }

深入理解 System.loadLibrary - Pqpo's Notes

(import暂时注释掉了,因为不知道怎么在import的情况下进行下一步用javac出来class文件...我真菜)

(哦我知道了,就正常编译就行,不用javac,去out目录下找class文件就行,比如我的在 out/target/common/obj/APPS/FactoryKitTest_intermediates/classes/里面hhh

2、Native层关联Java层本地方法

2.1 使用静态注册的方法关联Java层本地方法(不推荐)

2.1.1 javac 生成class文件

javac src/com/study/jnilearn/HelloWorld.java -d ./bin

-d是表示将编译后的class文件放到指定的目录下,不加也行

2.1.2 用javah -jni命令,根据class字节码文件生成.h头文件

(ps:如果使用动态注册的话就不用生成.h头文件,但必须实现JNI_OnLoad回调函数,动态注册的工作就是在这里完成的)

javah -jni -classpath ./bin -d ./jni com.study.jnilearn.HelloWorld 或者

javah -jni -classpath ./bin -o HelloWorld.h com.study.jnilearn.HelloWorld

-classpath :类搜索路径,这里表示从当前的bin目录下查找

-d :将生成的头文件放到当前的jni目录下

-o : 指定生成的头文件名称,默认以类全路径名生成(包名+类名.h)

注意:-d和-o只能使用其中一个参数。

我这次用的javah -jni  -classpath ./src/ -d .  包名.类名

这里需要注意:

javah找不到类文件(已解决)_绝代不风华的博客-CSDN博客_java找不到类

(1)java和class不能放在同一个文件夹中!!!

(2)-classpath 后面的路径必须是包名的上一级目录!!!

2.1.3 编写cpp文件

引入生成的头文件,实现头文件中的方法;

#include "com_example_jnitest_Hello.h"

JNIEXPORT jstring JNICALL Java_com_example_jnitest_Hello_helloFromNative
        (JNIEnv *env, jobject obj){

    return env->NewStringUTF("Hello From Native");

}

2.2 使用动态注册的方法关联Java层本地方法 (推荐)

动态注册不用生成.h头文件,而是直接编写cpp文件。

注意。需要在cpp文件中实现JNI_OnLoad回调函数,也就是在这里完成动态注册的工作。

(因为Java层执行System.loadLibrary加载完动态库后,JNI_OnLoad方法会被调用(如果有的话),所以在JNI_OnLoad方法中通过调用RegisterNatives方法完成注册操作是非常合理的,另外虽然静态方法不需要实现这个JNI_OnLoad,但我们都建议实现一下,因为显然这个函数适合做一些初始化的操作)

#include <jni.h>
#include <cassert>
#include <cstdlib>
#include <iostream>
using namespace std;


/*native 方法实现
方法名无要求,但要保证方法的返回值和参数格式与java层保持一致,

其中返回值使用与java中返回值对应的jni类型;

而参数最少有2个:JNIEnv、jobect或jclass。

第一个是JNIEnv,固定不变(与java虚拟机线程相关的代表jni环境的结构体)
第二个jobject 代表的是java层的Hello这个类的对象(调用这个函数的对象),当然如果是实例方法才为jobject,如果是静态方法则为jclass;
剩下的参数与java方法中的参数保持一致。
*/
jstring native_hello(JNIEnv *env, jobject obj){
    return env->NewStringUTF("Hello From Native");
}

/*将需要注册的函数列表,放在JNINativeMethod 类型数组中
以后如果需要增加函数,只需在这里添加就行了
参数:
1.java代码中用native关键字声明的函数名
2.方法描述符(方法签名,包含参数类型和返回值类型),具体规则见下面的Jni描述符章节
3.C/C++中对应函数的函数名
*/
static JNINativeMethod getMethods[] = {
        { "helloFromNative", "()Ljava/lang/String;", (void*)native_hello},
};

//此函数通过调用JNI中 RegisterNatives 方法注册函数
static int registerNativeMethods(JNIEnv* env, const char* className,JNINativeMethod* getMethods,int methodsNum){
    jclass clazz;
    //找到声明native方法的Java类
    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){
    //指定Java层的类描述符(具体规则见下面的Jni描述符章节),通过FindClass 方法来找到对应的类
    const char* className  = "com/example/jnitest/Hello";
    return registerNativeMethods(env,className,getMethods, sizeof(getMethods)/ sizeof(getMethods[0]));
}

//回调函数,Java层调用System.loadLibrary后执行, 在这里面注册函数
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved){
    JNIEnv* env = NULL;
   //判断虚拟机状态是否有问题
    if(vm->GetEnv((void**)&env,JNI_VERSION_1_6)!= JNI_OK){
        return -1;
    }
    assert(env != NULL);
    //开始注册函数, 调用顺序registerNatives -》registerNativeMethods -》env->RegisterNatives;
    //其实就是最终要调用env->RegisterNatives
    if(!registerNatives(env)){
        return -1;
    }
    //返回jni 的版本
    return JNI_VERSION_1_6;
}

 看着麻烦但是其实核心函数就下面的部分,嫌麻烦可以用JNIHelper.h

    clazz = env->FindClass(className);
    if(clazz == NULL){
        return JNI_FALSE;
    }
   //注册函数 参数:java类 所要注册的函数数组 注册函数的个数
    if(env->RegisterNatives(clazz,getMethods,methodsNum) < 0){
        return JNI_FALSE;
    }

在Android中使用JNI_gogo_wei的博客-CSDN博客_android jni使用

(二)数据类型

1.

基本数据类型:

引用数据类型: 

 

 可以看到,在引用数据类型里,除了数组和String,Class,Throwable,其余的都在jni中是jobject

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值