cmake+JNI调用C/C++开发Base64编码解码APP
声明:本人也是初学者,对于java语言也是刚开始学,下内容可能存在错误希望阅读者指正,部分内容也是参考其他网络文章,但是我找不到文件链接,表示很抱歉,也很感谢指导。
摘要:在Androidstudio中通过cmake分别实现了JNI调用C和JNI调用C++的两种方式实现Base64编码解码app开发
1 JNI
1.1 JNI简介
JNI是Java Native Interface的缩写,它提供了若干的API实现了Java和其他语言的通信(主要是C&C++)。它允许Java代码和其他语言写的代码进行交互。JNI一开始是为了本地已编译语言,尤其是C和C++而设计的,但是它并不妨碍你使用其他编程语言,只要调用约定受支持就可以了。使用java与本地已编译的代码交互,通常会丧失平台可移植性。但是,有些情况下这样做是可以接受的,甚至是必须的。例如,使用一些旧的库,与硬件、操作系统进行交互,或者为了提高程序的性能。JNI标准至少要保证本地代码能工作在任何Java 虚拟机环境。
1.2 JNI的作用
JNI可以这样与本地程序进行交互:
1. 你可以使用JNI来实现“本地方法”(native methods),并在JAVA程序中调用它们。
2. JNI支持一个“调用接口”(invocation interface),它允许你把一个JVM嵌入到本地程序中。本地程序可以链接一个实现了JVM的本地库,然后使用“调用接口”执行JAVA语言编写的软件模块。
JNI是JAVA平台的一个重要特征,使用它我们可以重用以前用C/C++写的大量代码。JNI的部分设计思想来源于Netscape的Java Runtime Interface(JRI)。使用JNI可以结合C++和Java两种语言的不同优势,使得我们的开发产品具有更强大的功能和更好的体验。
一方面,C++具有很好的执行效率和广泛的算法开源库,但是C++的开发成本和维护成本都过高,而另一方面,Java有更广的应用场景(Web开发、服务器开发、移动开发等),Java提供JNI接口,能够使C++代码运行在Java的环境中,使C++具有更好的应用场景,一方面能够将海量的C++开源库进行代码复用,减少了开发成本。
2 Base64开发
2.1 开发环境
Android studio 3.0
JDK:Jdk是Java Development Kit的缩写,顾名思义是java开发工具包,是程序员使用java语言编写java程序所需的开发工具包,是提供给程序员使用的。JDK包含了JRE,同时还包含了编译java源码的编译器javac,还包含了很多java程序调试和分析的工具:jconsole,jvisualvm等工具软件,还包含了java程序编写所需的文档和demo例子程序。
NDK:NDK是一系列工具的集合,帮助开发者快速开发C(或C++)的动态库,并能自动将so和java应用一起打包成apk。这些工具对开发者的帮助是巨大的。NDK集成了交叉编译器,并提供了相应的mk文件隔离CPU、平台、ABI等差异,开发人员只需要简单修改mk文件(指出“哪些文件需要编译”、“编译特性要求”等),就可以创建出so。NDK可以自动地将so和Java应用一起打包,极大地减轻了开发人员的打包工作。
Cmake:是一个跨平台的编译(Build)工具,可以用简单的语句来描述所有平台的编译过程。
2.2 JNI调用C++开发Base64步骤
2.2.1 打开Android studio 新建一个工程base64,注意勾选Include C++ support 选项,勾选后会自动生成makeLists.txt文件
2.2.2 选择相应的安卓版本
2.2.3 添加一个空活动,择你所需要的C++标准,工程创建完成
可见自动生成了一个用于在应用程序启动时加载“native-lib”(本地)库,一个本地native调用的函数stringFromJNI(),并且在main文件夹下有一个cpp文件夹,在文件夹内的native-lib.cpp就有stringFromJNI()的接口
makeLists.txt文件
add_library( # Sets the name of the library.
native-lib – 设置库的名称
# Sets the library as a shared library.
SHARED --将库设置为共享库。
# Provides a relative path to your source file(s).
src/main/cpp/native-lib.cpp ---提供源文件的相对路径。此处可以根据自己的C/C++文件的具体位置更改设定)
build.gradle出现以下内容
2.2.4 设计app的应用界面
安卓app应用界面的设计是在app\res\layout\activity_main.xml下完成
2.2.6 编写接口
2.2.6.1 在MainActivity中编写本地调用接口,此时就是调用src/main/cpp/native-lib.cpp中的接口
2.2.6.2 在src/main/cpp/native-lib.cpp(此时native-lib.cpp是系统默认自动生成我们直接写入C++,不用其他操作)中编写C++源
写好接口函数如下:
同样要加入你需要的.h 头文件,并且在cpp中声明:
2.2.7 So文件生成
Make project一下就可以在cmake文件目录下看到生存了so文件
2.2.8 获取EditText的输入字符并且输出到TextView
2.2.9 在布局按钮中设置响应,触发解码编码
2.2.10 打包软件生成app
存在中文解码乱码错误!!!
2.3 JNI调用C开发Base64步骤
前期步骤同C++开发,界面完全相同,只是使用C语言进行JNI调用,以下是不同之处
2.3.1 创建c文件
在src/main/cpp目录下创建base64.c、base64.h文件
2.3.2 更改makeLists.txt使用C源码
步骤可省略!
2.3.3 编写接口
在MainActivity中编写本地调用接口,此时就是需要调用src/main/cpp/base64.c中的接口
2.3.4 得到对应.class文件
Build project可在
app/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/dqp/base64
下得到MainActivity.class
2.3.5 生成对应的.h文件
使用命令javac -h + .class路径
就可以生成对应的.h文件,可在app\src\main\jni下
找到com_dqp_base64_MainActivity.h,其中就是C中对应的接口
将内容拷贝到之前创建的base64.h中
2.3.6 编写C代码,完成接口
同样也要先引入.h文件
2.3.7 So文件生成
2.3.8 完成MainActivity中数据的获取和输出并且调用base.c得到结果
2.3.9 在布局按钮中设置响应,触发解码编码
2.3.10 打包生成app
3 关于中文乱码问题
java 内部是使用的 16 bit 的 unicode 编码(utf-16)来表示字符串的,无论英文还是中文都是 2 字节。
JNI 内部是使用 utf-8 编码来表示字符串的,utf-8 是变长编码的 unicode,一般 ascii 字符是 1 字节,中文是 3 字节。
C/C++ 使用的是原始数据,ascii 就是一个字节,中文一般是 GB2312 编码,用 2 个字节表示一个汉字。
java 调用的时候使用的是 utf-16 编码的字符串,jvm 把这个参数传递给 JNI,C 得到的输入是 jstring,此时,可以利用 JNI 提供的两种函数进行解析:一个是 GetStringUTFChars,这个函数将得到一个 UTF-8 编码的字符串; 另一个是 GetStringChars 这个将得到 UTF-16 编码的字符串。无论那个函数,得到的字符串如果含有中文,都需要进一步转化成GB2312的编码。
解决方法:
1.C 端进行编码转换
C 端进行中文字符转换传递个 java 的时候,也是调用了 java 中 String 类的编码转换方式进行转换。
2.java 端进行编码转换
在 C 中,将 char* 转换为 jbyteArray,然后返回。在 Java端,获取数组,然后从”GB2312”格式生成 utf16 格式的java字符串。