一、背景
今天介绍如何在层通过jni调用native方法,同时native层如何回调java方法。
二、java层实现
TestNative.java:
public class TestNative {
static
{
//加载native层源码编译得到的libtest_native.so
System.loadLibrary("test_native");
} public TestNative() {
}
//该方法用于native层回调java
public static void onNativeEvent(String native_event) {
//TODO: 通知java业务代码,native层回调了该方法
}
//该方法对应于jni层的native方法,用于java层调用native层方法
public native void testFunc(String arg1, int arg2);
}
三、native层实现
注:test_native.h和test_native.cpp编译得到libtest_native.so,该so在TestNative.java中被加载。
test_native.h:
1)定义连接java和native(c/c++)的上下文对象:
struct NativeContext : public RefBase
{
public:
NativeContext();
~NativeContext();
JavaVM *mVM = NULL;
jclass mObj = NULL; //指向app java层中包含native方法的java类,如com/test/natives/TestNative.java
sp<ALooper> mLooper; //可选项,当native业务是一个一直运行的服务时,通常需要使用ALooper
sp<AHandler> mAHandler; //可选项,当native业务是一个一直运行的服务时,通常需要使用AHandler
};
2)声明供java层需要调用的native方法:
jboolean native_test_func(JNIEnv *env, jobject thiz, jstring arg1, jint arg2);
3)声明java层与native方法的对应关系数组
static const JNINativeMethod native_test_methods[] = {
{"testFunc", "(Ljava/lang/String;I)Z", (void *)native_test_func},
};
test_native.cpp:
1)定义上下文对象,该对象是个静态对象,其生命周期与进程声明周期同步:
static sp<NativeContext> sNativeContext = NULL;
2)定义java层中包含native方法的类文件路径:
static const char *classPath_native_test = "com/test/natives/TestNative";
3)初始化上下文
//该方法是java层调用System.loadLibrary("test_native")时最先执行的方法,在该方法中完成native初始化工作
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
jint result = -1;
JNIEnv *env = NULL; //判断jni版本是否支持
if (vm->GetEnv((void **)&env, JNI_VERSION_1_4) != JNI_OK)
{
ALog(LOG_TAG, LOG_ERROR, "JNI_OnLoad, getEnv failed!");
return result;
} //初始化nativeContext对象
if (nativeContext == NULL)
{
sNativeContext = new NativeContext();
env->GetJavaVM(&sNativeContext->mVM);
} //looper和handler不是必须的,一般设计native回调java层的业务,通常而言native层都是类似服务一直运行的状态,所以会用到looper和handler
sp<ALooper> looper = new ALooper;
looper->start(false); sp<AHandler> handler = new AHandler(looper);
looper->registerHandler(handler); //查找com/test/natives/TestNative.java类,用于获取java层对象引用
jclass clazz_test_native = env->FindClass(classPath_native_sink); //获取java层对象引用,后续可以使用该对象引用,回调java层方法
jclass jobj = (jclass)env->NewGlobalRef(clazz_test_native); //保存上述对象到nativeContext对象中,供native层业务使用
sNativeContext->mVM = vm;
sNativeContext->mLooper = looper;
sNativeContext->mAHandler = handler;
sNativeContext->mObj = jobj;
//将java层的方法testFunc()与native声明的方法native_test_func()进行绑定,之后java层调用testFunc()就会立即调用native层的native_test_func()
if (env->RegisterNatives(clazz_test_native, native_test_methods, sizeof(native_test_methods) / sizeof((native_test_methods)[0])) < 0)
{
ALog(LOG_TAG, LOG_ERROR, "JNI_OnLoad, registerNatives error!");
return result;
} result = JNI_VERSION_1_4;
return result;
}
4)native层回调java层方法onNativeEvent(String native_event):
//注意:该方法是供native层其他业务调用的,方法内部实现用于示例如果回调java层方法
void onNativeEvent(const char* event) {
JNIEnv *env;
//标识是否临时绑定了env和当前线程
int attached = 0;
//检查支持的jni版本以及当前env是否已经绑定到了当前JVM线程
jint res = sNativeContext->mVM->GetEnv((void **)&env, JNI_VERSION_1_6);
if (JNI_EDETACHED == res) {
//支持当前的jni版本,但是env没有绑定到当前线程,进行主动绑定
res = sNativeContext->mVM->AttachCurrentThread(&env, NULL);
if (JNI_OK == res)
{
attached = 1;
}
else
{
// 绑定失败,返回
ALog(LOG_TAG, LOG_DEBUG, "Failed to attach, cancel,res %d", res);
return;
}
}
else if (JNI_OK == res) {
//env已绑定到当前线程,无需额外操作
ALog(LOG_TAG, LOG_DEBUG, "onNativeTest, has attached");
}
else {
//不支持指定的jni版本,返回
return;
} //构造返回给java层的参数
jstring jevent = charToJstring(env, event);
//获取java方法
jmethodID method = env->GetStaticMethodID(sNativeContext->mObj, "onNativeEvent", "(Ljava/lang/String;)");
//回调jave层方法,会直接到用到java层的方法onNativeEvent(String native_event)
env->CallStaticVoidMethod(sNativeContext->mObj, method, jevent);
//删除临时生成的回调参数缓存
env->DeleteLocalRef(jevent); if (attachedHere) {
//如果是前面临时绑定的线程,解除绑定
sNativeContext->mVM->DetachCurrentThread();
}
}