在之前的博文中使用Conan将Boost C++库交叉编译到Android,我们介绍了如何将Boost C++库交叉编译到Android以及如何将它们上传到Conan服务器。这样,我们组织中的开发人员就可以重复使用它们,而无需再次从源代码构建相同的二进制文件。

在本篇博文中,我们将介绍如何在Android Studio项目中使用这些预构建的Boost库。

注意:本博文假设您已阅读过上一篇博文,并且已

  • 为目标armveabi-v7、api级别21clang创建配置文件:~/.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-v7ac++_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。此外,应用程序可以打包为不同的架构分发,因此我们将针对更多架构重复我们在第一篇博文中看到的步骤。

  1. x86构建另一个独立的工具链。

     python make_standalone_toolchain.py --arch=x86 --api=21 --stl=libc++ --install-dir=/myfolder/x86_21_toolchain
    
  2. 创建一个新的配置文件.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=
    
  3. app/build.gradle文件中将x86添加到abiFilters,以便也为x86构建应用程序。

     ndk {
        abiFilters 'armeabi-v7a', 'x86'
        stl 'c++_shared'
     }
    
  4. 调整任务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}")
            }
        }
     }
    
  5. 此外,调整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)
    
  6. 为配置创建一个新的Conan包。

     conan install boost/1.66.0@conan/stable --build missing --profile=android_21_x86_clang
    
  7. 如果要与团队共享二进制文件,请将其上传到服务器。

     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
    
  8. 在x86模拟器上运行应用程序!

现在您的应用程序在abiFilters中接受更多架构,例如arm64-v8a以针对armv8,请按照相同的步骤创建独立工具链和新的配置文件android_21_arm64-v8a_clang

构建(手动或由CI服务器)团队将使用的所有包并将其上传到服务器将节省大量开发工作流程时间。

您可以在此存储库中找到示例项目。