Android studio生成so文件
一、简介
现在,大部分安卓app的开发几乎都是使用java完成,但是,Java在速度和一些功能上可能不如c/c++程序,并且一些好的开源项目是使用c/c++开发完成的,为了使用这一功能,android拓展了jni,提供这一服务。
二、 NDK
2.1 理解ndk
在Android OS上开发应用程序,Google提供了两种开发包:SDK和NDK,Android 平台从一开就已经支持了C/C++了。我们知道Android的SDK主要是基于Java的,所以导致了在用Android SDK进行开发的工程师们都必须使用Java语言。不过,Google从一开始就说明Android也支持JNI编程方式,也就是第三方应用完成可以通过JNI调用自己的C动态度。于是NDK就应运而生了。
2.2 为何ndk
1、在平台之间移植其应用
2、重复使用现在库,或者提供其自己的库重复使用
3、在某些情况下提性能,特别是像游戏这种计算密集型应用
4、使用第三方库,现在许多第三方库都是由C/C++库编写的,比如Ffmpeg这样库。
5、不依赖于Dalvik Java虚拟机的设计
6、代码的保护。由于APK的Java层代码很容易被反编译,而C/C++库反编译难度大
2.3 从NDK到so

从上图这个Android系统框架来看,我们上层通过JNI来调用NDK层的,使用这个工具可以很方便的编写和调试JNI的代码。因为C语言的不跨平台,在Mac系统的下使用NDK编译在Linux下能执行的函数库——so文件。其本质就是一堆C、C++的头文件和实现文件打包成一个库。目前Android系统支持以下七种不用的CPU架构,每一种对应着各自的应用程序二进制接口ABI:(Application Binary Interface)定义了二进制文件(尤其是.so文件)如何运行在相应的系统平台上,从使用的指令集,内存对齐到可用的系统函数库。对应关系如下:
- ARMv5——armeabi
- ARMv7 ——armeabi-v7a
- ARMv8——arm64- v8a
- x86——x86
- MIPS ——mips
- MIPS64——mips64
- x86_64——x86_64
三、JNI
3.1 解释jni
JNI,全称为Java Native Interface,即Java本地接口,JNI是Java调用Native 语言的一种特性。通过JNI可以使得Java与C/C++机型交互。即可以在Java代码中调用C/C++等语言的代码或者在C/C++代码中调用Java代码。由于JNI是JVM规范的一部分,因此可以将我们写的JNI的程序在任何实现了JNI规范的Java虚拟机中运行。同时,这个特性使我们可以复用以前用C/C++写的大量代码JNI是一种在Java虚拟机机制下的执行代码的标准机制。代码被编写成汇编程序或者C/C++程序,并组装为动态库。也就允许非静态绑定用法。这提供了一个在Java平台上调用C/C++的一种途径,反之亦然。
PS:
开发JNI程序会受到系统环境限制,因为用C/C++ 语言写出来的代码或模块,编译过程当中要依赖当前操作系统环境所提供的一些库函数,并和本地库链接在一起。而且编译后生成的二进制代码只能在本地操作系统环境下运行,因为不同的操作系统环境,有自己的本地库和CPU指令集,而且各个平台对标准C/C++的规范和标准库函数实现方式也有所区别。这就造成了各个平台使用JNI接口的Java程序,不再像以前那样自由的跨平台。如果要实现跨平台, 就必须将本地代码在不同的操作系统平台下编译出相应的动态库。
3.2 为何jni
可以使用Java代码和C/C++进行交互,与其它类似接口Microsoft的原始本地接口等相比,JNI的主要竞争优势在于:它在设计之初就确保了二进制的兼容性,JNI编写的应用程序兼容性以及其再某些具体平台上的Java虚拟机兼容性(当谈及JNI时,这里并不特比针对Davik虚拟机,JNI适用于所有JVM虚拟机)。这就是为什么C/C++编译后的代码无论在任何平台上都能执行。不过,一些早期版本并不支持二进制兼容。二进制兼容性是一种程序兼容性类型,允许一个程序在不改变其可执行文件的条件下在不同的编译环境中工作。
3.3 工作原理


JNI是作为一个桥梁的作用,链接java和c/c++
3.4 命名
例子
1 | JNIExport jstring JNICALL Java_com_example_hellojni_MainActivity_stringFromJNI( JNIEnv* env,jobject thiz ) |
jstring 是返回值类型
Java_com_example_hellojni 是包名
MainActivity 是类名
stringFromJNI 是方法名
其中**JNIExport和JNICALL**是不固定保留的关键字不要修改
3.5 JNI开发流程
- 第1步:在Java中先声明一个native方法
- 第2步:编译Java源文件javac得到.class文件
- 第3步:通过javah -jni命令导出JNI的.h头文件
- 第4步:使用Java需要交互的本地代码,实现在Java中声明的Native方法(如果Java需要与C++交互,那么就用C++实现Java的Native方法。)
- 第5步:将本地代码编译成动态库(Windows系统下是.dll文件,如果是Linux系统下是.so文件,如果是Mac系统下是.jnilib)
- 第6步:通过Java命令执行Java程序,最终实现Java调用本地代码。
PS:javah 是JDK自带的一个命令,-jni参数表示将class 中用到native 声明的函数生成JNI 规则的函数

四、实现一个JNI例子
4.1 创建app
打开android studio,新建一个project, Create Android Project

这里可以勾选Include C++ support, Android studio会给出一个使用JNI的例子。
4.2 创建 测试类
新建一个testso的java类,在里面声明一个native方法
1 | package com.qqq.sotest.sotest; |

注意,loadLibrary的是在CMakeLists.txt设置的library的名字,而不是c文件名
4.3 创建c文件
在app/src/main/cpp/目录下新建一个so-test.c的文件,实现c语言方法,如
1 |
|

创建c文件里面的JNICALL Java_xxxx_xxxx_xxxx必须遵循命名规则。
4.4 添加到CMakeLists.txt
需要把so-test.c文件告诉Android Studio,通过CMake修改
在app/src/main/CMakeLists.txt添加如下内容
1 | add_library( # Sets the name of the library. |
so-lib设置library的名字,在java代码里引用
SHARED将其设为共享的library
src/main/cpp/so-test.c c文件的路径

4.5 构建
点击Build->Make Project构建,或者ctrl+F9构建, 再运行
这样,c/cpp文件就被打包成so文件了。

打包完成后so文件会放在apk的lib/文件夹里

参考链接
Android JNI(一)——NDK与JNI基础:
Android Studio生成so文件的几种方式:
Android studio 编译NDK Android studio 生成.so文件: