第一步首先配置Android studio的NDK开发环境,首先在Android studio中下载NDK包
第二步在local.properties文件中,配置对应的NDK路径
第三歩,在app目录下的build.gradle文件中的,android{}闭包中,指定CMakeLists.txt路径
// 在android节点下 // 指定CMakeLists.txt路径 externalNativeBuild { cmake { // 在该文件种设置所要编写的c源码位置,以及编译后so文件的名字 path 'CMakeLists.txt' } }
在defaultConfig闭包下配置
// 增加cmake控制属性 externalNativeBuild { cmake { // 指定编译架构 abiFilters 'arm64-v8a', 'armeabi-v7a', 'x86', 'x86_64' } }
在app目录下,指定CmakeLists.txt文件,根据注释进行相应添加
# CMakeLists.txt # For more information about using CMake with Android Studio, read the # documentation: https://d.android.com/studio/projects/add-native-code.html # Sets the minimum version of CMake required to build the native library. #CMakeLists.txt cmake_minimum_required(VERSION 3.4.1) # Creates and names a library, sets it as either STATIC # or SHARED, and provides the relative paths to its source code. add_library( # 设置so文件名称. Hello # 设置这个so文件为共享. SHARED # Provides a relative path to your source file(s). src/main/jni/Hello.c) add_library( # 设置so文件名称. Test # 设置这个so文件为共享. SHARED # Provides a relative path to your source file(s). src/main/jni/Test.c) add_library( # 设置so文件名称. CCallJava # 设置这个so文件为共享. SHARED # Provides a relative path to your source file(s). src/main/jni/CCallJava.c) # 添加 log 库的链接 target_link_libraries(CCallJava log) target_link_libraries(Test log) # Searches for a specified prebuilt library and stores the path as a # variable. Because CMake includes system libraries in the search path by # default, you only need to specify the name of the public NDK library # you want to add. CMake verifies that the library exists before # completing its build. find_library( # Sets the name of the path variable. log-lib # Specifies the name of the NDK library that # you want CMake to locate. log ) # Specifies libraries CMake should link to your target library. You # can link multiple libraries, such as libraries you define in this # build script, prebuilt third-party libraries, or system libraries. target_link_libraries( # Specifies the target library. # 制定目标库. Hello # Links the target library to the log library # included in the NDK. ${log-lib} )
第四步,首先进行Java代码C代码的操作
1.JNI2.java代码如下
/** * @Author: ly * @Date: 2023/8/6 * @Description: java调C代码 */ public class JNI2 { static { System.loadLibrary("Test"); //加载动态链接库 } /** * 让C代码做加法运算,把结果返回 * 场景:大量运算,编解码之类的,需要性能很高的情况下,可以用C代码 * * @param x * @param y * @return */ public native int add(int x, int y); /** * 从java传入字符串,C代码进行拼接 * * @param s I am from java * @return I am from java and I am from c */ public native String sayHello(String s); /** * 让C代码给每个元素都加上10 * * @param intArray * @return */ public native int[] increaseArrayEles(int[] intArray); /** * 应用:检查密码是否正确,如果正确返回200,否则返回400 * * @param pwd * @return */ public native int checkPwd(String pwd); }
2.根据命令行javah +JNI2的全类名,生成Test.c对应的C代码头文件com_example_jniproject_JNI2.h
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class com_example_jniproject_JNI2 */ #ifndef _Included_com_example_jniproject_JNI2 #define _Included_com_example_jniproject_JNI2 #ifdef __cplusplus extern "C" { #endif /* * Class: com_example_jniproject_JNI2 * Method: add * Signature: (II)I */ JNIEXPORT jint JNICALL Java_com_example_jniproject_JNI2_add (JNIEnv *, jobject, jint, jint); /* * Class: com_example_jniproject_JNI2 * Method: sayHello * Signature: (Ljava/lang/String;)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_com_example_jniproject_JNI2_sayHello (JNIEnv *, jobject, jstring); /* * Class: com_example_jniproject_JNI2 * Method: increaseArrayEles * Signature: ([I)[I */ JNIEXPORT jintArray JNICALL Java_com_example_jniproject_JNI2_increaseArrayEles (JNIEnv *, jobject, jintArray); /* * Class: com_example_jniproject_JNI2 * Method: checkPwd * Signature: (Ljava/lang/String;)I */ JNIEXPORT jint JNICALL Java_com_example_jniproject_JNI2_checkPwd (JNIEnv *, jobject, jstring); #ifdef __cplusplus } #endif #endif
3.根据jni协议,去写对应的c代码,创建Test.c文件,代码如下
// // Created by DELL on 2023/8/6. // #include "com_example_jniproject_JNI2.h" #include "string.h" #include <stdlib.h> #include <android/log.h> #define TAG "luyu" #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,TAG ,__VA_ARGS__) // 定义LOGD类型 #define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG ,__VA_ARGS__) // 定义LOGI类型 #define LOGW(...) __android_log_print(ANDROID_LOG_WARN,TAG ,__VA_ARGS__) // 定义LOGW类型 #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG ,__VA_ARGS__) // 定义LOGE类型 #define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,TAG ,__VA_ARGS__) // 定义LOGF类型 JNIEXPORT char *JNICALL Java_com_example_MyClass_myMethod(JNIEnv *env, jobject obj, jstring jstr) { const char *cstr = (*env)->GetStringUTFChars(env, jstr, NULL); // 使用 cstr 进行操作,并获取结果(假设结果为 result) (*env)->ReleaseStringUTFChars(env, jstr, cstr); char *result = (char *) malloc(strlen(cstr) + 1); strcpy(result, cstr); return result; } /** * jint:返回值 * Java_全类名_方法名 * JNIEnv *env */ jint Java_com_example_jniproject_JNI2_add (JNIEnv *env, jobject jobj, jint ji, jint jj) { int result = ji + jj; return result; }; /** * 从java传入字符串,C代码进行拼接 * * @param s I am from java * @return I am from java and I am from c */ jstring Java_com_example_jniproject_JNI2_sayHello (JNIEnv *env, jobject jobj, jstring jstring1) { char *fromJava = Java_com_example_MyClass_myMethod(env, jobj, jstring1); char *fromC = " and I am from C"; //拼接函数,拼接后得到的结果放到第一个参数里面 strcat(fromJava, fromC);//把拼接的结果放在第一个参数里面 //jstring (*NewStringUTF)(JNIEnv*, const char*); LOGE("fromJava==%s\n", fromJava); return (*env)->NewStringUTF(env, fromJava); }; /** * 给每个元素加10 * 场景:图片处理,颜色矩阵(就是数组),进行数组的处理 */ jintArray Java_com_example_jniproject_JNI2_increaseArrayEles (JNIEnv *env, jobject jobj, jintArray jintArray1) { //1.得到数组的长度 //jsize (*GetArrayLength)(JNIEnv*, jarray); jsize size = (*env)->GetArrayLength(env, jintArray1); //2.得到数组的元素 // jint* (*GetIntArrayElements)(JNIEnv*, jintArray, jboolean*); jint *intArray = (*env)->GetIntArrayElements(env, jintArray1, JNI_FALSE);//这里传0 false表示 在同一份内存操作,不开辟新的内存 //3.遍历数组,给每个元素加10 int i; for (i = 0; i < size; i++) { *(intArray + i) += 10; } // 4. 同步修改到 Java 层 (*env)->ReleaseIntArrayElements(env, jintArray1, intArray, 0); //4.返回结果 return jintArray1; }; /** * 应用:检查密码是否正确,如果正确返回200,否则返回400 */ jint Java_com_example_jniproject_JNI2_checkPwd (JNIEnv *env, jobject jobj, jstring jstring1) { //假设服务器的密码是123456 char *origin = "123456"; char *fromUser = Java_com_example_MyClass_myMethod(env, jobj, jstring1); //函数比较字符串是否相同 int code = strcmp(origin, fromUser); LOGE("code==%d\n", code); if (code == 0) { return 200; } else { return 400; } };
第五步,C代码调用Java代码,首先创建JNI3.java
/** * @Author: ly * @Date: 2023/8/6 * @Description: C代码调用Java代码 */ public class JNI3 { static { System.loadLibrary("CCallJava"); //加载动态链接库 } //当执行这个方法的时候,让C代码调用 //public int add(int x, int y) public native void callbackAdd(); /** * 当执行这个方法的时候,让C代码调用 * public void helloFromJava() */ public native void callbackHelloFromJava(); /** * 当执行这个方法的时候,让C代码调用 * public void printString(String s) */ public native void callbackPrintString(); /** * 当执行这个方法的时候,让C代码调用 * public static void sayHello(String s) */ public native void callbackSayHello(); public int add(int x, int y) { Log.e("TAG", "add() x=" + x + " y=" + y); return x + y; } public void helloFromJava() { Log.e("TAG", "helloFromJava"); } public void printString(String s) { Log.e("TAG", "C中输入的:" + s); } public static void sayHello(String s) { Log.e("TAG", "我是java代码中的JNI" + ".java中的sayHello(String s)静态方法,我被C调用了:" + s); } }
2.根据命令行,javah +JNI3全类名,生成CCallJava.c对应的头文件
com_example_jniproject_JNI3.h
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class com_example_jniproject_JNI3 */ #ifndef _Included_com_example_jniproject_JNI3 #define _Included_com_example_jniproject_JNI3 #ifdef __cplusplus extern "C" { #endif /* * Class: com_example_jniproject_JNI3 * Method: callbackAdd * Signature: ()V */ JNIEXPORT void JNICALL Java_com_example_jniproject_JNI3_callbackAdd (JNIEnv *, jobject); /* * Class: com_example_jniproject_JNI3 * Method: callbackHelloFromJava * Signature: ()V */ JNIEXPORT void JNICALL Java_com_example_jniproject_JNI3_callbackHelloFromJava (JNIEnv *, jobject); /* * Class: com_example_jniproject_JNI3 * Method: callbackPrintString * Signature: ()V */ JNIEXPORT void JNICALL Java_com_example_jniproject_JNI3_callbackPrintString (JNIEnv *, jobject); /* * Class: com_example_jniproject_JNI3 * Method: callbackSayHello * Signature: ()V */ JNIEXPORT void JNICALL Java_com_example_jniproject_JNI3_callbackSayHello (JNIEnv *, jobject); #ifdef __cplusplus } #endif #endif
3.对应的CCallJava.c文件中的代码如下
// // Created by DELL on 2023/8/6. // #include "com_example_jniproject_JNI3.h" #include <stdlib.h> #include <stdio.h> #include <android/log.h> #define TAG "luyu" #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,TAG ,__VA_ARGS__) // 定义LOGD类型 #define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG ,__VA_ARGS__) // 定义LOGI类型 #define LOGW(...) __android_log_print(ANDROID_LOG_WARN,TAG ,__VA_ARGS__) // 定义LOGW类型 #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG ,__VA_ARGS__) // 定义LOGE类型 #define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,TAG ,__VA_ARGS__) // 定义LOGF类型 /** * 让C代码调用Java中JNI类的public int add(int x ,int y) */ JNIEXPORT void Java_com_example_jniproject_JNI3_callbackAdd(JNIEnv *env, jobject jobj) { //1.得到字节码 //jclass (*FindClass)(JNIEnv*, const char*); jclass jclazz = (*env)->FindClass(env, "com/example/jniproject/JNI3"); //2.得到方法 //jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*); //最后一个参数是方法签名 jmethodID jmethodId = (*env)->GetMethodID(env, jclazz, "add", "(II)I"); //3.实例化该类 jobject jobject1 = (*env)->AllocObject(env, jclazz); //4.调用方法 //jint (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...); jint value = (*env)->CallIntMethod(env, jobject1, jmethodId, 99, 1); //成功调用 LOGE("value==%d\n", value); }; /*** * 让C代码调用 * public void helloFromJava() */ JNIEXPORT void JNICALL Java_com_example_jniproject_JNI3_callbackHelloFromJava (JNIEnv *env, jobject jobj) { //1.得到字节码 //jclass (*FindClass)(JNIEnv*, const char*); jclass jclazz = (*env)->FindClass(env, "com/example/jniproject/JNI3"); //2.得到方法 //jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*); //最后一个参数是方法签名 jmethodID jmethodIds = (*env)->GetMethodID(env, jclazz, "helloFromJava", "()V"); //3.实例化该类 jobject jobject1 = (*env)->AllocObject(env, jclazz); //4.调用方法 //jint (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...); (*env)->CallVoidMethod(env, jobject1, jmethodIds); }; /*** * 让C代码调用 * public void printString(String s) * */ JNIEXPORT void JNICALL Java_com_example_jniproject_JNI3_callbackPrintString (JNIEnv *env, jobject jobj) { //1.得到字节码 //jclass (*FindClass)(JNIEnv*, const char*); jclass jclazz = (*env)->FindClass(env, "com/example/jniproject/JNI3"); //2.得到方法 //jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*); //最后一个参数是方法签名 jmethodID jmethodIds = (*env)->GetMethodID(env, jclazz, "printString", "(Ljava/lang/String;)V"); //3.实例化该类 jobject jobject1 = (*env)->AllocObject(env, jclazz); //4.调用方法 //jint (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...); // jstring (*NewStringUTF)(JNIEnv*, const char*); jstring jst = (**env).NewStringUTF(env, "I am Android!"); (*env)->CallVoidMethod(env, jobject1, jmethodIds, jst); }; /** * 让C代码调用 * public static void sayHello(String s) * */ JNIEXPORT void JNICALL Java_com_example_jniproject_JNI3_callbackSayHello (JNIEnv *env, jobject jobj){ //1.得到字节码 //jclass (*FindClass)(JNIEnv*, const char*); jclass jclazz = (*env)->FindClass(env, "com/example/jniproject/JNI3"); //2.得到方法 // jmethodID (*GetStaticMethodID)(JNIEnv*, jclass, const char*, const char*); //最后一个参数是方法签名 jmethodID jmethodIds = (*env)->GetStaticMethodID(env, jclazz, "sayHello", "(Ljava/lang/String;)V"); jstring jst = (**env).NewStringUTF(env, "I am Android!"); (*env)->CallStaticVoidMethod(env,jclazz,jmethodIds,jst); };
之后,在MainActivity中调用对应的方法即可
public class MainActivity extends AppCompatActivity { private JNI2 jin2; private static final String TAG = "MainActivity"; private JNI3 mJNI3; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); jin2 = new JNI2(); mJNI3 = new JNI3(); } /******以下是Java代码调用C*****/ public void checkPwd(View view) { int result = jin2.checkPwd("12345678"); Log.i(TAG, "result==" + result); } public void increaseArrayEles(View view) { int array[] = {1, 2, 3, 4}; jin2.increaseArrayEles(array); for (int i = 0; i < array.length; i++) { Log.i(TAG, "result[" + i + "]:" + array[i]); } } public void sayHello(View view) { String result = jin2.sayHello("I am from java"); Log.i(TAG, "result==" + result); } public void add(View view) { int result = jin2.add(99, 1); Log.i(TAG, "result==" + result); } /******以下是C调Java代码*****/ public void callbackAdd(View view) { mJNI3.callbackAdd(); } public void callbackHelloFromJava(View view) { mJNI3.callbackHelloFromJava(); } public void callbackPrintString(View view) { mJNI3.callbackPrintString(); } public void callbackSayHello(View view) { mJNI3.callbackSayHello(); } }
点击按钮便可输出对应的日志,表示调用成功。