Android NDK开发 Android.mk 转化 CMakeList.txt

最近没什么事,就想将十年前公司eclipse写的jni项目转化到Android Studio工程,并使用新的构建工具CMake。

从eclipse转AS工程很简单,直接用as自带的转换即可。最麻烦的部分就是jni那块,该文章主要介绍CMakeList.txt脚本的编写,以及Android.mk里面命令跟CMakeList.txt命令的对比。

一、首先将eclipse工程转为Android studio,并关联上jni

转换项目工程我就不说了,直接选择Import project(Gradle,Eclipse ADT,etc.)。

然后导入你的eclipse工程,等构建完毕后(编译的时候可能不会报错,运行才会提示),这个时候jni还并没有关联上。

第一种你可以直接运行,然后报错的时候as会提示你关联jni。

第二种方式,右键app->Link C++ Project with Gradle,如下图(一定要是右键Module,在Project右键是没有的)
Android NDK开发 Android.mk 转化 CMakeList.txt_第1张图片

然后会让你选择用哪种构建方式,有两种:
1. CMake:Android studio新的构建方式,Project Path需要选择CMakeList.txt文件路径,jni会按照这个脚本来进行编译,具体脚本的编写看下面。
2. ndk-build:老eclipse的构建方式,也就是Android.mk的形式。

Android NDK开发 Android.mk 转化 CMakeList.txt_第2张图片
点击ok之后就会开始构建,其实这个选择也就是在gradle里面加一句话而已。
Android NDK开发 Android.mk 转化 CMakeList.txt_第3张图片


二、编写CMakeList.txt,以及跟Android.mk的对比

1.设置需要设置构建jni所需的CMake的最低版本

cmake_minimum_required(VERSION 3.4.1)

2.设置生成的so动态库最后输出的路径,如果项目的so库不需要给别人使用,也可以不设置,不设置的话会生成在build里面,如果给别人使用,寻找的话还是比较麻烦,所以指定一下路径更方便。

# 设置生成的so动态库最后输出的路径,set后面两个参数,
# 前面一个是命令,后面是路径。
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/../jniLibs/${ANDROID_ABI})
  1. CMAKE_LIBRARY_OUTPUT_DIRECTORY : 指定的命令,用于设置输出的路径
  2. ${PROJECT_SOURCE_DIR} :是指当前路径,也就是CMakeList.txt所在的路径,其实也可以不写,因为默认就是以CMakeList.txt所在的路径为起点。
  3. ../jniLibs: ..两个点是回到上一层,一般来说我们都是建个jni文件夹用于放置c文件,CMakeList.txt是在jni文件夹下,也就是回到上一层,存放在jniLib里面。
  4. ${ANDROID_ABI} 会根据架构来生成不同的文件夹放置对应的so文件。
    这里写图片描述

3.设置头文件搜索路径,如果跟CMakeLists.txt同路径则不需要设置,同路径下文件夹不行。

#设置头文件搜索路径(和此txt同个路径的头文件无需设置),可选
include_directories(Cipher
                    Core
                    Core/Common
                    Core/DB
                    Core/DG)

对应于Android.mk 下的 LOCAL_C_INCLUDES

4.设置生成so库的名称,并为源文件提供一个相对路径

file(GLOB jni "*.cpp")//设置统配符
file(GLOB Cipher "Cipher/*.cpp")//设置统配符

add_library( # Sets the name of the library.  设置库的名称,也就是生成的so名称,在java代码中加载so库需要与此一致
             demo

             # Sets the library as a shared library. 将library设置为一个共享库
             SHARED

             # Provides a relative path to your source file(s).为源文件提供一个相对路径,可以使用通配符,也可以一个一个文件的引入
                demo.cpp
                ${jni}//引用上面设置到通配符
                ${Cipher}
             )

对应于Android.mk
 LOCAL_MODULE 设置so库的名称
 LOCAL_SRC_FILES  设置源文件
 FILE_LIST  设置文件列表,也就是通配符

5.使用本地系统库,在CMake默认的搜索路径就已经包含了系统库,所以直接指定名称就行,CMake会验证库是否存在。

find_library( # Sets the name of the path variable. 
                设置路径变量的名称,后面会引用到
              log-lib   //log日志,默认都需要添加

              # Specifies the name of the NDK library that 指定NDK库的名称
              # you want CMake to locate.
              log
              )

find_library( # Sets the name of the path variable. 设置路径变量的名称
               jnigraphics-lib

               # Specifies the name of the NDK library that 指定NDK库的名称
               # you want CMake to locate.
               jnigraphics
               )

对应于Android.mk 下的 LOCAL_LDLIBS

6.加载第三库,比如自己定义的库,或者用的第三方库,.a库等等

//比如添加一个第三方库libpng,这两个需要配合使用
add_library(libpng STATIC IMPORTED)
set_target_properties( libpng
                       PROPERTIES IMPORTED_LOCATION
                       ......../libpng.a )
使用的时候,只需要修改将两个libpng名称改成你需要的,可以随意取,该名称是后面用来链接使用的,再修改一下对应文件路径就ok。

对应于Android.mk 下的 LOCAL_LDLIBS

7.链接所有的库,比如上面我们添加的第三方库,添加的本地log库

target_link_libraries( # Specifies the target library.指定目标library中。 
                        //通过add_library添加的直接写设置的名字,
                        //一种是SHARED,一般来说就是我们需要生成的so文件
                        //二中是STATIC IMPORTED 添加的第三方静态库
                       demo
                       libpng

                       # Links the target library to the log library  将目标library链接到日志库
                       # included in the NDK.
                       //链接本地NDK里面的库
                       ${log-lib}
                       ${jnigraphics-lib}
                       )

8.如何通过CMakeList.txt生成多个so库?针对不同的so库,编写不同的CMakeList.txt,然后在主CMakeList.txt中加载

# 再主CMakeList.txt中加载其他的子CMakeList.txt文件
# 指定子CMakeList.txt文件路径(指定到CMakeList.txt所在文件夹即可)
ADD_SUBDIRECTORY(FrameWork/ThirdParty/a)
ADD_SUBDIRECTORY(b)

9.如果不进行配置,那么默认会生成四种架构的so库文件,如何生成指定的so库文件

externalNativeBuild {
            cmake {
                cppFlags ""
                abiFilters "armeabi-v7a"//需要什么构架的so,就在这边添加即可
            }
        }

三、贴上上面完整的CMakeList.txt文件脚本

# 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)

# 设置生成的so动态库最后输出的路径
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/../jniLibs/${ANDROID_ABI})

#设置头文件搜索路径(和此txt同个路径的头文件无需设置),可选
include_directories(Cipher
                    Core
                    Core/Common
                    Core/DB
                    Core/DG
                   )

file(GLOB jni "*.cpp")
file(GLOB Cipher "Cipher/*.cpp")


# 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. Gradle

add_library( # Sets the name of the library.  设置库的名称。
             demo

             # Sets the library as a shared library. 将library设置为一个共享库
             SHARED

             # Provides a relative path to your source file(s).为源文件提供一个相对路径
                demo.cpp
                ${jni}
                ${Cipher}
             )


# 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 指定NDK库的名称
              # you want CMake to locate.
              log
              )

find_library( # Sets the name of the path variable. 设置路径变量的名称
               jnigraphics-lib

               # Specifies the name of the NDK library that 指定NDK库的名称
               # you want CMake to locate.
               jnigraphics
               )

# 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. 


add_library(libpng STATIC IMPORTED)
set_target_properties( libpng
                       PROPERTIES IMPORTED_LOCATION
                       ..../libpng.a )

target_link_libraries( # Specifies the target library.
                       demo
                       libpng

                       # Links the target library to the log library 
                       # included in the NDK.
                       ${log-lib}
                       ${jnigraphics-lib}
                       )
//加载子CMakeList.txt文件
ADD_SUBDIRECTORY(FrameWork/ThirdParty/a)
ADD_SUBDIRECTORY(b)

四、所遇到的问题

  1. 从eclipse转化过来的工程,使用CMakeList.txt,部分头文件的导入路径会发生改变,需要进行修改。可能跟CMakeList.txt里面的头文件配置有关系。
  2. More than one file was found with OS independent path ‘lib/arm64-v8a/xxx.so’ 如果运行的时候遇到这个问题。在gradle里面配置一下。原因不详,按理说通过CMakeList.txt设置生成目录到jniLibs后,so存放在jniLibs里面,而在gradle里面配置只有so库存放在lib下才需要,但是看这个错误好像是lib下冲突,但我lib下并没有存放so文件,编译可以通过,运行就会报错,然后我看了一下bulid下面也有一份so库文件,难道运行后,会自动将bulid下的so文件复制到lib里面吗?如果有知道的原因,麻烦告诉一声,感激不尽!
sourceSets{
    main{
         jniLibs.srcDirs = ['libs']
    }
}

五、怕生成多个so库那块描述有些简单,所以做了一个简单的Demo,里面涉及到上述的脚本命令,以及生成多个so库的方式,需要的可以自己下载。

Demo链接

六、参考博客

Android Studio NDK CMake 指定so输出路径以及生成多个so的案例与总结

你可能感兴趣的:(android)