JNI c++对象与java对象互关联

      android中如果需要调用c++代码需要写jni将java操作转接到c++代码中.但是大部分的文章都只是通过讲解通过  extern "C" 代码来访问具体功能.

     在这里我要讲一种方便的方法将java对象和c++对象互相绑定起来.

     第一步:

    JNIObject.java:

public class JNIObject {
    protected long mObj;
}
MediaController.java

public class MediaController extends JNIObject {
    public MediaController()
    {
        construct();
    }

    @Override
    protected void finalize() throws Throwable {
        destruct();
        super.finalize();
    }
    
    @Keep
    private void logMessage(String msg){
        Log.d("AAA", "msg:"+msg);
    }


    static {
        System.loadLibrary("media_controller-lib");
    }

    private native void construct();
    private native void destruct();
    public native int ntopen(String file);
    public native void ntclose();
    public native int ntplay();
    public native void ntpause();
    public native void ntseek(int sec);
    public native String nttest(int msg);
}

jni代码:

static JavaVM* g_VM = NULL;

class JNIMediaControl
        :public MediaControl
{
public:
    JNIMediaControl()
    :javaVM(NULL),env_(NULL),thiz(NULL)
    {

    }

    void attachParent(JavaVM * vm,JNIEnv *env,jobject object)
    {
        javaVM =vm;
        thiz = env->NewGlobalRef(object);
    }

    void detachParent(JNIEnv *env)
    {
        env->DeleteGlobalRef(thiz);
        thiz = NULL;
    }
private:
    void AttachThread()
    {
        if(javaVM == NULL) return;
        if(thiz == NULL) return ;

        int ret = javaVM->AttachCurrentThread(&env_,NULL);
        if(ret != 0)
        {
        }
    }

    void DetachThread()
    {
        if(javaVM == NULL) return;
        javaVM->DetachCurrentThread();
        env_ = NULL;
    }

    virtual void Run()
    {
        AttachThread();
        MediaControl::Run();
        DetachThread();
    }

    void SendPacket(int64_t timestamp,AVFrame *frame)
    {
        JNIEnv *env = env_;
        if(env == NULL) return;

        char buffer[64];
        sprintf(buffer,"%lld",timestamp);
        jstring msg_ = env->NewStringUTF(buffer);
        jclass clazz = env->GetObjectClass(thiz);
        jmethodID  jid = env->GetMethodID(clazz,"logMessage","(Ljava/lang/String;)V");
        env->CallVoidMethod(thiz,jid,msg_);
        env->DeleteLocalRef(msg_);
    }

private:
    JavaVM* javaVM;
    JNIEnv *env_;
    jobject thiz;
};
#define  CONSTRUCT(T) { T *t = new T(); \
    jclass clazz = (jclass)(*env).GetObjectClass(thiz); \
    jfieldID fid = (jfieldID)(*env).GetFieldID(clazz, "mObj", "J"); \
    jlong jstr = (jlong) (*env).GetLongField(thiz, fid);  \
    (*env).SetLongField(thiz, fid, (jlong)t);}

#define OBJECT(T,name) jclass clazz = (jclass)env->GetObjectClass(thiz); \
     jfieldID fid = env->GetFieldID(clazz, "mObj","J");  \
     T *name = (T *)env->GetLongField(thiz, fid);

#define DESTRUCT(T)  {jclass clazz = (jclass)env->GetObjectClass(thiz); \
     jfieldID fid = env->GetFieldID(clazz, "mObj","J");  \
     T *object = (T *)env->GetLongField(thiz, fid); \
     if(object != NULL) delete object; \
     (*env).SetLongField(thiz, fid, (jlong)0);}

extern "C"
JNIEXPORT void JNICALL
Java_com_example_jfyang_mediacontrollerdemo_MediaController_construct(JNIEnv* env, jobject thiz){
    CONSTRUCT(JNIMediaControl);
    OBJECT(JNIMediaControl,control);
    if(control == NULL) return ;
    control->attachParent(g_VM,env,thiz);
}

extern "C"
JNIEXPORT void JNICALL
Java_com_example_jfyang_mediacontrollerdemo_MediaController_destruct(JNIEnv *env,
                                                                     jobject thiz) {
    {
        OBJECT(JNIMediaControl, control);
        if (control == NULL) return;
        control->detachParent(env);
    }
    DESTRUCT(JNIMediaControl);
}

extern "C"
JNIEXPORT void JNICALL
Java_com_example_jfyang_mediacontrollerdemo_MediaController_ntclose(JNIEnv *env,
                                                                      jobject thiz) {
    OBJECT(JNIMediaControl,control);
    if(control == NULL) return ;
    control->Close();
}


JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)
{
    JNIEnv* env = NULL;
    jint result = -1;

    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
        return -1;
    }
    assert(env != NULL);
    g_VM = vm;
    return JNI_VERSION_1_4;
}

在android中需要被底层使用的对象均继承自JNIObject.

JNI代码中 通过 

 
  
CONSTRUCT构造一个自定义对象与JNIObject关联起来.
OBJECT获取与JNIObject关联起来的对象
DESTRUCT释放与JNIObject关联起来的对象
具体的功能还是通过jni的c方法调用并转至c++对象中.

在jni底层线程中有时需要回调数据至android上层中,这种情况我们可以通过回调上层方法完成.

1.先将上层父对象建立全局引用

thiz = env->NewGlobalRef(object);

注意:所有的在底层线程中操作的上层对象均需要建立GlobalRef才能使用,否则会崩溃.

2.通过JNI_Onload获取本模块加载时传入的虚拟机并全局保存

3.通过虚拟机 在线程中创建一个线程相关JNIEnv.

4.通过线程相关JNIEnv 调用 GlobalRef对象的方法.


你可能感兴趣的:(ffmpeg)