【NDK系列】给移动开发者的JNI快速入门

基本流程

  1. 定义native接口
  2. 生成头文件(利用AS或者javah命令)和对应接口方法(在.h或者c文件都可)
  3. 实现接口方法(用c/c++都可)
  4. 编译成对应平台的动态库so文件;
  5. Java层导入so库

关于导入so库一般有如下两个方法:

static{
	// 导入本地动态库,比如一些很大的so文件可以放在服务器然后下载到本地(data/data之下)
	System.load("libTest.so");
	
	// 导入apk libs中的动态库
	System.loadSystem("libTest.so");
}

JNIEnv

指定编译时采用c的编译方式,c不支持函数重载,c++支持函数重载:

#ifdef _cplusplus
external "C" {
#endif
  • JNIEXPORT标记函数为外部调用的函数,如果不标记则编译能通过,但是Java层无法调用;
  • JNICALL标记函数为jni调用的函数,可以省略;
  • env表示jni执行环境,每个线程都有一个实例,c语言中它是一个结构体指针,c++中它是一个结构体;
  • jobj表示java方法为普通方法,如果是静态方法则该参数为jclass;
JNIEXPORT jstring JNICALL com_example_TEST_testString(JNIEnv *env, jobject jobj){
	......
}

native操作Java

1 访问Java属性

如何获取方法或者字段的签名?

  • 方法一:使用javap -p -s classname即可查看
  • 方法二:记住规律,参见下节表格

关于java的访问修饰符

  • 对于native层来说不区分public或者private,都是可见的

访问非静态属性:

jclass j_clz=(*env)->GetObjectClass(evn, jobj);
jfield j_fid=(*env)->GetFieldID(env, "field_name","field_signature");//filed_signature比如:Ljava/lang/String;
jobject j_fieldvalue=(*env)->GetObjectField(env, jobj,j_fid);    //这里返回值通常会直接转换为实际的字段类型,比如jstring
(*env)->SetObjectField(env, j_fid, j_value);

访问静态属性:

jfieldID j_fid = (*env)->GetStaticFieldID(evn, jclz, "field_name","field_signature");
jint j_value = (*env)->GetStaticIntField(env, jclz, j_fid);
j_value ++;
(*env)->SetStaticIntField(env, jclz, j_fid, j_value);

jstring与char*互转(jint与c的int之间不需要互转):

//java string to c string
char* c_str = (*env)->GetStringUTFChars(env, j_str, NULL);
(*env)->ReleaseStringUTFChars(env, c_str,j_str);

// c string to java string
(*env)->NewStringUTF(env, c_str);

2. 调用Java方法

调用非静态方法:

jclass j_clz = (*env)->GetObjectClass(env, jobj);
jmethodID j_mid=(*env)->GetMethodID(env, j_clz, "method_name","method_signature");
jint result = (*env)->CallIntMethod(env, jobj, j_mid, p1, p2);

Java数据类型与签名类型的对应关系

Java类型类型签名
booleanZ
byteB
intI
charC
shortS
longL
floatF
doubleD
voidV
数组[类型签名,比如int[][I
L全限定名;,比如String, 其签名为Ljava/lang/String;(注意后面有个分号)

方法签名规则:(所有参数类型)返回值类型

java方法签名
Point offset(int x, float y)(IF)Ljava/awt/Point;
public void test(int i, double index)(ID)V
public String test(String string, double index)(Ljava/lang/String;D)Ljava/lang/String;
String getName()()Ljava/lang/String;
Object[] getValue()()[java/lang/Object;
void test()()V

3. 构建Java对象

// 构建Point对象并返回
jclass point_clz = (*env)->FindClass(env, "java/awt/Point");
jmethod construct_id = (*env)->GetMehodID(env, "<init>", "(II)V");
jobject obj = (*env)->NewObject(env, point_clz, construct_id, 11, 12);
return obj;

Java操作native

1. 操作native对象

java层:构建java对象时初始化并获取native对象的引用

public class Mat{
	public final long nativeObj;   //c/c++对象的引用
	public Mat(){
		nativeObj = n_Mat();
	}

	public void doSomething(){
		n_doSomething(nativeObj);
	}
	// 初始化并获取native对象的引用
	private static native long n_Mat(); 
	// 调用native对象的方法,这里传nativeObj是为了在native层找到指定的native对象
	private static native void n_doSomething(long nativeObj);
}

native层:构建对象并返回内存地址(强转jlong)

JNIEXPORT jlong JNICALL Java_org_opencv_core_Mat_n_1Mat(){
	try{
		// 对象转地址
		// return (jlong)new Mat();   
		return reinterpret_cast<jlong>(new Mat());//这种写法更加正规
	}catch( const std::exception &e){
		throwJavaException(env, &e, "Mat_n_1Mat");
	}catch(...){
		throwJavaException();
	}
	return 0;
}

JNIEXPORT void JNICALL Java_org_opencv_core_Mat_n_1doSomething(JNIEnv* env, jclass jclz, jlong nativePtr){
	// 地址转对象
	Mat* mat = reinterpret_cast<Mat>(nativePtr);
	if(mat){
		// do something
	}
}

静态/动态注册Native方法

参考资料

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值