JNI开发

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);
        }
  • 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中的方法声明
        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);
        }

七、静态和动态注册

  • 静态注册

    • C语言中函数名和Java中函数名之间的对应关系
      Java_包名_类名_函数名

      c文件中的函数名

  • 动态注册

    • 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需要添加编译配置

posted @ 2023-10-01 23:43  eliwang  阅读(110)  评论(0编辑  收藏  举报