作者:bigfog

原文链接:www.bigfog.info

前言

实习时曾经用真机进行过so文件的调试。这次使用的模拟器,过程和原先也一样,但到断点处就是运行不起来。熬了二个晚上,终于发现模拟器不支持arm的so的调试。要模拟器调试so的话只能选择x86版本的so。通过gdb开端口来调试,弄下来很复杂。这篇文章简单介绍下安卓中So文件的编写,以及对so文件的调试。

相关知识介绍

在安卓程序中为什么要使用So库?

  1. So机制可以让开发者最大化利用已有的C和C++代码,达到重用的效果。
  2. So是二进制,没有多余开销,速度快。
  3. 相对于java代码,二进制代码的反编译难度更大,所以很多核心代码都可以放在里面。这也是研究So库的一个很大原因。

NDK基础概念

NDK:英文全称为Native Development Kit。它不是一个单独的工具,是一个包含API,交叉编译器,链接程序,调试器,构建工具,文档和示例应用程序的综合工具集。比如使用了哪个版本的C++编译,引用哪些共享库,还可以指定编译的ABI,只有有了这些NDK中的编译工具才能准确的编译C/C++代码。

JNI:英文全称为Java Native Interface,java本地接口。它允许java类的某些方法原生实现,但同时也能够像普通Java方法一样被调用和使用,这些原生方法也可以使用Java对象。java提供的jni与本地代码交互,极大增强了java语言的本地交互能力。

Cmake:是一个跨平台的编译工具,它会根据Cmakelist.txt的语言规则生成对应的Makefile或者project文件。

Android Studio 2.2版本之后工具增加了Cmake支持。这样编辑C/C++代码就有两种选择:

  1. ndk-build+Android.mk+Application.mk组合。
  2. Cmake+Cmakelist.txt组合
    这组合只是不同的构建脚本和构建命令。对第一种了解不多,所以这篇主要使用Cmake和cmakelist.txt来编写so库。

基本数据类型

java类型jni类型C/C++类型
BooleanJBooleanunsigned char
ByteJBytechar
CharJByteunsigned short
ShortJShortshort
IntJIntint
LongJLonglong long
FloatJFloatfloat
DoubleJDoubledouble

创建字符串

可以在原生代码中使用NewString函数构建Unicode编码格式的字符串实例。用NewStringUTF函数构建UTF-8编码格式的字符串实例。
用给定的C字符串创建java字符串

Jstring javaString;
javaString =(*env)->NewStringUTF(env,"hello world");

内存溢出,函数将返回Null会抛出异常,代码会停止。

java字符串转为C字符串

为了在原声代码中使用java字符串,需要先将java字符串转为C字符串。
GetStringChars函数可以将Unicode格式的java字符串转为C字符串,用GetStringUTFChars函数可以将UTF-8格式的字符串转为C字符串。这些函数的第三个参数均为可选参数,该可选参数名是isCopy,它让调用者确定返回的C字符串地址指向副本还是指向堆中固定的对象。

const jbyte* str;
jboolean iscopy;
str=(*env)->GetStringUTFChars(env,javastring,&isCopy);
if(0!=str){
    printf("java string: %s",str);
    if(JNI_TRUE==isCopy)
    {
        printf("C string is  a copy of the java string.");
    }
    else
    {
        printf("C string is  a copy of the java string.");
    }
}

划重点----->:通过JNI GetStringChars函数和GetStringUTFChars函数获得的C字符串在原生代码中使用完之后需要正确释放,否则会引起内存泄露。JNI提供ReleaseStringChars函数释放Unicode编码格式的字符串。而用ReleaseStringUTFChars函数释放Unicode编码格式的字符串。

(*env)->ReleaseStringUTFChars(env,javaString,str);

其他相关内容将在后续博客中更新。

动态链接库的编写

NDK和Cmake插件安装

新建一安卓项目,勾选include C++ support.

一路next到结束。工程构建好后,打开File-->Setting-->Android SDK-->SDK Tools,勾选Cmake和NDK。

打开的工程结构,会看到有一个native-lib.cpp和cmakeList.txt。分别是代码文件和配置文件。接下来就是我们的编写过程。

代码编写

程序设计思路是,输入一串字符,然后点击按钮去判断输入的正确与否。判断程序就是使用原生代码实现,根据返回值弹出提示正确还是错误。程序界面如下:

新建类,用来存放check类的调用。

先编写Mainactivity中的代码。因为在界面中直接使用了android:onClick="check"。所以直接编写check按钮函数。

新建Check原生代码函数。

此时程序结构如下:

原生代码编写,为了写函数方便,我们可以将native-lib.cpp中的代码粘贴过来进行修改。

CmakeList.txt配置文件更改

TODO 设置构建本机库文件所需的 CMake的最小版本
cmake_minimum_required(VERSION 3.4.1)

TODO 添加自己写的 C/C++源文件

add_library( Check

         SHARED
         src/main/cpp/Check.cpp )

TODO 依赖 NDK中的库

find_library( log-lib

          log )

TODO 将目标库与 NDK中的库进行连接

target_link_libraries( Check

                   ${log-lib} )

配置结束后就可以运行,看下效果。


So文件的查看

将apk拷贝出来,解压查看lib文件


总结

这是一次简单的尝试,本想把so文件的调试也写在后面,但篇幅就太长了,待日后再更新。

个人拙见,如有错误可指正,欢迎留言交流。

参考链接

https://www.52pojie.cn/thread-704056-1-1.html
https://blog.csdn.net/jiangwei0910410003/article/details/51500328
https://www.cnblogs.com/liushilin/p/6292263.html#_label2
https://www.jianshu.com/p/6332418b12b1
https://blog.csdn.net/zeqiao/article/details/77893167