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
  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值