一种比较好的JNI Java和C++相互传递参数和返回值的方法

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/yutao52shi/article/details/83721752

序言

以前在Android上做移动多媒体开发的时候,有很多需要在Java和C++相互传递参数的Case,以前因为时间卡的紧,一直没有去修复这一类的问题,因为能用,没有出什么问题,也就没有想什么优化方案。最近自己有一些闲暇时间折腾点小玩意儿,也需要从Java和C++之间相互传递参数。想到以前曾经有JNI方法20多个参数的情况,于是准备把这块好好优化一下。

思路

数组封装法

我想到的第一种思路是把各种类型的参数用数组做封装。比如这样一个方法

private native void init(int handle, int type, String res, String name, float faceIntensity, float eyeIntensity);

直接写成这样

private native void init(int[] intArgs, String[] stringObjs, float[] floatArgs);

这样确实能把参数的个数减少。但是无疑增加了处理数据的负担,native层必须写很多Java数组操作的相关方法去获取相关参数。而且参数的语意变得很不明确,维护起来很不方便。

对象封装法

对象封装法是我之前从WebRTC里面看到的,它里面有一个native_gen.py的脚本,可以把一个Java对象转化成一个Native的封装好的代码,可以很方便的去操作Java对象。这样的模式下我们可以直接把Java对象传递给native层,然后在Native层直接XXXObject.from即可获取一个对Java层对象进行操作的辅助类。大致代码演示如下:
Java:

public class Param {
    public int handle;
    public int type;
    public String res;
    public String name;
    public float faceIntensity;
    public float eyeIntensity;
}
public native void init(Param param);

C++:

	Java_XXX_XXX_init(JNIEnv *env, jobject obj, int handle, jobject jparams) {
	 	NativeParam* params = NativeParam::from(jparams);
	 	params->getHandle();
	 	params->getType();
	 	params->getRes();
	 	...
	 	delete params;
	}

这个方法能够比较好的解决参数过多的问题,但是比较麻烦的是我们需要去依赖脚本对应到Java的类生成操作的本地方法,这样无疑会导致库变大,并且文件一多也不方便维护。新人来还需要先学习脚本使用。

键值传递方法

此时笔者想到在Android层各个页面之间传递方法都是通过Intent里面的Bundle对象来进行传递。而Bundle本身就是在C++层进行的操作,会把值都存放在C++层,方便Binder进行进程间通讯。而Bundle是通过KeyValue的方式进行的多参数维护。虽然Native层的Bundle没有对我们开放,但是我们为何不自己写一个类似的东西来进行Java和C++之间进行参数传递呢?在Java层,我们可以直接封装一个对象传递的java方法给其他地方使用,然后自己封装到一个Bundle里面,再传递给C++里面直接使用。而如果是需要从C++层返回给Java层一些数据,我们也可以很方便的通过返回Bundle给上层来实现多个返回值的返回。使用方法如下:
Java:

public class Param {
    public int type;
    public String res;
    public String name;
    public float faceIntensity;
    public float eyeIntensity;
}
public void init(Param params) {
	NativeBundle bundle = new NativeBundle();
	bundle.putInt("type", params.type);
	bundle.putString("res", params.res);
	bundle.putString("name", params.name);
	bundle.putFloat("faceIntensity", faceIntensity);
	bundle.putFloat("eyeIntensity", eyeIntensity);
	nativeInit(handle, bundle);
}
private native void nativeInit(int handle, Param param);

C++:

	Java_XXX_XXX_init(JNIEnv *env, jobject obj, int handle, jobject jbundle) {
	 	    Bundle *bundle = getBundleFromJObject(jbundle);
			int64_t type = bundle->getInt("type");
			string* res = bundle->getString("res");
			string* name = bundle->getString("name");
			int faceIntensity = bundle->getInt("faceIntensity");
			int eyeIntensity = bundle->getInt("eyeIntensity");
			...
	}

通过键值对的映射,我们就可以很简单的从Java传任意多个参数到Native层,从而避免参数过多或者语义不清的问题。

具体实现请移步github: nBundle

扫描二维码关注公众号,回复: 4647573 查看本文章

猜你喜欢

转载自blog.csdn.net/yutao52shi/article/details/83721752
今日推荐