JNI注册Native方法的步骤:
1、 Java层有五个Native方法:
DsmccDownloader类下面有五个native方法
public native voidloadcancel(int handle);
private native booleaninit();
private native voidsetCurrentFrequency(int freq);
private native int loadDir(XXXXXX);
private native intloadfile(XXXXX);
2、 JNI层添加native数组:
数组形式={native方法名称,(参数类型列表)返回值类型,JNI层方法名称}
static JNINativeMethod gDSMCCNativeMethod[] = {
{ "loadDir",
"(JJIII[B[BLipanel/dsmccdownloadservice/DsmccDownloaderListener;)I",
(void*)JNI_LoadDir, },
{ "loadfile",
"(JJIII[BLjava/io/FileDescriptor;Lipanel/dsmccdownloadservice/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,"ipanel/dsmccdownloadservice/DsmccDownloader");
if(clazz == NULL) {
return -1;
}
//注册Native方法
if((*env)->RegisterNatives(env,clazz, gDSMCCNativeMethod, 5) < 0) {
return -1;
}
gvm = vm;//保存Jvm
JNI注册回调方法的步骤:
Ø Java层回调方法:
public interface DsmccDownloaderListener {
publicvoid doNotifyStart(String path);
public void doNotifyFinish(String path, int isSuccess);
publicvoid doNotifyDirInfo(String[]path);
}
Ø JNI层为每个方法添加methodID.
static jmethodID DirTask_doNotifyStart_methodID = NULL;
static jmethodID DirTask_doNotifyFinish_methodID = NULL;
static jmethodID DirTask_doNotifyDirInfo_methodID = NULL;
Ø Java层调用init()方法,实现回调方法的注册:
JNIEXPORTjboolean JNICALL JNI_InitDSMCC(JNIEnv* env, jobject thiz) {
jbooleanret = JNI_TRUE;
jclass class = NULL;
class = (*env)->FindClass(env,"ipanel/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");
}
Ø 回调方法的调:以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回调方法
Ø Java原型方法:
class DsmccClient{
class FDTaskextends Task {
FDTask() {}
publicvoidwriteDataByFD(FileDescriptor mFileDescriptor, String buf,
String path) { XXXXXXXXXXXXX;}
}
}
Ø JNI实现:
1. 创建全局变量MethodID
static jmethodID FDTask_writeDataByFD_methodID =NULL;
2. 构造Jclass为得到jobject,jmethodID
class = (*env)->FindClass(env,"ipanel/dsmccdownloadservice/DsmccClient$FDTask");
3. 构造jobject;再此之前需要先得到该类的构造函数:
static jobjectfdTaskObject =NULL;
jmethodID init =(*env)->GetMethodID(env,class,"<init>","(Lipanel/dsmccdownloadservice/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);
Ø 注意事项:
- 若当前回调方法处于某个类下面,必须先得到该类的构造方法。
- MethodID和object必须处于同一个线程下创建
- 参数类型的转换,若为引用类型需加上L。