使用Conan和C++ Boost库的Android Studio项目
在之前的博文中使用Conan将Boost C++库交叉编译到Android,我们介绍了如何将Boost C++库交叉编译到Android以及如何将它们上传到Conan服务器。这样,我们组织中的开发人员就可以重复使用它们,而无需再次从源代码构建相同的二进制文件。
在本篇博文中,我们将介绍如何在Android Studio项目中使用这些预构建的Boost库。
注意:本博文假设您已阅读过上一篇博文,并且已
- 为目标armveabi-v7、api级别21和clang创建配置文件:
~/.conan/profiles/android_21_armeabi-v7_clang
- Boost包已上传到远程服务器(或本地Conan缓存)。
- 已安装Conan客户端,并配置了指向我们上传包的服务器的远程连接(如果使用本地Conan缓存则无需此步骤)。
打开Android Studio并创建一个名为MyBoostApp的新应用程序,包括C++支持。
选择API级别(我们使用21),以匹配用于交叉编译Boost库的级别。
创建一个空活动。
并将其命名为MainActivity。
最后,在向导的最后一个对话框中,选择默认值。
切换到项目视图。
并在app文件夹中创建一个包含以下内容的conanfile.txt
文件。
[requires]
boost/1.66.0@conan/stable
[generators]
cmake
打开app文件夹中的CMakeLists.txt
文件,并将其内容替换为
cmake_minimum_required(VERSION 3.4.1)
include(${CMAKE_CURRENT_SOURCE_DIR}/conan_build/conanbuildinfo.cmake)
set(CMAKE_CXX_COMPILER_VERSION "5.0") # Unknown miss-detection of the compiler by CMake
conan_basic_setup(TARGETS)
add_library(native-lib SHARED src/main/cpp/native-lib.cpp)
target_link_libraries(native-lib CONAN_PKG::boost)
上面的CMakeLists.txt
文件
-
包含文件“conanbuildinfo.cmake”。此文件将由Conan生成,并包含有关如何链接依赖项的信息,在本例中为Boost,但我们可以在
conanfile.txt
中添加其他依赖项。 -
添加一个名为native-lib的原生库,并将其与Boost库链接。
打开app/build.gradle
文件。
-
在android/defaultConfig部分,指定使用armeabi-v7a和c++_shared标准库。
ndk { abiFilters 'armeabi-v7a' stl 'c++_shared' }
-
在android/defaultConfig/externalNativeBuild部分添加-DANDROID_STL=c++shared参数,强制CMake使用正确的STL。
externalNativeBuild { cmake { cppFlags "" arguments "-DANDROID_STL=c++_shared" } }
最后,我们在android块之后添加了一个新任务,该任务将调用Conan来获取所需的Boost包并生成提到的conanbuildinfo.cmake
文件。
我们将使用在上一篇博文中创建的android_21_armeabi-v7a_clang配置文件为ARM构建我们的应用程序,对应于指定的abiFilter:armeabi-v7a。
task conanInstall {
def buildDir = new File("app/conan_build")
buildDir.mkdirs()
// if you have problems running the command try to specify the absolute
// path to conan (Known problem in MacOSX) /usr/local/bin/conan
def conan_path = ""; // "/usr/local/bin/"
def cmd = conan_path + "conan install ../conanfile.txt --profile android_21_armeabi-v7a_clang"
print(">> ${cmd} \n")
def sout = new StringBuilder(), serr = new StringBuilder()
def proc = cmd.execute(null, buildDir)
proc.consumeProcessOutput(sout, serr)
proc.waitFor()
println "$sout $serr"
if(proc.exitValue() != 0){
throw new Exception("out> $sout err> $serr" + "\nCommand: ${cmd}")
}
}
完整的build.gradle
文件应如下所示。
apply plugin: 'com.android.application'
android {
compileSdkVersion 26
apply plugin: 'com.android.application'
android {
compileSdkVersion 26
defaultConfig {
applicationId "com.jfrog.myboostapp"
minSdkVersion 21
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
cppFlags ""
arguments "-DANDROID_STL=c++_shared"
}
}
ndk {
abiFilters 'armeabi-v7a'
stl 'c++_shared'
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
}
task conanInstall {
def buildDir = new File("app/conan_build")
buildDir.mkdirs()
// if you have problems running the command try to specify the absolute
// path to conan (Known problem in MacOSX) /usr/local/bin/conan
def conan_path = "" // "/usr/local/bin/"
def cmd = conan_path + "conan install ../conanfile.txt --profile android_21_armeabi-v7a_clang"
print(">> ${cmd} \n")
def sout = new StringBuilder(), serr = new StringBuilder()
def proc = cmd.execute(null, buildDir)
proc.consumeProcessOutput(sout, serr)
proc.waitFor()
println "$sout $serr"
if(proc.exitValue() != 0){
throw new Exception("out> $sout err> $serr" + "\nCommand: ${cmd}")
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:26.1.0'
implementation 'com.android.support.constraint:constraint-layout:1.0.2'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
}
打开app/src/main/cpp/native-lib.cpp
中的默认示例cpp库,并包含一些使用库的代码行。如果在向导中使用了其他应用程序名称,请注意JNICALL名称。
#include <jni.h>
#include <string>
#include <boost/regex.hpp>
#include <boost/version.hpp>
#include <sstream>
extern "C"
JNIEXPORT jstring
JNICALL
Java_com_jfrog_myboostapp_MainActivity_stringFromJNI(
JNIEnv *env,
jobject /* this */) {
std::string line;
std::stringstream output;
std::string sentence_with_space = "Boost Rules";
output << "Using Boost " << (BOOST_VERSION / 100000) << "." << ((BOOST_VERSION / 100) % 1000) << "." << (BOOST_VERSION % 100) << std::endl;
boost::regex expr("\\w+\\s\\w+");
if (boost::regex_match(sentence_with_space, expr)) {
output << "The regex matches! '" << sentence_with_space << "'" << std::endl;
}
else{
output << "The regex doesn't match!" << std::endl;
}
return env->NewStringUTF(output.str().c_str());
}
然后在ARM模拟器上运行应用程序,这需要一段时间,因为模拟ARM速度很慢。
加速模拟和构建多配置应用程序
运行ARM架构的Android模拟器非常慢,因此如果我们想测试或调试我们的应用程序,最好将应用程序构建到Android/x86。此外,应用程序可以打包为不同的架构分发,因此我们将针对更多架构重复我们在第一篇博文中看到的步骤。
-
为x86构建另一个独立的工具链。
python make_standalone_toolchain.py --arch=x86 --api=21 --stl=libc++ --install-dir=/myfolder/x86_21_toolchain
-
创建一个新的配置文件
.conan/profiles/android_21_x86_clang
,以针对x86的新工具链。standalone_toolchain=/myfolder/x86_21_toolchain target_host=i686-linux-android cc_compiler=clang cxx_compiler=clang++ [settings] compiler=clang compiler.version=5.0 compiler.libcxx=libc++ os=Android os.api_level=21 arch=x86 build_type=Release [env] CONAN_CMAKE_FIND_ROOT_PATH=$standalone_toolchain/sysroot PATH=[$standalone_toolchain/bin] CHOST=$target_host AR=$target_host-ar AS=$target_host-as RANLIB=$target_host-ranlib CC=$target_host-$cc_compiler CXX=$target_host-$cxx_compiler LD=$target_host-ld STRIP=$target_host-strip CFLAGS= -fPIC -I$standalone_toolchain/include/c++/4.9.x CXXFLAGS= -fPIC -I$standalone_toolchain/include/c++/4.9.x LDFLAGS=
-
在
app/build.gradle
文件中将x86添加到abiFilters,以便也为x86构建应用程序。ndk { abiFilters 'armeabi-v7a', 'x86' stl 'c++_shared' }
-
调整任务conanInstall,使其迭代所有声明的abiFilters并使用不同的配置文件安装多个版本的Boost,此新版本的任务是通用的,对我们可能添加到abiFilters的任何架构都适用。
task conanInstall { android.defaultConfig.ndk.abiFilters.each { def arch = it; def buildDir = new File("app/conan_build_" + arch) buildDir.mkdirs() // if you have problems running the command try to specify the absolute // path to conan (Known problem in MacOSX) /usr/local/bin def conan_path = ""; // "/usr/local/bin/" def cmd = conan_path + "conan install ../conanfile.txt --profile android_21_" + arch + "_clang" print(">> ${cmd} \n") def sout = new StringBuilder(), serr = new StringBuilder() def proc = cmd.execute(null, buildDir) proc.consumeProcessOutput(sout, serr) proc.waitFor() println "$sout $serr" if (proc.exitValue() != 0) { throw new Exception("out> $sout err> $serr" + "\nCommand: ${cmd}") } } }
-
此外,调整CMake以根据正在构建的架构查找正确的
conanbuildinfo.cmake
。cmake_minimum_required(VERSION 3.4.1) include(${CMAKE_CURRENT_SOURCE_DIR}/conan_build_${ANDROID_ABI}/conanbuildinfo.cmake) set(CMAKE_CXX_COMPILER_VERSION "5.0") # Unknown miss-detection of the compiler by CMake conan_basic_setup(TARGETS) add_library(native-lib SHARED src/main/cpp/native-lib.cpp) target_link_libraries(native-lib CONAN_PKG::boost)
-
为配置创建一个新的Conan包。
conan install boost/1.66.0@conan/stable --build missing --profile=android_21_x86_clang
-
如果要与团队共享二进制文件,请将其上传到服务器。
conan upload boost/1.66.0@conan/stable --all -r=myremote conan upload zlib* --all -r=myremote conan upload bzip2* --all -r=myremote # or just conan upload * --all -r=myremote
-
在x86模拟器上运行应用程序!
现在您的应用程序在abiFilters中接受更多架构,例如arm64-v8a以针对armv8,请按照相同的步骤创建独立工具链和新的配置文件android_21_arm64-v8a_clang。
构建(手动或由CI服务器)团队将使用的所有包并将其上传到服务器将节省大量开发工作流程时间。
您可以在此存储库中找到示例项目。