前言:想借助JNI和NDK的知识开发Android的串口通信,但是之前对这一部分没有了解过,以至于第一步so文件的生成和使用,就花费了两天,这里记录下配置过程。(网上有些资料也不完全对,走了很多弯路。) 借鉴博客如下:
so文件的生成及其使用
- Android Studio软件的配置说明
- NDK/JNI介绍
- 什么是NDK?
- 为什么使用NDK?
- 什么是JNI?
- 为什么使用JNI?
- 安卓中的so文件是什么?
- NDK的安装及其配置
- NDK的安装
- NDK的配置
- so库开发
- 新建本地方法
- 编译该类得到对应的.h文件
- 创建jni文件夹,添加.c文件
- 编写Android.mk文件
- 使用so文件
- 相关配置
- MainActivity中引用libdemo.so和使用本地函数
- 注意
Android Studio软件的配置说明
- Android Studio Arctic Fox | 2020.3.1 Patch 2
- distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip
- classpath "com.android.tools.build:gradle:7.0.2"
- 以下工程就是正常新建工程得到的
- 之所以提到版本号,是因为不同版本号操作步骤略有不同,这里只是给大家做参考。
NDK/JNI介绍
什么是NDK?
- NDK全称是Native Development Kit,NDK提供了一系列的工具,帮助开发者快速开发C(或C++)的动态库,并能自动将so和java应用一起打包成apk。NDK继承了交叉编译器(交叉编译器需要UNIX或LINUX系统环境),提供了响应的mk文件隔离CPU、平台、ABI等差异,开发人员只需要简单地修改mk文件(指出”哪些文件需要编译“、“编译特性要求等”),就可以创建出so。
为什么使用NDK?
- 代码的保护,由于apk的java代码层很容易被反编译,而C/C++库反汇难度较大。
- 可以方便地使用现存的开源库。大部分现存的开源库都是用C/C++代码编写的。
- 提高程序的执行效率,将要求高性能的应用逻辑使用C开发,从而提高程序的执行效率。
- 便于移植,用C/C++写的库可以方便地在其他的嵌入式平台上再次使用。
什么是JNI?
- JNI的全称是Java Native Interface ,它提供了若干的API实现了Java和其他语言的通信(主要是C/C++)。
为什么使用JNI?
- JNI的目的是使java方法能够调用c实现的 一些函数。
安卓中的so文件是什么?
- android中用到的so文件是一个c++的函数库。在android的JNI中,要先将相应的C语言打包成so库,然后导入到lib文件夹中供java调用。
NDK的安装及其配置
NDK的安装
- 可以直接从Android Studio软件中的SDK Manager中:Settings — Appearance & Behavior — Android SDK
- 选中SDK Tools
- 下载NDK和CMake,只需要勾选前面,点击OK即可下载。
- 这里注意可以点击右下角的 Show Package Details,可以看到安装包的版本。
- 开始NDK和CMake我都选择了最新版本:NDK – 24.0.8215888 , CMake – 3.18.1
- 但是出现问题NDK does not contain any platforms,后来查阅资料知道,是因为NDK版本过高,不适合当前版本的Android Studio,因此又下载了较低版本的NDK和CMake,具体版本如上图所示。
- 之前如果设置过SDK的下载路径,则NDK的下载路径会默认下载到SDK的下载路径里。等待10-15分钟左右,时间稍微有些长。
- 但这里要注意下,Android Studio中的路径不要设置带空格和带中文的 ,我就是因为之前SDK的下载路径是带空格的,之前一直没出现问题,结果NDK的默认下载路径还是这个,但因为带空格,所以后面测试NDK是否配置成功时,一直不反应,无奈只能复制NDK和CMake的资料到另外新建的文件夹中(路径名不带空格)。
- 原本下载路径是:D:\JAVA\tools\Android Studio\Android_Sdk
- 将NDK和CMake单独复制出来的路径是:D:\JAVA\tools\AndroidSdk(其实由于我也不知道NDK和CMake的资料是哪几部分,索性把Android_Sdk中的内容全部复制到AndroidSdk文件夹中)
- 大家如果路径不带空格和中文,就不用看我上面的废话,我只是做个笔记。
NDK的配置
- 第一步:环境变量的配置
- 电脑右键属性 – 高级系统设置 – 环境变量 – 系统变量(非用户变量)
- 第一种方式:
- 首先新建NDK_HOME 变量名,变量值是D:\JAVA\tools\AndroidSdk\ndk-bundle(不带空格) ,因为 ndk-bundle 文件夹中有 ndk-build 命令,因此需要设置。
- 编辑Path 环境变量,添加进 ndk-bundle 的路径,%NDK_HOME%
- 第二种方式:就是直接把 ndk-bundle 的路径添加进Path环境变量中,不需要在建立中间变量NDK_HOME。
- 但我还是建议用第一种,因为NDK_HOME这个变量我们在使用终端命令时,是用得到的,即和JAVA_HOEM变量一样。
- 第二步:Android Studio软件中配置 ndk-bundle 的路径,这里我写进的是带空格的路径,也不影响,但上面提到的Path环境变量里需要是不带空格的路径。
- 我们一般是在SDK Location里直接选择 ndk-bundle 的路径即可,但我们这里显示灰色,无法选择,查阅资料,直接在 local.properties文件 中写进路径:
- 第三步:验证NDK配置是否成功:
- 打开Android Studio的终端窗口:输入 ndk-build 命令行
- 出现以下信息便证明配置成功
so库开发
新建本地方法
- 在MainActivity.java中建立了一个方法public native String getStrFromJNI();
- 可以看到这个方法的声明中有 native 关键字,这个关键字表示这个方法是本地方法,也就是说这个方法getStrFromJNI()是通过本地代码(C/C++)实现的,在java代码中仅仅是声明。
编译该类得到对应的.h文件
- 进入终端命令窗口
- 切换到java目录下:cd app\src\main\java
- 再输入: javah -jni 包名.类名
- 这里输入:javah -jni com.example.testndk2.MainActivity
- 编译成功后,等待会可以看到编译出的.h文件。
- 打开.h文件,其中最重要的代码是:
JNIEXPORT jstring JNICALL Java_com_example_testndk2_MainActivity_getStrFromJNI
(JNIEnv *, jobject);
- 其中遵循 Java_包名_类名_本地方法名 来定义。
创建jni文件夹,添加.c文件
- 右键Moudle,菜单中选择 New -> Folder -> JNI Folder,弹出的对话框,直接点击完成。
- 将前面生成 .h文件 移到 jni 文件夹中
- 再右键jni文件夹,弹出菜单选择New -> C/C++ Source File,创建demo.c文件。
- 在demo.c文件中编写测试代码:
#include <string.h>
#include <jni.h>
jstring
Java_com_example_testndk2_MainActivity_getStrFromJNI(JNIEnv *env,
jobject thiz) {
return (*env)->NewStringUTF(env, "I`m Str !");
}
- 注意demo.c代码中的格式和.h文件中的相同:Java_包名_类名_本地方法名
编写Android.mk文件
- 右键 jni文件夹,弹出菜单 New -> File
- 在 Android.mk 文件中编写代码
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := demo
LOCAL_SRC_FILES := demo.c
include $(BUILD_SHARED_LIBRARY)
- LOCAL_MODULE:=demo 是要生成的so库的名称,但实际为libdemo.so
- LOCAL_SRC_FILES:=demo.c 是要使用的文件,刚才编写的demo.c文件
- 注意这两句后面不要跟着注释,否则会报错Android NDK: LOCAL_MODULE definition in D:/JAVA/tools/AndroidSerialPort/TestNdk2/app/src/main/jni/Android.mk must not contain space
- 接着在终端命令窗口中,在java目录下,输入指令:ndk-build
- 接着窗口会打印出一系列信息:说明编译成功,生成.so文件。
- 等待会或者刷新工程,便看到工程目录中多出 libs 和 obj 两个文件夹。
- libs文件夹中有四个子文件夹,每一个文件夹都有 libdemo.so文件 ,便是生成成功。
使用so文件
相关配置
- 在app的 build.gradle 的 android节点 下设置:
- 注意路径是libs文件夹的相对路径
MainActivity中引用libdemo.so和使用本地函数
注意
如果改变demo.c文件中代码内容,都需要重新ndk-build,重新编译生成.so文件。