18 -- jni C++与Java互相调用

一:基本概念: JNI是Java Native Interface的缩写,通过使用Java本地接口书写程序,可以确保代码在不同的平台上方便移植。 NDK提供了一系列的工具,帮助开发者快速开发C(或C++)的动态库,并能自动将so和java应用一起打包成apk。

二:  Java调用C++

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

1.静态注册Native函数


public native void changeName();

public static native void changeAge();
extern "C"  //必须采用C的编译方式,无论是C还是C++ 最终是调用到 C的JNINativeInterface,所以必须采用C的方式 extern "C"

JNIEXPORT //标记该方法可以被外部调用 
void JNICALL
//Java_包名_类名_方法名,
//JNIEnv *env :JNI的桥梁
//  jobject jobj  谁调用,就是谁的实例  MainActivity this
Java_com_derry_as_1jni_1project_MainActivity_changeName(JNIEnv *env, jobject thiz) {

}

extern "C"
JNIEXPORT void JNICALL
//  jclass clazz 谁调用,就是谁的class MainActivity.class
Java_com_derry_as_1jni_1project_MainActivity_changeAge(JNIEnv *env, jclass jcls) {

}

三:C++调用Java


// NDK工具链里面的 log 库 引入过来
#include <android/log.h>

#define TAG "Derry"

#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)


extern "C"
JNIEXPORT void JNICALL
Java_com_derry_as_1jni_1project_MainActivity_changeName(JNIEnv *env, jobject thiz) {
   // 获取class
   jclass j_cls = env->GetObjectClass(thiz);

   // 获取属性  L对象类型 都需要L
   // jfieldID GetFieldID(MainActivity.class, 属性名, 属性的签名)
   jfieldID j_fid = env->GetFieldID(j_cls, "name", "Ljava/lang/String;");

   // 转换工作
   jstring j_str = static_cast<jstring>(env->GetObjectField(thiz ,j_fid));

   // 打印字符串  目标
   char * c_str = const_cast<char *>(env->GetStringUTFChars(j_str, NULL));
    LOGD("native : %s\n", c_str);
    LOGE("native : %s\n", c_str);
    LOGI("native : %s\n", c_str);

    // 修改成 Beyond
    jstring jName = env->NewStringUTF("Beyond");
    env->SetObjectField(thiz, j_fid, jName);

   // printf()  C
   // cout << << endl; // C++
}


extern "C"
JNIEXPORT void JNICALL
Java_com_derry_as_1jni_1project_MainActivity_callAddMethod(JNIEnv *env, jobject job) {
    // 自己得到 MainActivity.class
    jclass  mainActivityClass = env->GetObjectClass(job);

    // GetMethodID(MainActivity.class, 方法名, 方法的签名)
   jmethodID j_mid = env->GetMethodID(mainActivityClass, "add", "(II)I");

   // 调用 Java的方法
   jint sum = env->CallIntMethod(job, j_mid, 3, 3);
   LOGE("sum result:%d", sum);

}

/*
   签名规则 大写

   javap -s -p MainActivity     必须是.class

    Java的boolean  --- Z  注意点
    Java的byte  --- B
    Java的char  --- C
    Java的short  --- S
    Java的int  --- I
    Java的long  --- J     注意点
    Java的float  --- F
    Java的double  --- D
    Java的void  --- V
    Java的引用类型  --- Lxxx/xxx/xx/类名;
    Java的String  --- Ljava/lang/String;
    Java的array  int[]  --- [I         double[][][][]  --- [[[D
    int add(char c1, char c2) ---- (CC)I
    void a()     ===  ()V

    javap -s -p xxx.class    -s 输出xxxx.class的所有属性和方法的签名,   -p 忽略私有公开的所有属性方法全部输出
 */