Android JNI Java/C++互相调用

一,Java 调用 C

   1, 首先我们创建一个文件名字叫做,JNI。其实你不创建也行。看自己

public class JNI {
    //加载本地C语言文件库。库名字为你写的C语言文件名
    static {
        System.loadLibrary("Hello");
    }
    
    
    //todo: java 调用 C =======
    public native String stringFromJNI();
//相加
    public native  int numberTest(int a,int b);

//字符串拼接 java 和 C
    public native String strOrStr(String str);

 // 在C中数组中的每一个元素+10 然后返回到 java;
    public native int [] getArray(int array[]);

}

 2, 再次来到Cpp文件里

#include <jni.h>
#include <string>
#include<android/log.h>
#define  LOG_TAG    "nativeprint"
#define  LOGI(...)  __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGD(...)  __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGE(...)  __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__

// java String 转成C char
char *jstringTostring(JNIEnv *env, jstring jstr);

#pragma clang diagnostic push
#pragma ide diagnostic ignored "err_typecheck_member_reference_arrow"
extern "C" JNIEXPORT jstring JNICALL
Java_com_cwj_ndkc_JNI_stringFromJNI(JNIEnv *env, jobject) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}


extern "C"
JNIEXPORT jint JNICALL
Java_com_cwj_ndkc_JNI_numberTest(JNIEnv *env, jobject thiz, jint a, jint b) {
    // TODO: implement numberTest()
    int result = a + b;
    return result;
}


extern "C"
JNIEXPORT jstring JNICALL
Java_com_cwj_ndkc_JNI_strOrStr(JNIEnv *env, jobject thiz, jstring str) {
    // TODO: implement strOrStr()
    char *javas = jstringTostring(env, str);
    char *cc = "我是C语言中的char*";
    strcat(javas, cc);
//    return (*env)->NewStringUTF(env,javas);
    return env->NewStringUTF((char *) javas);
}

//char* 转成String
jstring stoJstring(JNIEnv *env, const char *pat) {
    jclass strClass = env->FindClass("java/lang/String");
    jmethodID ctorID = env->GetMethodID(strClass, "<init>", "([BLjava/lang/String;)V");
    jbyteArray bytes = env->NewByteArray(strlen(pat));
    env->SetByteArrayRegion(bytes, 0, strlen(pat), (jbyte *) pat);
    jstring encoding = env->NewStringUTF("utf-8");
    return (jstring) env->NewObject(strClass, ctorID, bytes, encoding);
}
// java String 转成C char
char *jstringTostring(JNIEnv *env, jstring jstr) {
    char *rtn = NULL;
    jclass clsstring = env->FindClass("java/lang/String");
    jstring strencode = env->NewStringUTF("utf-8");
    jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B");
    jbyteArray barr = (jbyteArray) env->CallObjectMethod(jstr, mid, strencode);
    jsize alen = env->GetArrayLength(barr);
    jbyte *ba = env->GetByteArrayElements(barr, JNI_FALSE);
    if (alen > 0) {
        rtn = (char *) malloc(alen + 1);
        memcpy(rtn, ba, alen);
        rtn[alen] = 0;
    }
    env->ReleaseByteArrayElements(barr, ba, 0);
    return rtn;
}

extern "C"
JNIEXPORT jintArray JNICALL
Java_com_cwj_ndkc_JNI_getArray(JNIEnv *env, jobject thiz, jintArray array) {
    // TODO: implement getArray()
    //1, 得到数组的长度
    int lenght = env->GetArrayLength(array);
    //2,得到数组的元素
    jint *intArray = env->GetIntArrayElements(array,0);
    //3,遍历数组顺便加10
    int i;
    for (i = 0; i < lenght; ++i) {
        *intArray+=10;
    }
    return array;
}

注意: 观察,Java Native方法,再看C++里面的实现。会发现,

C++的方法名字:是Java+JavaNative接口包名+接口名的方式生成的,对,这个不用自己写。直接根据AS提示,自动创建。

3,在Activity中调用。先看看目录

     case R.id.btn_java:
                //java调用C
                jni.strOrStr("参数");
                

二,C++调用Java

1,首先要有一个java 方法吧,然后C调用java了 ,最后,还是要回到Activity中去调用。 这里只是把java方法和native接口写在了一个类中。


    //todo: C 调用java=======================================================
    public int  addJava(int x,int y){
        Log.e(TAG, "addJava() x=" + x + " y=" + y);
        return x+y;
    }
    public native void callAdd();

    
    //todo:C  c调用静态java方法
     public static void javaString(String jstr){
         Log.e(TAG, "javaString()  "  +jstr);

     }
     public native void javaStringCall();

2,Cpp代码

//=============================================todo:===================== C调用java
extern "C"
JNIEXPORT void JNICALL
Java_com_cwj_ndkc_JNI_callAdd(JNIEnv *env, jobject thiz) {
    // TODO: implement callAdd()
    //要用到反射
    //1,得到字节码
    jclass  jclass1=env->FindClass("com/cwj/ndkc/JNI");
    //2,得到方法
    jmethodID jmethodId=env->GetMethodID(jclass1,"addJava","(II)I");
    //3,实例化该类
    jobject  jobject=env->AllocObject(jclass1);
    //4,调用方法,得到结果
   jint jint1= env->CallIntMethod(jobject,jmethodId,99,1);
}
//调用静态java方法
extern "C"
JNIEXPORT void JNICALL
Java_com_cwj_ndkc_JNI_javaStringCall(JNIEnv *env, jobject thiz) {
    // TODO: implement javaStringCall()
        //1,得到字节码
            jclass jclass1=env->FindClass("com/cwj/ndkc/JNI");
        //2.得到方法 字节码,方法名,方法签名
            jmethodID jmethodId=env->GetStaticMethodID(jclass1,"javaString","(Ljava/lang/String;)V");
        //3,调用方法
            jstring jst=env->NewStringUTF("这个是java静态方法参数");
            env -> CallStaticVoidMethod(jclass1,jmethodId,jst);
            printf("日志:=======");
            __android_log_print(ANDROID_LOG_INFO,"日志",  "aa");
}

3,最后调用方式是一样的。

 case R.id.btn_c:
                //C调用java
                jni.callAdd();//C
                jni.javaStringCall();//C

注意:学这个之前先把C++ 看一遍。其实语法差不多,最大的区别是,C++的指针,和结构体,这俩要多看几遍

三,C调用Activity中的方法,更新UI

    Activity ==================

    public  native void showToastCall();

    public void  showToast(){
        Toast.makeText(this, "C++调用我了", Toast.LENGTH_SHORT).show();
    }

    Cpp==================

    extern "C"
JNIEXPORT void JNICALL
/**
 * 
 * @param env  
 * @param thiz 这个代表的Activity  ,不需要实例化
 */
Java_com_cwj_ndkc_MainActivity_showToastCall(JNIEnv *env, jobject thiz) {
    // TODO: implement showToastCall()
    //要用到反射
    //1,得到字节码
    jclass  jclass1=env->FindClass("com/cwj/ndkc/MainActivity");
    //2,得到方法
    jmethodID jmethodId=env->GetMethodID(jclass1,"showToast","()V");
    //3,实例化该类
//    jobject  jobject=env->AllocObject(jclass1);
    //4,调用方法,得到结果
    env->CallVoidMethod(thiz,jmethodId);
}


Activity调用=================================
                 MainActivity.this.showToastCall();

    

 

 

 

四,日志的配置

这个放到你的Cpp文件中顶部。
#include<android/log.h>
#define  LOG_TAG    "nativeprint"
#define  LOGI(...)  __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGD(...)  __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGE(...)  __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__


      build.gradle

  defaultConfig {
        applicationId "com.cwj.ndkc"
        minSdkVersion 24
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        externalNativeBuild {
            cmake {
                cppFlags " "
            }
            ndk {
                // Specifies the ABI configurations of your native
                // libraries Gradle should build and package with your APK.
//                abiFilters 'x86', 'x86_64', 'armeabi', 'armeabi-v7a',
//                               'arm64-v8a'

                ldLibs "log"
            }

        }

使用:      __android_log_print(ANDROID_LOG_INFO,"日志",  "aa");

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章