一、背景

今天介绍如何在层通过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();
     }
 }