JNI(java native interface),Java本地开发接口,实现JAVA和C语言之间的相互调用。
一、NDK
- NDK,Native Develop Kits,是JNI开发的工具包(在Android Studio中下载即可)
二、项目创建
- 普通项目:模板Empty Activity(Java)
- JNI项目:模板Native C++(Java + C)
- 会多一些默认配置
三、快速实现
-
1、新建java类 + native方法
package com.example.jni; public class Utils { // 加载c文件 static { System.loadLibrary("encrypt"); } // 1、java实现 public static String v0(String data) { return data + "hello world"; } // 2、native方法(声明即可,具体在c语言中实现) public static native int v1(int data1, int data2); }
-
2、创建c文件 + 创建函数【Java_包名_类名_方法名】
- 在【cpp】目录下,右键【new】选择【C/C++ Source File】,记得文件后缀改成【.c】
- 快捷键:直接输入java类中定义的native方法名【v1】,根据提示选择后回车
#include <jni.h> JNIEXPORT jint JNICALL Java_com_example_jni_Utils_v1(JNIEnv *env, jclass clazz, jint data1, jint data2) { return data1 + data2 + 100; }
-
3、c编译配置
-
4、java类中加载c文件
package com.example.jni; public class Utils { static { System.loadLibrary("encrypt"); } // 1、java实现 public static String v0(String data) { return data + "hello world"; } // 2、c实现 public static native int v1(int data1, int data2); }
-
5、逆向和反编译
- jadx反编译并定位到了native方法
- 将apk包中的so文件导出到ida中进一步分析
点击进入后,按F5,并可以导入jni.h头文件等操作
四、类型对照
-
JNIEXPORT jstring JNICALL Java_com_example_jni_Utils_v2(JNIEnv *env, jclass clazz, jstring data) { // jstring转c语言中的字符串 char *newData = (*env)->GetStringUTFChars(env, data, 0); newData[0] = 'a'; newData[3] = 'x'; // c语言中的字符串转jstring return (*env)->NewStringUTF(env, newData); }
六、示例
-
1、Java调用C示例
- 字符串拼接
- java中的方法声明
// 字符串拼接 public static native String v3(String data1, String data2);
- encrypt.c
#include <jni.h> #include <string.h> #include <malloc.h> JNIEXPORT jstring JNICALL Java_com_example_jni_Utils_v3(JNIEnv *env, jclass clazz, jstring data1, jstring data2) { char *res1 = (*env)->GetStringUTFChars(env, data1, 0); char *res2 = (*env)->GetStringUTFChars(env, data2, 0); char *result = malloc(strlen(res1) + strlen(res2) + 1); strcpy(result, res1); strcat(result, res2); return (*env)->NewStringUTF(env, result); }
- java中的方法声明
- 字符串拼接
-
2、C调用Java示例
- 静态方法
- java中的方法声明
package com.example.jni; public class Utils { static { System.loadLibrary("encrypt"); } // c调用java静态方法 public static native String v4(); }
- C语言中需要调用的java方法声明
package com.example.jni; public class SignQuery { public static String getPart1() { return "hello"; } public static String getPart2(int len) { return "world".substring(2); } public static String getPart3(String prev) { return "NB"; } public static int getPart4(String prev, int v1) { return 100; } }
- encrypt.c
#include <jni.h> #include <string.h> #include <malloc.h> JNIEXPORT jstring JNICALL Java_com_example_jni_Utils_v4(JNIEnv *env, jclass clazz) { // 1、找类 (类名路径分隔符"/") jclass cls = (*env)->FindClass(env, "com/example/jni/SignQuery"); // 2、找静态方法 (参数3:方法名, 参数4:JNI签名) jmethodID methodId1 = (*env)->GetStaticMethodID(env, cls, "getPart1", "()Ljava/lang/String;"); jmethodID methodId2 = (*env)->GetStaticMethodID(env, cls, "getPart2", "(I)Ljava/lang/String;"); jmethodID methodId3 = (*env)->GetStaticMethodID(env, cls, "getPart3", "(Ljava/lang/String;)Ljava/lang/String;"); jmethodID methodId4 = (*env)->GetStaticMethodID(env, cls, "getPart4", "(Ljava/lang/String;I)I"); // 3、调用静态方法 (参数3:jmethodID,如果方法有参数,则以位置参数的形式依次写上) // 调用方法时,需要根据返回值类型来进行选择 jstring res1 = (*env)->CallStaticObjectMethod(env, cls, methodId1); jstring res2 = (*env)->CallStaticObjectMethod(env, cls, methodId2, 100); jstring res3 = (*env)->CallStaticObjectMethod(env, cls, methodId3, (*env)->NewStringUTF(env, "come on!")); jint res4 = (*env)->CallStaticIntMethod(env, cls, methodId4, (*env)->NewStringUTF(env, "my god!"), 28); // 4、jstring => c语言字符串 char *result1 = (*env)->GetStringUTFChars(env, res1, 0); char *result2 = (*env)->GetStringUTFChars(env, res2, 0); char *result3 = (*env)->GetStringUTFChars(env, res3, 0); // 5、c语言中字符串相关操作 char *result = malloc(strlen(result1) + strlen(result2) + strlen(result3) + 1); strcpy(result, result1); strcat(result, result2); strcat(result, result3); // 6、C语言字符串 => jstring return (*env)->NewStringUTF(env, result); }
- java中的方法声明
- 实例方法
- java中的方法声明
package com.example.jni; public class Utils { static { System.loadLibrary("encrypt"); } // c调用java实例方法 public static native String v5(); }
- C语言中需要调用的java方法声明
package com.example.jni; public class GetSign { String name; int age; public GetSign(String name, int age){ this.name = name; this.age = age; } public String getPart1() { return this.name; } public String getPart2(int len) { return "world".substring(2); } public String getPart3(String prev) { return "NB"; } public int getPart4(String prev, int v1) { return 100; } }
- encrypt.c
#include <jni.h> #include <string.h> #include <malloc.h> JNIEXPORT jstring JNICALL Java_com_example_jni_Utils_v5(JNIEnv *env, jclass clazz) { // 1、找类 (类名路径分隔符"/") jclass cls = (*env)->FindClass(env, "com/example/jni/GetSign"); // 2、找构造方法 jmethodID init = (*env)->GetMethodID(env, cls, "<init>", "(Ljava/lang/String;I)V"); // 3、实例化对象 jobject obj = (*env)->NewObject(env, cls, init, (*env)->NewStringUTF(env, "eliwang"), 28); // 4、找实例方法 jmethodID methodId1 = (*env)->GetMethodID(env, cls, "getPart1", "()Ljava/lang/String;"); jmethodID methodId2 = (*env)->GetMethodID(env, cls, "getPart2", "(I)Ljava/lang/String;"); jmethodID methodId3 = (*env)->GetMethodID(env, cls, "getPart3", "(Ljava/lang/String;)Ljava/lang/String;"); jmethodID methodId4 = (*env)->GetMethodID(env, cls, "getPart4", "(Ljava/lang/String;I)I"); // 5、执行实例方法 jstring res1 = (*env)->CallObjectMethod(env, obj, methodId1); jstring res2 = (*env)->CallObjectMethod(env, obj, methodId2, 100); jstring res3 = (*env)->CallObjectMethod(env, obj, methodId3, (*env)->NewStringUTF(env, "come on!")); jint res4 = (*env)->CallIntMethod(env, obj, methodId4, (*env)->NewStringUTF(env, "my god!"), 28); // 6、jstring => c语言字符串 char *result1 = (*env)->GetStringUTFChars(env, res1, 0); char *result2 = (*env)->GetStringUTFChars(env, res2, 0); char *result3 = (*env)->GetStringUTFChars(env, res3, 0); // 7、c语言中字符串相关操作 char *result = malloc(strlen(result1) + strlen(result2) + strlen(result3) + 1); strcpy(result, result1); strcat(result, result2); strcat(result, result3); // 8、C语言字符串 => jstring return (*env)->NewStringUTF(env, result); }
- java中的方法声明
- 静态方法
七、静态和动态注册
-
静态注册
- C语言中函数名和Java中函数名之间的对应关系
Java_包名_类名_函数名
c文件中的函数名
- C语言中函数名和Java中函数名之间的对应关系
-
动态注册
- DynamicUtils.java
package com.example.jni; public class DynamicUtils { static { System.loadLibrary("dynamic"); } // JNI动态注册 public static native int add(int v1, int v2); }
- dynamic.c
#include <jni.h> jint plus(JNIEnv *env, jobject obj, jint v1, jint v2){ return v1 + v2; } static JNINativeMethod gMethods[] = { {"add", "(II)I", (void *) plus}, }; JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { JNIEnv *env = NULL; // 在java虚拟机中获取env if ((*vm)->GetEnv(vm, (void **) &env, JNI_VERSION_1_6) != JNI_OK) { return JNI_ERR; } // 找到Java中的类 jclass clazz = (*env)->FindClass(env, "com/example/jni/DynamicUtils"); // 将类中的方法注册到JNI中 (RegisterNatives) int res = (*env)->RegisterNatives(env, clazz, gMethods, 1); if (res < 0) { return JNI_ERR; } return JNI_VERSION_1_6; }
dynamic.c需要添加编译配置
- DynamicUtils.java