最近的项目涉及到JNI编程,经过一段时间的JNI编程之后,终于完美弄完了。所以,把在android studio中编译c/c++文件成.so库的过程记录一下。

在Android studio中使用cmake编译 .so库

1. 安装JNI(java Native Interface)的开发环境。

(1)NDK(Native Development kit):NDK是一个工具集,允许你的App使用一些底层语言代码,例如C和C++。

(2)CMake:CMake是一个跨平台的编译工具,可以用简单的语句来描述所有平台的编译过程,它能够输出各种各样的makefile或者project文件,能测试编译器所支持的C++特性,类似UNIX下的automake。(谷歌从AndroidStudio2.2以上就添加了Cmake方式来编译NDK代码)

(3)LLDB:支持断点调试c/c++源码。

2. 在Android studio中,创建一个支持c/c++的project。

勾选上那个支持c++的选项:

3. project创建成功之后,会出现cmakelists.txt文件,文件中包含的指令:

(1)cmake的最低版本要求:

cmake_minimum_required(VERSION 3.4.1)

(2)使用 add_library() 的 CMake指令构建脚本添加源文件或库,为了确保 CMake 可以在编译时定位您的标头文件,您需要将 include_directories() 命令添加到 CMake 构建脚本中并指定标头的路径:

demo脚本:

add_library( # Sets the name of the library.
             my-native-lib     #library的名称

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
             src/main/cpp/native-lib.cpp 
             src/main/cpp/hello.cpp )

# Specifies a path to native header files.
include_directories(src/main/cpp/include/)

(3)使用find_library添加 NDK库

Android NDK 提供了一套实用的原生 API 和库。通过将 NDK库包含到项目的 CMakeLists.txt 脚本文件中,您可以使用这些 API 中的任意一种。预构建的 NDK 库已经存在于 Android 平台上,因此,您无需再构建或将其封装到 APK 中。由于 NDK 库已经是 CMake 搜索路径的一部分,您甚至不需要在您的本地 NDK 安装中指定库的位置 - 只需要向 CMake 提供您希望使用的库的名称,并将其关联到您自己的原生库。

demo脚本:

find_library( # Sets the name of the path variable.
              log-lib   #例如:添加ndk中log-lib库

              # Specifies the name of the NDK library that
              # you want CMake to locate.
              log )

(4)为了确保创建的原生库可以使用log 库中的函数,需要使用 CMake 构建脚本中的 target_link_libraries() 命令关联库。

demo脚本:

target_link_libraries( # Specifies the target library.
                       my-native-lib

                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib} )

完整的cmakelists.txt文件中的内容如下:

demo脚本:

# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html

# Sets the minimum version of CMake required to build the native library.

cmake_minimum_required(VERSION 3.4.1)

# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.

add_library( # Sets the name of the library.
             my-native-lib

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
             src/main/cpp/native-lib.cpp
             src/main/cpp/hello.cpp )

# Specifies a path to native header files.
include_directories(src/main/cpp/include/)

# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.

find_library( # Sets the name of the path variable.
              log-lib

              # Specifies the name of the NDK library that
              # you want CMake to locate.
              log )

# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.

target_link_libraries( # Specifies the target library.
                       my-native-lib

                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib} )

一些截图:

225013_Hpp9_2002921.png

225136_j96r_2002921.png

其中,NDK 还以源代码的形式包含一些库,您在构建和关联到您的原生库时需要使用这些代码。您可以使用 CMake 构建脚本中的 add_library() 命令,将源代码编译到原生库中。要提供本地 NDK 库的路径,您可以使用 ANDROID_NDK 路径变量,Android Studio 会自动为您定义此变量。

以下命令可以指示 CMake 构建 android_native_app_glue.c,后者会将 NativeActivity 生命周期事件和触摸输入置于静态库中并将静态库关联到库中:

demo脚本:

add_library( app-glue
             STATIC
             ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c )

# You need to link static libraries against your shared native library.
target_link_libraries( my-native-lib app-glue ${log-lib} )

添加其他预构建库

添加预构建库与为 CMake 指定要构建的另一个原生库类似。不过,由于库已经预先构建,您需要使用 IMPORTED 标志告知 CMake 您只希望将库导入到项目中:

add_library( imported-lib
             SHARED
             IMPORTED )

然后,您需要使用 set_target_properties() 命令指定库的路径,如下所示。

某些库为特定的 CPU 架构(或应用二进制接口 (ABI))提供了单独的软件包,并将其组织到单独的目录中。此方法既有助于库充分利用特定的 CPU 架构,又能让您仅使用所需的库版本。要向 CMake 构建脚本中添加库的多个 ABI 版本,而不必为库的每个版本编写多个命令,您可以使用 ANDROID_ABI 路径变量。此变量使用 NDK 支持的一组默认 ABI,或者您手动配置 Gradle 而让其使用的一组经过筛选的 ABI。例如:

复制代码

add_library(...)
set_target_properties( # Specifies the target library.
                       imported-lib

                       # Specifies the parameter you want to define.
                       PROPERTIES IMPORTED_LOCATION

                       # Provides the path to the library you want to import.
                       imported-lib/src/${ANDROID_ABI}/libimported-lib.so )

复制代码

为了确保 CMake 可以在编译时定位您的标头文件,您需要使用 include_directories() 命令,并包含标头文件的路径:

include_directories( imported-lib/include/ )

:如果您希望封装一个并不是构建时依赖项的预构建库(例如在添加属于 imported-lib 依赖项的预构建库时),则不需要执行以下说明来关联库。

要将预构建库关联到您自己的原生库,请将其添加到 CMake 构建脚本的 target_link_libraries() 命令中:

target_link_libraries( my-native-lib imported-lib app-glue ${log-lib} )

在您构建应用时,Gradle 会自动将导入的库封装到 APK 中。您可以使用 APK 分析器验证 Gradle 将哪些库封装到您的 APK 中。如需了解有关 CMake 命令的详细信息,请参阅 CMake 文档。

4. module中gradle文件的

(abi相关知识:https://developer.android.google.cn/ndk/guides/abis.html

(1)默认配置中的externalNativeBuild的配置

defaultConfig {
    ...
    externalNativeBuild {
        cmake {
            cppFlags ""     //配置c++的版本库,其中""表示使用默认的,如 cppFlags "-std=c++14" 为c++14版本
            abiFilters "armeabi", "armeabi-v7a", "x86","x86_64","mips","mips64"   // 输出指定abi体系结构下的so库
        }
    }
}

(2)配置所引用的cmakelists.txt文件

externalNativeBuild {
    cmake {
        path "CMakeLists.txt"
    }
}

完整的demo的gradle文件内容如下:

apply plugin: 'com.android.application'

android {
    ...
    defaultConfig {
        ...
        externalNativeBuild {
            cmake {
                cppFlags ""   //配置c++的版本库,其中""表示使用默认的,如 cppFlags "-std=c++14" 为c++14版本
                abiFilters "armeabi", "armeabi-v7a", "x86","x86_64" // 输出指定abi体系结构下的so库
            }
        }
    }
    ...
//配置引用的CMakeLists.txt文件
    externalNativeBuild {
        cmake {
            path "CMakeLists.txt"
        }
    }
}

dependencies {
    ...
}

5. 加载编写的原生库

demo脚本:

static {
    System.loadLibrary("my-native-lib");  //通过静态块,加载原生库
}

public native String stringFromJNI();  //定义一个native 方法

231529_ORwr_2002921.png

navity-lib.cpp的内容:

demo脚本:

#include <jni.h>
#include <string>

extern "C" JNIEXPORT jstring

JNICALL
Java_com_youbale_cmakedemo_Mylib_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

6. build一下project,在build/intermediates/cmake目录下就可以找到编译完成的 .so库。(命名规则:lib+库的名称)

232207_P2Tg_2002921.png

在android studio中把c/c++源码编译成.so文件就大功告成。

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐