JNI native方法以及回调方法的实现

て烟熏妆下的殇ゞ 提交于 2019-12-01 04:26:47

JNI注册Native方法的步骤:

1、 Java层有五个Native方法:

DsmccDownloader类下面有五个native方法


public native voidloadcancel(int handle);

    private native boolean init();

    private native void setCurrentFrequency(int freq);

    private native int loadDir(XXXXXX);

    private native int loadfile(XXXXX);


2、 JNI层添加native数组:

数组形式={native方法名称,(参数类型列表)返回值类型,JNI层方法名称}


static JNINativeMethod gDSMCCNativeMethod[] = {

      {     "loadDir",

           "(JJIII[B[BLdsmccdownloadservice/DsmccDownloaderListener;)I",

           (void*)JNI_LoadDir,   },

      {     "loadfile",

           "(JJIII[BLjava/io/FileDescriptor;Ldsmccdownloadservice/DsmccDownloaderListener;)I",

           (void*)JNI_LoadFile,  },

      {     "loadcancel",

           "(I)V",

           (void*)JNI_LoadCancel, },

      {     "init",

           "()Z",

           (void*)JNI_InitDSMCC, },

      {     “setCurrentFrequency",

           "(I)V",

           (void*)JNI_SetCurrentFrequency,   },

};


3、 Native方法注册

当Java层调用到  System.loadLibrary("DSMCCDownload");将调用到 JNI中的JNICALL

  

static JavaVM* gvm = NULL;//全局变量用于保存JavaVM

        JNI_OnLoad(JavaVM*vm, void* reserved)完成Native方法的注册:

        JNIEnv* env = NULL;

        jclass clazz = NULL;

        if((*vm)->GetEnv(vm, (void**)&env ,JNI_VERSION_1_4) !=JNI_OK){//获取Java 版本
           return -1;
        } 

      //加载有Native方法的Java类

        clazz = (*env)->FindClass(env,"dsmccdownloadservice/DsmccDownloader");

      //注册Native方法

       if((*env)->RegisterNatives(env,clazz, gDSMCCNativeMethod, 5) < 0) {
          return -1;
       }

        gvm = vm;//保存Jvm

 JNI注册回调方法的步骤:

Ø  Java层回调方法:

public interface DsmccDownloaderListener {

        public void doNotifyStart(String path);

        public void doNotifyFinish(String path, int isSuccess);

        public void doNotifyDirInfo(String[] path);

}

1.JNI层为每个方法添加methodID.

static jmethodID DirTask_doNotifyStart_methodID = NULL;

static jmethodID DirTask_doNotifyFinish_methodID = NULL;

static jmethodID DirTask_doNotifyDirInfo_methodID = NULL; 

2. Java层调用init()方法,实现回调方法的注册:     

JNIEXPORTjboolean JNICALL JNI_InitDSMCC(JNIEnv* env, jobject thiz) {

     jbooleanret = JNI_TRUE;

     jclass class = NULL;

     class = (*env)->FindClass(env,"dsmccdownloadservice/DsmccClient$DirTask");

      DirTask_doNotifyStart_methodID= (*env)->GetMethodID(env, class,"doNotifyStart", "(Ljava/lang/String;)V");

    DirTask_doNotifyFinish_methodID= (*env)->GetMethodID(env, class,"doNotifyFinish", "(Ljava/lang/String;I)V"); 

    DirTask_doNotifyDirInfo_methodID= (*env)->GetMethodID(env, class,"doNotifyDirInfo", "([Ljava/lang/String;)V"); 

}

 3. 回调方法的调:以doNotifyFinish为例:   

static voidJNIDSMCCDownloadReq_doNotifyFinish(JNIDSMCCDownloadReq* req, int reason, char* path) {

    JNIEnv*env = NULL;
    jstringpath_obj = NULL;

    if((*gvm)->AttachCurrentThread(gvm, &env, NULL) < 0) {
       return ;
    }

    path_obj= (*env)->NewStringUTF(env, path);

    assert(path_obj!= NULL);

   (*env)->CallVoidMethod(env, req->listener,DirTask_doNotifyFinish_methodID, path_obj, reason); 
    (*gvm)->DetachCurrentThread(gvm); 
}

JNI注册自定义Java回调方法

1.Java原型方法:

class DsmccClient{

  class FDTaskextends Task {

       FDTask() {}

        public void writeDataByFD(FileDescriptor mFileDescriptor, String buf, String path) {             XXXXXXXXXXXXX;

        }

    }

2. JNI实现:

[1] 创建全局变量MethodID

 static jmethodID FDTask_writeDataByFD_methodID =NULL;

[2]构造Jclass为得到jobject,jmethodID

  class = (*env)->FindClass(env,"dsmccdownloadservice/DsmccClient$FDTask");

[3]构造jobject;再此之前需要先得到该类的构造函数:       

static jobject fdTaskObject =NULL;

        jmethodID init =(*env)->GetMethodID(env,class,"<init>","(Ldsmccdownloadservice/DsmccClient;)V");

        jobject obj = (*env)->NewObject(env,class, init,NULL);

        fdTaskObject = (*env)->NewGlobalRef(env,obj);

         在此处构造全局jobject是因为MethodID是全局变量,他们需要在同一个线程中创建,使用的时候才能匹配成功。

[4] 构造MethodID:

 FDTask_writeDataByFD_methodID =(*env)->GetMethodID(env,class,"writeDataByFD","(Ljava/io/FileDescriptor;Ljava/lang/String;Ljava/lang/String;)V");

[5]调用回调方法:    

 (*gvm)->AttachCurrentThread(gvm,&env,NULL) ;//激活Jvm

      buf_jstring= (*env)->NewStringUTF(env, buf);//参数转换 

      (*env)->CallVoidMethod(env,fdTaskObject,FDTask_writeDataByFD_methodID,req->mFileDescriptor,buf_jstring,path_jstring);

     (*gvm)->DetachCurrentThread(gvm);//关闭jvm
  其中req->mFileDescriptor:jobject类型

    buf_jstring,path_jstring:jstring类型,即C层的char*转换得到。

[6]释放资源:

        全局资源需要进行释放:  (*env)->DeleteGlobalRef(env,fdTaskObject);

Ø  注意事项:

  1. 若当前回调方法处于某个类下面,必须先得到该类的构造方法。
  2. MethodID和object必须处于同一个线程下创建
  3. 参数类型的转换,若为引用类型需加上L。
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!