NDK学习笔记:java类封装c++类
Posted vonchenchen1
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了NDK学习笔记:java类封装c++类相关的知识,希望对你有一定的参考价值。
背景
在最近的开发中遇到了这样的一个场景,使用ffmpeg同时解码多路h264流,之前解码一路视频时,可以直接在jni文件中定义一个包装了ffmpeg解码功能的c++类的对象,如果继续采取这种写法必须在jni中定义多个对象,使得程序很不灵活。如果能把一个java类直接和c++类建立关系,则可以在多路解码时分别创建java对象,在使用完毕后由java进行gc,这样代码就灵活了许多。下面来研究一下java类与c++类之间如何建立关系。
实现
jni文件为我们提供了java层和c/c++层通信的接口,但是这些接口都是c函数的形式,也就是正常情况下要想调用一个c++类的方法,我们需要在jni中建立一个全局的c++对象。如果想要直接使用c++类,必须建立一个c++对象,如果建立全局对象肯定不符合我们的要求,这时可以提供一个jni函数,用来在堆中创建一个c++对象。
正常情况下我们是用一个指针指向堆中创建的c++对象,由于指针本身是一个地址值,所以可以将其当作一个数值。把这个地址值当作数字返回给java层,java就拿到了这个c++对象的句柄,便可以对这个对象进行操作。
java包装类实现
之所以称其为包装类,是因为其实这个java类是包装了c++类,其功能实现是由c++实现。下面我们看下这个java包装的实现。
package com.vonchenchen.jnitest;
import android.util.Log;
import static android.content.ContentValues.TAG;
/**
* Created by lidechen on 6/5/17.
*/
public class JavaClassDemoWrapper
private static final String TAG = "JavaClassDemoWrapper";
static
System.loadLibrary("demo");
private int mCppObjWapper;
public JavaClassDemoWrapper()
mCppObjWapper = getCppObjWrapper();
public void setTag(String tag)
setTag(tag, mCppObjWapper);
public String getTag()
return getTag(mCppObjWapper);
@Override
protected void finalize() throws Throwable
try
Log.e(TAG, "finalize()");
if (mCppObjWapper != 0)
release(mCppObjWapper);
mCppObjWapper = 0;
finally
super.finalize();
//获取cpp对象指针
public native int getCppObjWrapper();
//调用cpp对象中对应的方法
public native void setTag(String tag, int cppObjWapper);
public native String getTag(int cppObjWapper);
//释放cpp对象
public native void release(int cppObjWapper);
这个类主要包含以下几个功能
1.getCppObjWrapper() 这个方法是一个jni接口,用来返回jni中创建的c++对象的指针,并由java类记录。
2.jni 中具体实现功能的方法,这些方法都需要传入c++对象的句柄,用来标识执行哪个对象的方法。
3.release()方法用来释放c++中的资源,该类实现finalize()方法,也就是在虚拟机gc的时候单独将这个类放入一个列表,分别执行这个方法,用来释放资源,这时我们就把c++中相关的内容一起释放。
jni接口实现
直接上代码
#include <jni.h>
/* Header for class com_vonchenchen_jnitest_JavaClassDemoWrapper */
#ifndef _Included_com_vonchenchen_jnitest_JavaClassDemoWrapper
#define _Included_com_vonchenchen_jnitest_JavaClassDemoWrapper
#include "CppClassDemo.h"
#include "log.h"
#ifdef __cplusplus
extern "C"
#endif
//对象包装类 此处包含被包装的功能类CppClassDemo对象指针和ctx指针
// ctx用于保存一些中间变量
class CppClassWrapper
public:
CppClassDemo *obj;
void *ctx;
;
/*
* Class: com_vonchenchen_jnitest_JavaClassDemoWrapper
* Method: getCppObj
* Signature: ()I
*
* JNIEXPORT jint JNICALL Java_com_vonchenchen_jnitest_JavaClassDemoWrapper_getCppObjWrapper
* 修改返回值类型为CppClassWrapper *
*/
JNIEXPORT CppClassWrapper * JNICALL Java_com_vonchenchen_jnitest_JavaClassDemoWrapper_getCppObjWrapper
(JNIEnv *env, jobject obj)
CppClassWrapper *wrapper = new CppClassWrapper();
wrapper->obj = new CppClassDemo();
return wrapper;
/*
* Class: com_vonchenchen_jnitest_JavaClassDemoWrapper
* Method: setTag
* Signature: (Ljava/lang/String;I)V
*/
JNIEXPORT void JNICALL Java_com_vonchenchen_jnitest_JavaClassDemoWrapper_setTag
(JNIEnv *env, jobject obj, jstring tag, CppClassWrapper *wrapper)
char *cTag = env->GetStringUTFChars(tag, JNI_FALSE);
wrapper->obj->setTag(cTag);
env->ReleaseStringUTFChars(tag, cTag);
env->DeleteLocalRef(tag);
/*
* Class: com_vonchenchen_jnitest_JavaClassDemoWrapper
* Method: getTag
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_vonchenchen_jnitest_JavaClassDemoWrapper_getTag
(JNIEnv *env, jobject obj, CppClassWrapper *wrapper)
char *tag = wrapper->obj->getTag();
return env->NewStringUTF(tag);
/*
* Class: com_vonchenchen_jnitest_JavaClassDemoWrapper
* Method: release
* Signature: (I)V
*/
JNIEXPORT void JNICALL Java_com_vonchenchen_jnitest_JavaClassDemoWrapper_release
(JNIEnv *env, jobject obj, CppClassWrapper *wrapper)
if(wrapper != NULL)
wrapper->obj->release();
delete wrapper;
wrapper = NULL;
#ifdef __cplusplus
#endif
#endif
这里注意我们并没有直接创建需要调用的c++对象,而是把这个对象进行了一层包装,因为在实际开发中发现有许多中间变量需要存储,但是我们不能将这些变量定义为全局,否则多个java对象就会共用这些全局变量。这里使用CppClassWrapper封装,增加了一个void *ctx指针,我们可以根据需求定义其他存储结构,用ctx指针指向这些结构。
c++对象
#include "CppClassDemo.h"
char* CppClassDemo::getTag()
return mTag;
void CppClassDemo::setTag(char *tag)
if(mTag != NULL)
free(mTag);
mTag = NULL;
int len = strlen(tag);
mTag = (char *)malloc(sizeof(char) * len + 1);
strcpy(mTag, tag);
void CppClassDemo::release()
if(mTag != NULL)
free(mTag);
mTag = NULL;
简单实现了一个字符串存储的功能。
调用示例
JavaClassDemoWrapper wrapper1 = new JavaClassDemoWrapper();
JavaClassDemoWrapper wrapper2 = new JavaClassDemoWrapper();
JavaClassDemoWrapper wrapper3 = new JavaClassDemoWrapper();
wrapper1.setTag("I am wrapper1");
wrapper2.setTag("I am wrapper2");
wrapper3.setTag("I am wrapper3");
Log.i(TAG, "wrapper1 "+wrapper1.getTag());
Log.i(TAG, "wrapper2 "+wrapper2.getTag());
Log.i(TAG, "wrapper3 "+wrapper3.getTag());
可以发现此时我们定义的三个java对象中存储了不同的字符串。我们可以在release中打印标示信息,手动触发gc,可以发现在垃圾回收的时候release方法会被调用。
代码链接:http://download.csdn.net/detail/lidec/9861699
勘误
代码在一部分机器上运作直接崩溃,经过排查发现在32位的arm机器上可以正常运作,但是换成x86或者64位arm都会崩溃。这里打印地址我们会发现64位机器返回c++对象的地址数值比较大,会超出32位,也就是java的int类型不可以用来保存c++对象的地址。此处必须使用long类型接收c++对象的地址。
修改
在JavaClassDemoWrapper类中,需要做如下修改,创建对象后直接返回一个long类型,同时填入对象参数也是long类型
//用long类型保存c++类对象的地址
private long mCppObjWapper;
......
//获取cpp对象指针
public native long getCppObjWrapper();
//调用cpp对象中对应的方法
public native void setTag(String tag, long cppObjWapper);
public native String getTag(long cppObjWapper);
//释放cpp对象
public native void release(long cppObjWapper);
jni文件也需要将对应的int型改为long型
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_vonchenchen_jnitest_JavaClassDemoWrapper */
#ifndef _Included_com_vonchenchen_jnitest_JavaClassDemoWrapper
#define _Included_com_vonchenchen_jnitest_JavaClassDemoWrapper
#include "CppClassDemo.h"
#include "log.h"
#ifdef __cplusplus
extern "C"
#endif
//对象包装类 此处包含被包装的功能类CppClassDemo对象指针和ctx指针
// ctx用于保存一些中间变量
class CppClassWrapper
public:
CppClassDemo *obj;
void *ctx;
;
/*
* Class: com_vonchenchen_jnitest_JavaClassDemoWrapper
* Method: getCppObj
* Signature: ()I
*
* JNIEXPORT jint JNICALL Java_com_vonchenchen_jnitest_JavaClassDemoWrapper_getCppObjWrapper
* 修改返回值类型为CppClassWrapper *
*/
JNIEXPORT jlong JNICALL Java_com_vonchenchen_jnitest_JavaClassDemoWrapper_getCppObjWrapper
(JNIEnv *env, jobject obj)
CppClassWrapper *wrapper = new CppClassWrapper();
wrapper->obj = new CppClassDemo();
LOGE("getCppObjWrapper addr %p", wrapper);
return wrapper;
/*
* Class: com_vonchenchen_jnitest_JavaClassDemoWrapper
* Method: setTag
* Signature: (Ljava/lang/String;I)V
*/
JNIEXPORT void JNICALL Java_com_vonchenchen_jnitest_JavaClassDemoWrapper_setTag
//(JNIEnv *env, jobject obj, jstring tag, CppClassWrapper *wrapper)
(JNIEnv *env, jobject obj, jstring tag, long handle)
LOGE("setTag addr %p", handle);
//int real_handle = (int)handle;
long real_handle = handle;
CppClassWrapper *wrapper = real_handle;
char *cTag = env->GetStringUTFChars(tag, JNI_FALSE);
wrapper->obj->setTag(cTag);
env->ReleaseStringUTFChars(tag, cTag);
env->DeleteLocalRef(tag);
/*
* Class: com_vonchenchen_jnitest_JavaClassDemoWrapper
* Method: getTag
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_vonchenchen_jnitest_JavaClassDemoWrapper_getTag
//(JNIEnv *env, jobject obj, CppClassWrapper *wrapper)
(JNIEnv *env, jobject obj, long handle)
//int real_handle = (int)handle;
long real_handle = handle;
CppClassWrapper *wrapper = real_handle;
char *tag = wrapper->obj->getTag();
return env->NewStringUTF(tag);
/*
* Class: com_vonchenchen_jnitest_JavaClassDemoWrapper
* Method: release
* Signature: (I)V
*/
JNIEXPORT void JNICALL Java_com_vonchenchen_jnitest_JavaClassDemoWrapper_release
//(JNIEnv *env, jobject obj, CppClassWrapper *wrapper)
(JNIEnv *env, jobject obj, long handle)
//int real_handle = (int)handle;
long real_handle = handle;
CppClassWrapper *wrapper = real_handle;
if(wrapper != NULL)
wrapper->obj->release();
delete wrapper;
wrapper = NULL;
#ifdef __cplusplus
#endif
#endif
以上是关于NDK学习笔记:java类封装c++类的主要内容,如果未能解决你的问题,请参考以下文章