Android NDK开发,跨平台构建crypto++动态链接库并部署到Android Stdio中

Android NDK开发,跨平台构建crypto++动态链接库并部署到Android Stdio中
一、基础
  1. 需要在AS中安装NDK、CMake,DDLB一般默认与AS一起安装。
  2. CMake与ndk-build不能同时使用(本文使用的是CMake)。
  3. CMake入门可以看这个:CMake 入门实战 | HaHack
  4. 安卓官网的部分资料: CMake | Android NDK | Android Developers (google.cn) 配置 CMake | Android 开发者 | Android Developers (google.cn)
二、如何新建项目支持C/C++

直接在AS新建项目时选择Native C++,这时项目会自动生成native-lib.cpp并配置好对应的CMakeLists.txt和build.gradle(:app)文件。可以仔细看看这两个文件中的配置,仔细理解,方便后面自己添加cpp源码时应该知道如何配置。

三、如何向已有的项目中添加新的C/C++文件

已有一个纯的Android项目,如何添加C++源代码并构建原生代码库。本质上是将第2节中AS帮我们做了的事情,我们自己做一下。

  1. 按照第二节的布置,自己新建cpp文件夹和CMakeLists.txt文件,并在cpp目录下创建源代码文件

  2. 配置CMake以将原生代码入库:在CMakeLists.txt中添加相应设置

    #指定要求的最低版本
    cmake_minimum_required(VERSION 3.10.2)
    #指定当前项目的名称
    project("ndksecondtry")
    #将native-lib.cpp加入库,参数一是名称(全局唯一即可,一般是name-lib,参数2表示共享库,参数3指定目标cpp文件)
    add_library(  native-lib   SHARED  src/main/cpp/native-lib.cpp)
    
    #specifies the directory that the cpp.head file would be
    include_directories(src/main/cpp/include/)
    
    #use the NDK library
    find_library(log-lib  log)
    #将native-lib和log-lib连接起来
    target_link_libraries(native-lib ${log-lib})
    
  3. 提供CMake或者ndk-build脚本文件的路径以配置Gradle,以将源代码导入Android项目并将SO库打包到应用(修改gradle之后要Sync Project)

    //对比第2节中的gradle代码,可以发现主要修改三个部分
    //在android下,compileSdk 31下面添加
     buildToolsVersion "31.0.0"
    
    //在android.defaultConfig中加
    externalNativeBuild{
                cmake {
                    cppFlags '' //指定cpp的版本
                    abiFilters "arm64-v8a"  //指定要构建的.so的abi类型
                }
            }
    ndk {
        abiFilters "arm64-v8a"  //指定要打包的abi下的.so
    }
    //在android中加
    externalNativeBuild{
            cmake{
                path = file("CMakeLists.txt")//注意我的CMakeLists就在app路径下,如果其他路径需要指明
            }
        }
    
  4. 在native-lib.cpp文件中写下具体的jni代码,并在项目中进行加载调用。

    //我专门写了一个类来加载native代码,需要用的时候直接调用NDKHelper().getSumFromJNI()
    class NDKHelper {
        companion object{
            init {
                System.loadLibrary("native-lib")
            }
        }
    
        external fun getSumFromJNI(a: Int, b: Int ): Int//声明这个函数
    }
    
    //native-lib.cpp
    #include <jni.h>
    #include <string>
    #include <cstdio>
    #include <cstdlib>
    
    using namespace std;
    //函数名是固定格式的
    extern "C"
    JNIEXPORT jint JNICALL
    Java_com_example_ndksecondtry_NDKHelper_getSumFromJNI(JNIEnv *env, jobject thiz, jint a, jint b) {
        return a+b;
    }
    

AS生成的.so文件可以拿出来给其他项目用(可以去app\build\outputs\apk\debug下将.apk文件改为.zip并解压,拿出.so)。

四、利用gcc生成第三方so文件(无法在AS中使用)
  • 生成一个demo的so文件:
  1. 打开C:\MinGW\msys\1.0\msys.bat,出现类似于cmd的界面(如果没有找到,就打开MinGW Installer去安装)。
  2. 将路径转到目标目录下:cd…(我的目标文件下有一个hello.cpp)
  3. 输入:g++ -fPIC -shared -o libhello.so hello.cpp,会在当前目录下生成libhello.so
  4. 给hello写一个头文件,再写一个测试文件
  5. g++ -o test test.cpp -L -m libhello.so 会生成一个a.exe文件(前一个-o test是为了指定输出文件的名称)
  • 生成crypto++的so文件
  1. 打开C:\MinGW\msys\1.0下的msys.bat,出现类似于cmd的界面。
  2. 将路径转到目标目录下:cd…/cryptopp850
  3. make (该步生成的静态库)
  4. make libcryptopp.so (执行该步生成动态库)

remark:gcc生成的.so文件在命令下可以与test.cpp(自己调用.so中的函数接口写的测试程序)一起运行。但是该方法只能生成一个.so文件,而安卓要求为不同类型的cpu配置不同的.so(指令集不一样),所以这样生成的.so文件不能在AS中使用。经过尝试,我们发现可以在g++编译时指定好ABI,并且可以在对应型号的机子上运行,但是这样生成的.so会部署到AS中会出现运行时错误(由于没有调用NDK的工具链,少了一些针对Android的FLAG,没有进一步尝试了)。

五、使用VS生成.so文件并导入AS

利用vs为不同的ABI生成对应的.so文件

  1. 安装vs,安装的时候在工作负载中要选“使用C++的移动开发”
  2. 创建VS项目,选择创建“动态共享库(选择为安卓平台生成动态共享库的项目)”,创建结束后写一个.cpp源码和.h文件
  3. 右击项目,属性,选择“常规”,设置平台工具集:Clang 5.0; 目标API级别:android 27(目前VS中的最高级别); STL的使用:LLVM libc++ 静态库(c++_static)
  4. 右击项目,生成。(注意在release和debug下的生成貌似Android都能用)
  5. 在ARM,ARM64,X86,X64下分别执行3.4步,即可为不同的ABI生成不同的.so文件
5.1 将VS生成的.so文件导入AS
  1. 在当前模块目录下(app)新建文件夹libs用于存放.so 文件。(有的说可以去src/main/下新建文件夹jniLibs来存放.so 文件,并在CMakeLists.txt中指明路径就行,但是我试了不行)

  2. 在libs下建立四个文件夹:arm64-v8a、armeabi-v7a、x86、x86_64,并将前面生成的.so文件直接复制到对应的文件夹下

  3. 在/src/main/cpp下新建文件夹include,并将.so库中用到的头文件全部放到该文件夹下

  4. 修改CMakeLists.txt文件,gradle可以不用改

    #与第2节和第3节中的CMake文件进行对比,主要加上下面的代码(其余已有的部分保持不变)
    #指明头文件放的位置
    include_directories(src/main/cpp/include/)
    
    add_library(  add-lib   SHARED  IMPORTED)
    set_target_properties( add-lib
            PROPERTIES IMPORTED_LOCATION
            ${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libcryptoGenerate.so)
    
    #use the NDK library
    find_library(log-lib  log)
    target_link_libraries(native-lib add-lib ${log-lib})#加上add-lib
    
  5. 在native-lib.cpp中调用

    //这个写法和上面一致
    class NDKHelper {
        companion object{
            init {
                System.loadLibrary("native-lib")
            }
        }
        external fun add(a:Int, b:Int): Int
    }
    
    #include <jni.h>
    #include <string>
    #include <cstdio>
    #include <cstdlib>
    #include <add.h>//注意加上头文件
    using namespace std;
    
    extern "C"
    JNIEXPORT jint JNICALL
    Java_com_example_ndksecondtry_NDKHelper_add(JNIEnv *env, jobject thiz, jint a, jint b) {
        int c = add(a,b);
        return c;
    
    }
    
六、生成Crypto++加密动态连接库(.so)

由于crypto++工程较大,文件较多,直接用前面的几个方法都不适用(归根结底是需要写makefile,主要是编译规则和依赖关系)。makefile有多种,构建工具也很多,如GUNmake,nmake,qmake,makepp,ninja等,不同的工具需要编写的makefile也不尽相同,面对的平台也不同。为了实现跨平台开发,我们选cmake。cmake可以写一个适用于各种平台的CMakeLists.txt,在这个文档中只用指定项目文件的编译规则,无需考虑平台和ABI。然后调用cmake命令可以为不同的平台、不同ABI、不同的make工具生成不同类型的makefile,再由对应的make工具进行编译。

6.1 利用AS生成指定ABI下的.so文件

Android的NDK通过工具链文件支持CMake,其底层调用的make工具是ninja,C++编译工具是clang。为了实现在AS中成功编译出共享库,我们需要写CMakeLists文件。我在github上找到了一份(链接CMake files for Crypto++ project )。注意,crypto++官方不支持Cmake。

  1. 修改CMakelists.txt。可能是因为这个CMakeLists写的比较早了,我用的crypto++源码是最新的(cryptopp850),所以有些地方需要改动。

    • 源码中的TestPrograms文件夹下的代码文件名后缀是.cxx,而CMakeLists中写的是.cpp,需要手动改一下。
    • 如果想让CMakeLists.txt的某些信息在控制台显示,可以用message()并将输出类型指定为SEND_ERROR。详见message — CMake 3.21.2 Documentation
  2. src/main/cpp下新建文件夹crptoppinclude,将crypto++的所有源文件都考至cryptopp下,并将步骤1中修改好的CMakeLists.txt也考至该目录;将crypto++中的所有.h头文件都考至include下。

  3. 在项目的app目录下新建一个顶层的CMakeLists.txt文件,内容如下:

    cmake_minimum_required(VERSION 3.10.2)
    project("ndkfifthtry")
    
    #关联另一个CMake文件
    #设置目标CMake项目的路径
    set(cryptopp_src_DIR src/main/cpp/cryptopp)
    
    #将目标CMake指定为依赖
    add_subdirectory(
            ${cryptopp_src_DIR}#directory of cmake file
    )
    
    #specifies the directory that the cpp.head file would be
    include_directories(src/main/cpp/include/)
    
    add_library(native-lib SHARED src/main/cpp/native-lib.cpp)
    #use the NDK library
    find_library(log-lib log)
    target_link_libraries(native-lib ${log-lib})
    
  4. app/build.gradle中写好配置(和第三节的步骤3一样,但是ABI可以不指定,不指定的话默认生成4中ABI下的),在cpp目录下创建C++源文件native-lib.cpp,内容如下(另外需要向第二节一样在Activity中通过JNI进行调用):

    #include <jni.h>
    #include <stdlib.h>
    #include "aes.h"
    using namespace std;
    using namespace CryptoPP;
    
    extern "C"
    JNIEXPORT jstring JNICALL
    Java_com_example_ndkfifthtry_NDKHelper_cryptoString(JNIEnv *env, jobject thiz, jstring s) {
        string a = to_string(AES::BLOCKSIZE);
        return env->NewStringUTF(a.c_str());
    }
    
  5. sync并构建,运行之后,可以在NDKFifthTry/app/build/intermediates/cmake/debug/obj目录下或者看到四种不同的ABI和对应的.so文件。可以拷贝出来在其他项目中调用啦。

6.2 直接利用CMake和ninja生成指定ABI的.so文件

根据AS 开发官网说明——从命令行调用 CMake,可以在调用CMake在Android Stdio之外生成ninja项目。之后调用ninja自然就可以构建处动态链接库了。

  1. 类似6.1节的步骤1进行修改,另外,我们将CMakeLists.txt中的option(BUILD_TESTING "Build library tests" ON)改为option(BUILD_TESTING "Build library tests" OFF)。并将该文件放到cryptopp源码中。
  2. ryptopp目录下新建一个文件夹cryptopp_build用做CMake的输出路径。打开cmd或者PowerShell(确保cmake和ninja已经加入环境变量了),并转到cryptopp_build路径下。
  3. 输入命令cmake .. -DANDROID_ABI=arm64-v8a -DANDROID_PLATFORM=android-30 -DANDROID_NDK=C:\Users\user01\AppData\Local\Android\Sdk\ndk\21.4.7075529 -DCMAKE_TOOLCHAIN_FILE=C:\Users\user01\AppData\Local\Android\Sdk\ndk\21.4.7075529\build\cmake\android.toolchain.cmake -G Ninja -DCMAKE_MAKE_PROGRAM=D:\Microsoft\ninja-win\ninja.exe,开始构建(命令参考AS 开发官网说明)。可以看到该目录下生成了build.ninja等文件。
  4. 调用命令:ninja,将在当前文件路径下生成libcryptopp.solibcryptopp.a文件。这个.so文件就可以直接用在aem64-v8a的架构下。其他ABI可以对应生成。
七、AS调用libcryptopp.so
  1. 新建一个Native C++项目,将四种ABI对应的.so文件放到app/libs目录下(也可以只放其中一些ABI的,此时需要在app/build.gradle中指定ABI类型),在app/src/main/cpp下新建文件夹include,并将crypto++中所有的头文件放进去。

  2. 修改CMakeLists.txt

    cmake_minimum_required(VERSION 3.10.2)
    project("encryptofile01")
    
    add_library(cryptopp-lib SHARED IMPORTED)
    set_target_properties(cryptopp-lib PROPERTIES IMPORTED_LOCATION  ${CMAKE_SOURCE_DIR}/../../../libs/${ANDROID_ABI}/libcryptopp.so)#这里必须是绝对路径
    include_directories(include/)
    
    add_library(encryptofile01 SHARED native-lib.cpp)
    
    find_library(log-lib log)
    
    target_link_libraries(encryptofile01 cryptopp-lib ${log-lib})
    
  3. native-lib中调用加密方法即可。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值