JNI与NDK

JNI,是Java Native Interface的缩写,中文为Java本地调用。通俗地说,JNI是一种技术,通过这种技术可以做到以下两点:
· Java程序中的函数可以调用Native语言写的函数,Native一般指的是C/C++编写的函数。
· Native程序中的函数可以调用Java层的函数,也就是在C/C++程序中可以调用Java的函数。

交叉编译

  • 在一个平台下,编译出另一个平台能够执行的二进制代码
  • 平台:windows , mac os, linux
  • 处理器:x86(主要厂商英特尔和英伟达,pc一般用这个),arm(嵌入式设备,手机用这个),mips(开源的一个处理器架构,很多厂商对它进行修改)
  • 原理:

    • 源代码->编译->链接->可执行程序
    • 模拟其他平台特性
  • 交叉编译的工具链

    • 多个工具集合,一个工具使用完后接着调用下一个工具
  • 常见工具

    • NDK:模拟其他平台特性来编译代码的工具
    • CDT:C/C++ Development Tools(高亮显示C语言关键字)
    • cygwin:模拟器,可以在windows下运行linux指令

NDK介绍

  • NDK目录结构
    • build/tools:linux的批处理文件
    • platforms:编译C代码需要使用的头文件和类库
    • prebuild:预编译使用的二进制可执行文件
    • python-pachages:
    • sources:NDK的源码
    • toolchains:工具链
    • ndk-build.cmd:编译打包C代码的一个指令,肯定会调用toolchains开发人员不用管

使用JNI

  1. 在项目根目录下创建 jni文件夹
  2. 在jni文件夹中创建一个 C 文件
  3. 除了两个标准头文件在包含
    <jni.h>
  4. 在Java代码中创建一个本地方法helloFromC
    publicnativeString helloFromC();
  5. 在JNI中定义函数实现这个方法,函数必须这么写
    Java_com_hk_hellojni_MainActivity_helloFromC(JNIEnv*env,jobject obj)
    • 其中Java是必须的关键字,后面跟着包名类名方法名,中间用_隔开
    • 参数必须是JNIEnv* env和jobject obj
      env是一个二级指针,指向存放Java虚拟机的内存地址的内存块
    • obj表示那个对象调用该方法
    • 可以使用javah指令自动生成:javah 包名.类名
    • JDK1.7 在src目录下执行
      • JDK1.6 在bin/classes目录下执行
        • 结果:jstring JNICALL Java_com_hk_hellojni_MainActivity_helloFromC(JNIEnv *, jobject);
  6. 返回一个字符串,用C定义一个字符串

     char* cstr = "hello from C";
    
  7. 将C字符串转化成Java字符串

    jstring jstr = (*env)->NewStringUTF(env,cstr);
    
    • NewStringUTF是env所指向的指针所指向的结构体中的函数指针变量
  8. 在jni文件夹中创建Android.mk文件,文件内容如下

     LOCAL_PATH:= $(call my-dir)
     include $(CLEAR_VARS)
     #编译生成的类库叫什么名字
     LOCAL_MODULE:= hello
     #要编译的C文件
     LOCAL_SRC_FILES:= Hello.c
     include $(BUILD_SHARED_LIBRARY)
    
  9. 在jni文件夹下执行ndk-build.cmd指令(要提前配置到环境变量中)
  10. Java代码中加载so类库,调用本地方法

    System.loadLibrary("hello");//一般在静态代码块中执行
    

    注意:ndk-build.cmd指令执行默认生成arm架构的类库,如果需要支持其他cpu架构需要在jni文件夹下创建Application.mk文件里面加入需要支持的架构,如,加入x86的支持
    APP_ABI :=armeabi armeabi-v7a x86
    如果需要支持cpu全部架构,把等号右边换成all

JNI常见错误

  1. findLibrary returned null (类库加载失败)
    • CPU平台不匹配
    • 加载类库时,写错类库名字
  2. 本地方法找不到
    • 忘记加载类库
    • C代码中的和Java本地方法对应的方法名写错

Eclipse配置NDK开发环境

  • 指定NDK位置:Windows->Preferences->Android->NDK->Broawse到NDK所放位置下的build文件夹
  • 关联jni.h:选中项目右键->Properties->C/C++ General->Paths and Symbols->Add->选择NDK目录下的platforms选择对应的SDK版本号,选择cpu架构选择usr选择include(例如:D:\android\android-ndk-r11c\platforms\android-21\arch-arm\usr\include)->点击完成
    配置完成后可以直接运行Android Application,在编译的时候会自动生成类库

利用逆向助手反编译apk

  • 工具:Android逆向助手-v2.0.rar
    • 提取Java代码
      1. 提取dex
      2. dex转jar
    • 获取资源文件,图片,jar,so
      1. 解压缩即可

C语言使用Logcat(调试)

  • Android.mk文件增加
    LOCAL_LDLIBS += -llog
  • C代码中增加

    #include <android/log.h>
    #define LOG_TAG "hvcker"
    #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
    #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
    
    • 在代码中就可以用了
      LOGI(“info\n”);
      LOGD(“debug\n”);

在C中使用反射调用Java方法

    /**
     * 得到Java字节码对象内存地址
     */
    //jclass      (*FindClass)(JNIEnv*, const char*);
    jclass clazz = (*env)->FindClass(env,"com/example/jniccj/MainActivity");

    /**
     * 得到Java方法,最后一个参数是方法签名,可用java -s 获取
     */
    //jmethodID   (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
    jmethodID methodID = (*env)->GetMethodID(env,clazz,"show","(Ljava/lang/String;)V");

    /**
     * 调用方法,CallXxxMethod,Xxx为方法返回值,最后一个参数是可变参数,传入方法参数
     */
    //void        (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
    (*env)->CallVoidMethod(env,obj,methodID,(*env)->NewStringUTF(env,"hello Java"));

* javap -s 要在projectName/bin/classes目录下执行
* 执行代码:javap -s packageName.classnName

调用C++代码

    #include <jni.h>
    //包含同一个文件夹下的声明函数的头文件
    #include "com_example_jnicpp_MainActivity.h"

    JNIEXPORT jstring JNICALL Java_com_example_jnicpp_MainActivity_helloCpp(
    JNIEnv * env, jobject obj) {
        char * cstr = "hello from cpp";

        //C++的env只是一个一级指针
        return env->NewStringUTF(cstr);
    }
  • 包含头文件(用javah 生成的那个在src目录下的文件)
  • C的env和C++的env的区别


  • 在C中JNIEnv是一个结构体指针typedef const struct JNINativeInterface* JNIEnv;,后面再加一个*就表示是一个二级指针,所以用的时候要先取出env所指向的地址,改地址是指向JNINativeInterface结构体的指针,所以要调用JNINativeInterface里面的方法要(*env)->xxx
  • 在C++中JNIEnv是一个里面含有JNINativeInterface的结构体
    typedef _JNIEnv JNIEnv;
    struct _JNIEnv {
    /* do not rename this; it does not seem to be entirely opaque */
    const struct JNINativeInterface* functions;
    ...
    }

    所以C++的 JNIEnv* 是一个一级指针,所以调用的时候直接env->xxx就可以了。

还有一点要注意的是:C++是面向对象的,所以它在执行类似newStringUTF方法时不需要传入本身env,直接env->newStringUTF(“hello from c”);底层也是调用JNINativeInterface中的函数
jstring NewStringUTF(const char* bytes)
{ return functions->NewStringUTF(this, bytes); }

分支C进程

int pid = fork();
//如果pid = 0分支成功
if(pid == 0){
    while(1){
        LOGD("fork C");
        sleep(1);
    }
}
  • 分支出来的C进程在不同型号的手机会有不同的表现形式,例如在魅族手机,依赖于Android进程,程序管理里面强制停止,C进程随之停止,但在有些型号手机中,C进程怎么杀也杀不死,只能用adb shell kill [pid]来删除
  • 可以分支出两个守护进程,这样怎么杀也杀不掉(可以给线程做保活)

常用代码(C部分)

  • 中文乱码

    jstring ctojstring(JNIEnv *env, char* tmpstr) {
        jclass Class_string;
        jmethodID mid_String, mid_getBytes;
        jbyteArray bytes;
        jbyte* log_utf8;
        jstring codetype, jstr;
        Class_string = (*env)->FindClass(env, "java/lang/String"); //获取class
        //先将gbk字符串转为java里的string格式
        mid_String = (*env)->GetMethodID(env, Class_string, "<init>",
        "([BLjava/lang/String;)V");
        bytes = (*env)->NewByteArray(env, strlen(tmpstr));
        (*env)->SetByteArrayRegion(env, bytes, 0, strlen(tmpstr), (jbyte*) tmpstr);
        codetype = (*env)->NewStringUTF(env, "gbk");
        jstr = (jstring) (*env)->NewObject(env, Class_string, mid_String, bytes,codetype);
        (*env)->DeleteLocalRef(env, bytes);
    
        //再将string变utf-8字符串。
        mid_getBytes = (*env)->GetMethodID(env, Class_string, "getBytes",
        "(Ljava/lang/String;)[B");
        codetype = (*env)->NewStringUTF(env, "utf-8");
        bytes = (jbyteArray) (*env)->CallObjectMethod(env, jstr, mid_getBytes,
                codetype);
        log_utf8 = (*env)->GetByteArrayElements(env, bytes, JNI_FALSE);
        return (*env)->NewStringUTF(env, log_utf8);
    }
    
  • Java字符串转C

    char* _JString2CStr(JNIEnv* env, jstring jstr) {
         char* rtn = NULL;
         jclass clsstring = (*env)->FindClass(env, "java/lang/String");
         jstring strencode = (*env)->NewStringUTF(env,"GB2312");
         jmethodID mid = (*env)->GetMethodID(env, clsstring, "getBytes", "(Ljava/lang/String;)[B");
         jbyteArray barr = (jbyteArray)(*env)->CallObjectMethod(env, jstr, mid, strencode); // String .getByte("GB2312");
         jsize alen = (*env)->GetArrayLength(env, barr);
         jbyte* ba = (*env)->GetByteArrayElements(env, barr, JNI_FALSE);
         if(alen > 0) {
            rtn = (char*)malloc(alen+1); //"\0"
            memcpy(rtn, ba, alen);
            rtn[alen]=0;
         }
         (*env)->ReleaseByteArrayElements(env, barr, ba,0);
         return rtn;
    }
    
  • C语言操作Java数组

    //拿到整形数据的长度和整形数组的指针
    //jsize       (*GetArrayLength)(JNIEnv*, jarray);
    int length = (*env)->GetArrayLength(env,jintarr);
    //jint*       (*GetIntArrayElements)(JNIEnv*, jintArray, jboolean*);
    int * arrp = (*env)->GetIntArrayElements(env,jintarr,0);
    int i;
    for(i = 0;i<length;i++){
        *(arrp+i)+=20;
    }
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
智慧校园整体解决方案是响应国家教育信息化政策,结合教育改革和技术创新的产物。该方案以物联网、大数据、人工智能和移动互联技术为基础,旨在打造一个安全、高效、互动且环保的教育环境。方案强调从数字化校园向智慧校园的转变,通过自动数据采集、智能分析和按需服务,实现校园业务的智能化管理。 方案的总体设计原则包括应用至上、分层设计和互联互通,确保系统能够满足不同用户角色的需求,并实现数据和资源的整合与共享。框架设计涵盖了校园安全、管理、教学、环境等多个方面,构建了一个全面的校园应用生态系统。这包括智慧安全系统、校园身份识别、智能排课及选课系统、智慧学习系统、精品录播教室方案等,以支持个性化学习和教学评估。 建设内容突出了智慧安全和智慧管理的重要性。智慧安全管理通过分布式录播系统和紧急预案一键启动功能,增强校园安全预警和事件响应能力。智慧管理系统则利用物联网技术,实现人员和设备的智能管理,提高校园运营效率。 智慧教学部分,方案提供了智慧学习系统和精品录播教室方案,支持专业级学习硬件和智能化网络管理,促进个性化学习和教学资源的高效利用。同时,教学质量评估中心和资源应用平台的建设,旨在提升教学评估的科学性和教育资源的共享性。 智慧环境建设则侧重于基于物联网的设备管理,通过智慧教室管理系统实现教室环境的智能控制和能效管理,打造绿色、节能的校园环境。电子班牌和校园信息发布系统的建设,将作为智慧校园的核心和入口,提供教务、一卡通、图书馆等系统的集成信息。 总体而言,智慧校园整体解决方案通过集成先进技术,不仅提升了校园的信息化水平,而且优化了教学和管理流程,为学生、教师和家长提供了更加便捷、个性化的教育体验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值