一 检测库和执行程序能否在Android上用
1.1 我们知道Cmake不止能编译Linux库程序,也能编译出其它系统的库,如windows,ios和android等,那么上一篇生成的Linux的库程序能否直接用于Android上呢,下面先来做个测试。
1.2 我们先在Linux平台生成一个最简单的C语言可执行程序,main.c
创建文件
touch main.c
修改文件,文本编辑器(如vi/vim、nano等)或图形界面编辑器(如gedit、kate等)打开并修改文件内容,ctrl+x保存退出
nano main.c
#include <stdio.h>
int main(){
printf("Hello Word\n");
return 0;
}
生成执行程序
gcc main.c -o appexe
运行程序,可以看到正常输出文字
$ ./appexe
Hello Word
1.3 看执行程序能否在Android上运行。Android程序需要用到ADB命令,所以使用ADB工具来测试,提前先在Linux或者Windows上安装好ADB工具
1.4 如果ADB安装在Windows,那么可以把库复制到windows上来测试,下面以Windows为例,我现在D盘已经有了个Linux生成的appexe执行程序
1.5 adb调试,先Andoird机打开开发者选项来连接工具,用adb devices查看是否连接成功。出现设备说明连接成功
>adb devices
List of devices attached
10AC741Q7K000NG device
1.6 Win+R打开终端,用adb push命令来把文件移到Android系统上,因为Andoird其它也是Linux内核,所以是可以直接执行该程序的
adb push D:\VSProject\cmaketest4\appexe /sdcard
1.7 用shell工具来测试,打开shell
adb shell
1.8 进入sdcard 目录
$ cd sdcard/
1.9 根linux一样执行程序方式一样
./appexe
1.10 先报没有权限访问,chmod 需改权限也是不行的
chmod 777 appexe
/sdcard $ ./appexe
/system/bin/sh: ./appexe: can't execute: Permission denied
1.11 需要修改放执行程序的目录为临时目录
adb push D:/VSProject/cmaketest4/appexe /data/local/tmp/
1.12 再次执行上面步骤
/data/local/tmp $ chmod 777 appexe
/data/local/tmp $ ./appexe
11.12 错误一,这时候看到错误信息变了,说不能执行ELF文件(这是因为非交叉编译的执行文件,不能用在Android系统上),那么下面就看怎样交叉编译
/data/local/tmp $ ./appexe
/system/bin/sh: ./appexe: not executable: 64-bit ELF file
二 AndroidNDK生成可执行程序
2.1 上面已经测试Linux生成的库程序是不能在Android上用的,那么怎样才能让Android也能使用呢,这就需要用到CMake交叉编译,交叉编译可以理解为跨平台编译,来实现库的跨平台使用。
2.2 由于C++编译器的不同,所以生成的库不能跨平台。想要生成Andorid能用的库就需要Andorid平台的C++编译工具。这个工具就是AndroidNDK,NDK下载官网链接:
https://developer.android.google.cn/ndk/downloads?hl=zh-cn
下载得到压缩包zip或tar或tar.gz
首先进入要解压的文件夹,我这是/home
cd /home
我压缩文件放在/mnt/d 目录下,所以解压命令是
tar -xvf /mnt/d/android-ndk-r17c.tar
这时候NDK的完整安装路径就是 /home/android-ndk-r17c,下面就配置NDK环境变量。
打开vim编辑器,输入命令字母“o”,切换为插入模式(--INSERT--),可以在光标末尾插入新行对文件进行修改
# sudo vim /etc/profile
在末尾添加NDK的安装路径
export ANDROID_NDK=/home/android-ndk-r17c
export PATH=$PATH:$ANDROID_NDK
保存并退出,按 esc 键,退出插入模式即可进入命令模式。在末尾输入 :wq ,回车即可写入保存并离开 。或者按大写ZZ也可以快捷保存退出
刷新使配置生效
source /etc/profile
或
. /etc/profile
或
source ~/.bashrc
查看环境变量是否有NDK路径
echo $PATH
验证配置是否成功
ndk-build -v
成功会打印如下信息:
$ ndk-build -v
GNU Make 3.81
Copyright (C) 2006 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
This program built for x86_64-pc-linux-gnu
2.3 我们知道CMake只是构建工具,真正执行编译的是C++编译器,Linux平台的C++编译器是gcc,所以Linux的编译命令是:
gcc main.c -o appexe
那么Android平台的编译工具在哪呢,就在NDK的toochins目录下
/home/android-ndk-r17c/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-gcc main.c -o appexe
这个路径从哪来的呢,那就从NDK里面找支持的C++编译器,我的NDK安装在/home/android-ndk-r17c目录下,那么就一级一级找,如下所示
上面路径太长了,每次输入容易出错,那么就可以定义一个环境变量,怎样定义参考NDK环境变量设置
export ANDROID_NDK_GCC="/home/android-ndk-r17c/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-gcc"
简化后的指令就变为,后面CMake也要用到了
$ANDROID_NDK_GCC main.c -o appexe
2.4 错误二,提示找不到文件头
$ $ANDROID_NDK_GCC main.c -o appexe
main.c:1:19: fatal error: stdio.h: No such file or directory
#include <stdio.h>
^
compilation terminated.
配置 --sysroot 环境变量(--sysroot 自动寻找头文件,库文件)。
寻找库文件位置 ,可以看到里面全部的库
寻找头文件位置 ,可以看到里面全部的头文件
那配置库和头文件环境变量的路径就是
export SYSROOT="--sysroot=$ANDROID_NDK/platforms/android-21/arch-arm -isystem $ANDROID_NDK/sysroot/usr/include"
查看是否设置成功
$ echo $SYSROOT
--sysroot=/home/android-ndk-r17c/platforms/android-21/arch-arm -isystem /home/android-ndk-r17c/sysroot/usr/include
再次编译
$ANDROID_NDK_GCC $SYSROOT main.c -o appexe
2.5 出现第三个错误,意思执行过程中找不到 ams(汇编)支持
$ $ANDROID_NDK_GCC $SYSROOT main.c -o appexe
In file included from /home/android-ndk-r17c/sysroot/usr/include/sys/types.h:36:0,
from /home/android-ndk-r17c/sysroot/usr/include/stdio.h:42,
from main.c:1:
/home/android-ndk-r17c/sysroot/usr/include/linux/types.h:21:23: fatal error: asm/types.h: No such file or directory
#include <asm/types.h>
^
compilation terminated.
寻找asm路径
再次配置$SYSROOT的路径
$ export SYSROOT="--sysroot=$ANDROID_NDK/platforms/android-21/arch-arm -isystem $ANDROID_NDK/sysroot/usr/include -isystem $ANDROID_NDK/sysroot/usr/include/arm-linux-androideabi"
再次执行编译,不再报错,可以成功生成appex执行程序
$ $ANDROID_NDK_GCC $SYSROOT main.c -o appexe
$ ls
appexe main.c
2.6 把文件按开头方法导入Android手机运行测试
C:\Users\dong>adb push D:/VSProject/cmaketest4/appexe /data/local/tmp/
D:/VSProject/cmaketest4/appexe: 1 file pushed, 0 skipped. 5.8 MB/s (6436 bytes in 0.001s)
C:\Users\dong>adb shell
PD2219:/ $ cd /data/local/tmp/
PD2219:/data/local/tmp $ ls
appexe device-explorer sky.com.example.flutter_app.sha1
com.wnxds.tataim-build-id.txt lldb-server start_lldb_server.sh
databasesfinance.db perfd
databasesfinance.db-wal sky.com.dongfangdashu.tangshu.flutterproject.sha1
PD2219:/data/local/tmp $ chmod 777 appexe
PD2219:/data/local/tmp $ ./appexe
"./appexe": error: Android 5.0 and later only support position-independent executables (-fPIE).
2.7 adb运行最后一个错误,需要pie文件
/data/local/tmp $ ./appexe
"./appexe": error: Android 5.0 and later only support position-independent executables (-fPIE).
那就在末尾添加pie重新生成执行程序
$ $ANDROID_NDK_GCC $SYSROOT -pie main.c -o appexe
再次用adb测试,终于成功了,记录这一重要时刻
PD2219:/data/local/tmp $ rm -f appexe
PD2219:/data/local/tmp $ exit;
C:\Users\dong>adb push D:/VSProject/cmaketest4/appexe /data/local/tmp/
D:/VSProject/cmaketest4/appexe: 1 file pushed, 0 skipped. 14.1 MB/s (6488 bytes in 0.000s)
C:\Users\dong>adb shell
PD2219:/ $ cd /data/local/tmp/
PD2219:/data/local/tmp $ chmod 777 appexe
PD2219:/data/local/tmp $ ./appexe
Hello Word
2.8 NDK环境变量配置总结,要完成NDK对C/C++的编译至少需要配置以下环境变量
#NDK环境变量
export ANDROID_NDK=/home/android-ndk-r17c
#GCC环境变量
export ANDROID_NDK_GCC="/home/android-ndk-r17c/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-gcc"
#头文件,库文件环境变量
export SYSROOT="--sysroot=$ANDROID_NDK/platforms/android-21/arch-arm -isystem $ANDROID_NDK/sysroot/usr/include -isystem $ANDROID_NDK/sysroot/usr/include/arm-linux-androideabi"
#so库架构环境变量
export ANDROID_NDK_AR="/home/android-ndk-r17c/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin/aarch64-linux-android-ar"
三 Android 三方so库的导入
3.1 先用androidStuiod的Native项目生成一个so库
创建所需文件
calc.h
#include "include/calc.h"
int Calc::add(int a, int b) {
return a+b;
}
calc.cpp
#include "include/calc.h"
int Calc::add(int a, int b) {
return a+b;
}
calclib.cpp
#include <jni.h>
#include <string>
#include <filesystem>
#include "calc.h"
extern "C" JNIEXPORT jstring JNICALL
Java_com_bob_nativelib_CalcTools_stringFromJNI(
JNIEnv* env,
jobject) {
Calc calcclass;
int value = (calcclass.add(10, 10));
std::string valueString = std::to_string(value);
std::string hello = "相加的值是:" + valueString;
// std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
CMakeLists.txt
cmake_minimum_required(VERSION 3.10.2)
project("calclib")
#导入头文件
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include/)
add_library(
calclib
SHARED
#所有源文件
calc.cpp
calcjni.cpp)
target_link_libraries(
calclib)
CalcTools.java
package com.bob.nativelib;
public class CalcTools {
static {
System.loadLibrary("calclib");
}
public native String stringFromJNI();
}
MainActivity.java
public class MainActivity extends AppCompatActivity {
private TextView tvText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//调用函数库
CalcTools calcTools=new CalcTools();
Log.e("EEE", calcTools.stringFromJNI() );
}
}
成功会输出
相加的值是:20
此时在build - cmake - debug - obj 目录下会生成对应架构的so库文件
3.2 上面我们已经生成了so库,那么我们怎样引用这些so库,来进行JNI交互呢。
先新建一个module,导入该so库和头文件到我们java项目里面
3.2 在build.gradle里面增加相关配置
plugins {
id 'com.android.library'
}
android {
namespace 'com.xixia.jincmakelib'
compileSdk 33
defaultConfig {
minSdk 21
targetSdk 33
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
externalNativeBuild {
cmake {
cppFlags ""
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
externalNativeBuild {
cmake {
path "src/main/cpp/CMakeLists.txt"
version "3.22.1"
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'com.google.android.material:material:1.5.0'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}
4.3 CMakeLists.txt 配置修改如下:
cmake_minimum_required(VERSION 3.22.1)
project("jincmakelib")
#导入库文件开始---------------------------------------------
#导入头文件
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include/)
#导入库文件
add_library(
calclib
SHARED
IMPORTED)
# 指定库的路径
set_target_properties(
calclib
PROPERTIES IMPORTED_LOCATION
${CMAKE_CURRENT_SOURCE_DIR}/${CMAKE_ANDROID_ARCH_ABI}/libcalclib.so
)
#导入库文件结束---------------------------------------------
add_library(
jincmakelib
SHARED
jincmakelib.cpp)
find_library(
log-lib
log)
target_link_libraries(
jincmakelib
calclib
${log-lib})
3.4 编写JNI文件 jnicmaketest.cpp,调用动态库函数calc.so
#include <jni.h>
#include <string>
#include <calc.h>
extern "C" JNIEXPORT jstring JNICALL
Java_com_xixia_jincmakelib_NativeCmakeLib_stringFromJNI(
JNIEnv* env,
jobject /* this */) {
Calc calcclass;
int value = (calcclass.add(10, 10));
std::string valueString = std::to_string(value);
std::string hello = "相加的值是:" + valueString;
//std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
3.5 编写Java层接口NativeCmakeLib.java,加载jni库
package com.xixia.jincmakelib;
public class NativeCmakeLib {
static {
System.loadLibrary("jincmakelib");
System.loadLibrary("calclib");
}
public native String stringFromJNI();
}
至此JNI层就写好了,下面写java层来调用native层,注意so库名字一定要在前面加上lib,不然会报找不到so库错误
比如源来是这样写的
set_target_properties(
calclibPROPERTIES IMPORTED_LOCATION
${CMAKE_CURRENT_SOURCE_DIR}/x86_64/calc.so
)
编译可以通过,但运行可能就会报错
java.lang.UnsatisfiedLinkError: dlopen failed: library "libcalclib.so" not found
at java.lang.Runtime.loadLibrary0(Runtime.java:1016)
at java.lang.System.loadLibrary(System.java:1669)
at com.xixia.jincmakelib.NativeCmakeLib.<clinit>(NativeCmakeLib.java:6)
按照提示改为libcalclib.so,当然也可能与编译有关,按提示名字改就行了
set_target_properties(
calclibPROPERTIES IMPORTED_LOCATION
${CMAKE_CURRENT_SOURCE_DIR}/x86_64/libcalclib.so
)
3.6 java层调用JNI层,MainActivity.java
public class MainActivity extends AppCompatActivity {
private TextView tvText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvText = (TextView) findViewById(R.id.tv_text);
//Cmake测试so库
NativeCmakeLib testCallBack = new NativeCmakeLib();
String cpuArchitecture = Build.CPU_ABI; // 获取当前设备的 CPU 架构
StringBuilder stringBuilder=new StringBuilder();
stringBuilder.append("CPU架构:"+cpuArchitecture).append("\n");
stringBuilder.append(testCallBack.stringFromJNI());
tvText.setText(stringBuilder);
}
}
能输出信息说明就成功了
CPU架构:x86_64
相加的值是:20
3.7 编译过程可能遇到的错误:
错误1:
Build command failed.
Error while executing process D:\AndroidSdk\cmake\3.22.1\bin\ninja.exe with arguments {-C D:\AndroidProject\MyApplication\jincmakelib\.cxx\Debug\4r5q3z6v\x86_64 jincmakelib}
ninja: Entering directory `D:\AndroidProject\MyApplication\jincmakelib\.cxx\Debug\4r5q3z6v\x86_64'ninja: error: 'src/x86_64/libcalclib.so', needed by 'D:/AndroidProject/MyApplication/jincmakelib/build/intermediates/cxx/Debug/4r5q3z6v/obj/x86_64/libjincmakelib.so', missing and no known rule to make it
报这样错误可能就是so文件路径没写对,比如我随便写个路径,找不断该路径就会报这错误
# 指定库的路径
set_target_properties(
calclib
PROPERTIES IMPORTED_LOCATION
src/x86_64/libcalclib.so
)
这种把路径写对就可以了,相对路径,绝对路径都可以
# 指定库的路径
set_target_properties(
calclib
PROPERTIES IMPORTED_LOCATION
${CMAKE_CURRENT_SOURCE_DIR}/${CMAKE_ANDROID_ARCH_ABI}/libcalclib.so
)
错误2:
2 files found with path 'lib/x86_64/libcalclib.so' from inputs:
- D:\AndroidProject\MyApplication\jincmakelib\build\intermediates\merged_jni_libs\debug\out\x86_64\libcalclib.so
- D:\AndroidProject\MyApplication\jincmakelib\build\intermediates\cxx\Debug\4r5q3z6v\obj\x86_64\libcalclib.so
If you are using jniLibs and CMake IMPORTED targets, see
https://developer.android.com/r/tools/jniLibs-vs-imported-targets
会提示重复so库文件,这是因为我们可能放so库在jniLibs目录下,如下
# 指定库的路径
set_target_properties(
calclib
PROPERTIES IMPORTED_LOCATION
../jniLibs/x86_64/libcalclib.so
)
这个目录是自动加载so库的,我们在CMakelist.txt 里面又导入了一个,就会重复,所以我们可以放在另一个任意地方,不要放在jniLibs目录下。
# 指定库的路径
set_target_properties(
calclib
PROPERTIES IMPORTED_LOCATION
${CMAKE_CURRENT_SOURCE_DIR}/${CMAKE_ANDROID_ARCH_ABI}/libcalclib.so
)
四 AndroidNDK编译静态动态库
4.1 上面用androidStudio环境生成so库可以正常调用,那在linux环境编译出的库文件能否被android使用呢,下面来测试下
4.2 同样在cmaketest2文件夹下创建test.h和test.cpp文件
test.h
#ifndef HEAD_H
#define HEAD_H
int add(int a,int b);
#endif
test.cpp
#include "test.h"
int add(int a, int b) {
return a+b;
}
main.cpp测试程序
#include <string>
#include "test.h"
int main(){
int value = add(10, 10);
printf("%d\n",value);
return 0;
}
先用gcc编译器测试main.cpp是否运行正常,看到可以正常输出20
cmaketest2$ gcc test.cpp main.cpp -o mainexe
cmaketest2$ ./mainexe
20
4.3 用AndroidNDK编译动态库,编译文成会在cmaketest2文件夹里面生成一个libcalc.so库文件
$ANDROID_NDK_GCC $SYSROOT -shared -fPIC test.cpp -o libcalc.so
4.4 用AndroidNDK编译静态库
静态库需要用到另一个编译类型,所以需要再配置一个环境变量
查找 arm-linux-androideabi-ar 位置
配置环境变量
export ANDROID_NDK_AR="/home/android-ndk-r17c/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin/aarch64-linux-android-ar"
将这源文件编译成目标文件,生成test.o文件
$ANDROID_NDK_GCC -c test.cpp
接下来使用ar命令将目标文件打包成一个静态库文件calctlib.a
$ANDROID_NDK_AR rcs -o libcalc.a test.o
把编译出来的so库放进androdStudio里面,按上面第二标题方式编译运行,如果能成功输出信息,说明编译成功
相加的值是:20
五 CMake配置交叉编译
5.1 上面已经用C++编译工具直接编译后的可执行程序演示,那么怎样用CMake进行交叉编译呢?来看下CMake需要增加的配置:
cmake .. -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake \
-DANDROID_NDK=$ANDROID_NKD \
-DANDROID_ABI=arm64-v8a \
-DANDROID_TOOLCHAIN=gcc \
-DANDROID_PLATFORM=android-21 \
-DCMAKE_BUILD_TYPE=debug \
- DCMAKE_TOOLCHAIN_FILE:指定ndk交叉编译链工具的路径,在ndk16版本以上,ndk自带cmake交叉编译工具链。
- DANDROID_NDK:NDK的安装跟目录
- DANDROID_ABI:各大平台:如armeabi-v7a、x86、mips等。android下基本编译一个armeabi-v7a就可以了,当然也可以加一个x86,方便在虚拟机上调试。
- DANDROID_TOOLCHAIN:表示交叉编译链类型,取值gcc或者clang,clang++;gcc已经被废弃
- DANDROID_PLATFORM:定义最低api版本
- DCMAKE_BUILD_TYPE:定义构建类型,取值Debug或Release,Release
执行cmake后编译如下:
$ cmake .. -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake \
-DANDROID_NDK=$ANDROID_NKD \
-DANDROID_ABI=arm64-v8a \
-DANDROID_TOOLCHAIN=gcc \
-DANDROID_PLATFORM=android-21 \
-DCMAKE_BUILD_TYPE=debug \
>
CMake Warning at /home/android-ndk-r17c/build/cmake/android.toolchain.cmake:185 (message):
GCC is deprecated and will be removed in the next release. See
https://android.googlesource.com/platform/ndk/+/master/docs/ClangMigration.md.
Call Stack (most recent call first):
/usr/share/cmake-3.22/Modules/CMakeDetermineSystem.cmake:124 (include)
CMakeLists.txt:2 (project)
-- The C compiler identification is GNU 4.9.0
-- The CXX compiler identification is GNU 4.9.0
-- Detecting C compiler ABI info
CMake Warning at /home/android-ndk-r17c/build/cmake/android.toolchain.cmake:185 (message):
GCC is deprecated and will be removed in the next release. See
https://android.googlesource.com/platform/ndk/+/master/docs/ClangMigration.md.
Call Stack (most recent call first):
/mnt/d/VSProject/cmaketest/build/CMakeFiles/3.22.1/CMakeSystem.cmake:6 (include)
/mnt/d/VSProject/cmaketest/build/CMakeFiles/CMakeTmp/CMakeLists.txt:3 (project)
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /home/android-ndk-r17c/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin/aarch64-linux-android-gcc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
CMake Warning at /home/android-ndk-r17c/build/cmake/android.toolchain.cmake:185 (message):
GCC is deprecated and will be removed in the next release. See
https://android.googlesource.com/platform/ndk/+/master/docs/ClangMigration.md.
Call Stack (most recent call first):
/mnt/d/VSProject/cmaketest/build/CMakeFiles/3.22.1/CMakeSystem.cmake:6 (include)
/mnt/d/VSProject/cmaketest/build/CMakeFiles/CMakeTmp/CMakeLists.txt:3 (project)
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /home/android-ndk-r17c/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin/aarch64-linux-android-g++ - skipped
-- Detecting CXX compile features
CMake Warning at /home/android-ndk-r17c/build/cmake/android.toolchain.cmake:185 (message):
GCC is deprecated and will be removed in the next release. See
https://android.googlesource.com/platform/ndk/+/master/docs/ClangMigration.md.
Call Stack (most recent call first):
/mnt/d/VSProject/cmaketest/build/CMakeFiles/3.22.1/CMakeSystem.cmake:6 (include)
/mnt/d/VSProject/cmaketest/build/CMakeFiles/CMakeTmp/CMakeLists.txt:3 (project)
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /mnt/d/VSProject/cmaketest/build
然后再执行make,可以成功生成so库
build$ make
[ 25%] Building C object CMakeFiles/calc.dir/src/main.c.o
[ 50%] Building C object CMakeFiles/calc.dir/src/test1.c.o
[ 75%] Building C object CMakeFiles/calc.dir/src/test2.c.o
[100%] Linking C shared library ../outpath/lib/libcalc.so
[100%] Built target calc
5.2 脚本进行cmake编译
上面步骤可以写在一个脚本里面,增加可读性,简化控制台流程
第一步放头文件,源文件,脚本文件在一个文件夹里面,如下结构
cmaketest2
├── CMakeLists.txt
├── include
│ └── test.h
└── src├── test.cpp
└── main.cpp
CMakeList.txt 内容:
cmake_minimum_required(VERSION 3.0)
project(cmaketest2)
# 输出库文件目录
set(MYPATH /mnt/d/VSProject/cmaketest2/outpath)
set(LIBRARY_OUTPUT_PATH ${MYPATH}/lib)
# 示例:
set(SOURCE_DIR /mnt/d/VSProject/cmaketest2)
# 包含头文件
include_directories(${SOURCE_DIR}/include)
file(GLOB SRC_LIST ${SOURCE_DIR}/src/*.cpp)
# 包含库路径
# link_directories(${MYPATH}/lib)
# 链接静态库
# link_libraries(calc)
# 启动程序
# add_executable(apptest ${SRC_LIST})
# add_library(calc STATIC ${SRC_LIST})
add_library(calc SHARED ${SRC_LIST})
# 程序启动后链接动态库
# target_link_libraries(apptest calc)
# 输出一般日志信息
# message(STATUS "STATUS Message")
# 输出警告信息
# message(WARNING "WARNING Message")
# 输出错误信息
# message(FATAL_ERROR "FATAL_ERROR Message")
第二步cmaketest项目目录,touch命令 新建 myscript.sh 脚本
touch myscript.sh
第三步打开编辑器
nano myscript.sh
第四步编写脚本内容
#!/bin/bash
rm -r build
mkdir build
cd build
cmake -DCMAKE_SYSTEM_NAME=Android \
-DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake \
-DANDROID_NDK=$ANDROID_NKD \
-DANDROID_ABI=x86_64 \
-DANDROID_TOOLCHAIN=gcc \
-DANDROID_PLATFORM=android-21 \
-DCMAKE_BUILD_TYPE=debug \
..
make
cd ..
第五步编辑好后保存退出
Ctrl + O # 保存文件
Enter # 确认保存
Ctrl + X # 退出编辑器或者关闭当前的shell会话
第六步查看文件权限
ls -l myscript.sh
第七步添加脚本文件权限
chmod +x myscript.sh
第八步执行该脚本
./myscript.sh
注意,可能会报换行错误,找不到文件
$ ./myscript.sh
rm: cannot remove 'build'$'\r': No such file or directory
这时候需要设置编码方式,打开vim文件
vim myscript.sh
设置格式,输入以下指令
: set fileformat=unix
再次输入保存退出指令
: wq
再次执行脚本就可以成功了
$ ./myscript.sh
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /mnt/d/VSProject/cmaketest/build
[ 25%] Building C object CMakeFiles/calc.dir/src/main.c.o
[ 50%] Building C object CMakeFiles/calc.dir/src/test1.c.o
[ 75%] Building C object CMakeFiles/calc.dir/src/test2.c.o
[100%] Linking C static library ../outpath/lib/libcalc.a
[100%] Built target calc
5.3 编译后会生成以下目录
cmaketest2
├── build
├── CMakeLists.txt
├── include
│ └── test.h
├── outpath
│ └── lib
│ └── libcalc.so # 动态库的名字
└── src├── test.cpp
└── main.cpp
把编译出来的so库放进androdStudio里面,按上面第二标题方式编译运行
可以看到也能成功输出
相加的值是:20
六 总结
编译C/C++到Android平台的so库的三种方式:
1,源码编译,把C/C++源码完成的复制到Android的项目里面,Android用jni接口来调用你C/C++代码,这种是最原始最简单的方式,但也伴随着兼容性,安全性,维护性等方面问题,所以一半不采用这种方案。
2,C/C++编译器,在Linux平台通过GCC编译器把C/C++程序编译为目标架构的so库,然后Android导入so库编译运行。
3,Cmake构建编译器脚本,通过简单配置把C/C++编译为so库,供android使用。