作为一个Android开发,最近在工作中需要频繁的与底层交互,就避免不了与.c/.cpp/so/.a打交道。通过不停的学习,总算摸出来了点门道,跟大家交流交流,也把自己踩过的坑列出来,避免后人上当。
目录
一丶工程以及文件夹的创建。
二 丶配置文件编写。
1.配置子Cmake文件。
2.配置Project级CmakeList.txt文件
3.配置module级build.gradle文件。
注意事项:
老一套,新建C++工程。红圈部分勾选是必须的,如下图。
因为涉及刀一些路径操作,所以建议换成project模式进行操作。同时为了更清晰的描述创建库过程,在cpp文件夹下分别创建oneA和twoB文件夹,然后加一个cmake文件(复制粘贴自动生成的就好)分别对应创建静态库和动态库。然后随便在里面写点函数。
然后为了方便寻找,建一个文件夹专门放置生成的库文件。
这里再次声明:请大家注意一下工程结构,你所创建的文件夹路径跟你下面需要配置的文件信息有很大关系!!!本人因为已经因为路径跳了无数次坑了!!!
1.注意3个CmakeList.txt的路径。原工程生成的CmakeList.txt在app下,跟module级build.gradle同一个父目录。另外俩个在app/src/main/自己新建的文件夹下。
2.我新建的存放库文件跟头文件的libBuild文件夹,在project文件夹下,跟app文件夹同级目录。
准备工作做好了,下面进入重点。重点在俩方面,Cmake文件和gradle文件配置。
注释加代码,我相信已经可以很清晰的描述过程了。
步骤概括为以下三点:
1编译库文件
2修改输出路径
3复制头文件到制定路径(可以忽略)
静态库配置
cmake_minimum_required(VERSION 3.4.1) set(CMAKE_VERBOSE_MAKEFILE on) #指定用到的系统库或者NDK库或者第三方库的搜索路径。 #link_directories(../lib) #头文件引用路径 include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) #编译出一个静态库 源文件是 src/main/cpp/one/src/one.c add_library( # Sets the name of the library. lib_a # Sets the library as a shared library. STATIC # Provides a relative path to your source file(s). ${CMAKE_CURRENT_SOURCE_DIR}/src/one.c ) # 设置库文件存放路径 set(lib_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../../../../libBuild) set_target_properties(lib_a PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "${lib_DIR}/one/lib/${ANDROID_ABI}") # copy out lib header file... TARGET 库名 POST_BUILD add_custom_command(TARGET lib_a POST_BUILD COMMAND "${CMAKE_COMMAND}" -E #复制路径(头文件地址) copy "${CMAKE_CURRENT_SOURCE_DIR}/include/one.h" #目标路径 "${lib_DIR}/one/include/one.h" COMMENT "Copying one to output directory")
动态库配置
cmake_minimum_required(VERSION 3.4.1)
set(CMAKE_VERBOSE_MAKEFILE on)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
add_library(lib_so SHARED src/two.c)
set(distribution_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../../../../libBuild)
set_target_properties(lib_so
PROPERTIES
LIBRARY_OUTPUT_DIRECTORY
"${distribution_DIR}/two/lib/${ANDROID_ABI}")
add_custom_command(TARGET lib_so POST_BUILD
COMMAND "${CMAKE_COMMAND}" -E
copy "${CMAKE_CURRENT_SOURCE_DIR}/include/two.h"
"${distribution_DIR}/two/include/two.h"
COMMENT "Copying two to output directory")
核心在于通过配置启动俩个子模块的cmakelist.txt分别构建不同的库。project级别的Cmake.txt在你的project级别的build.gradle(APP文件夹同一个父级 )引用。当然你也可以选择直接构件库,只需要一个cmakelist.txt就OK了~
cmake_minimum_required(VERSION 3.4.1) set(CMAKE_VERBOSE_MAKEFILE on) #lib_src_DIR设置路径(全局变量) 指向环境变量HOME/tmp(临时)目录 set(lib_src_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src/main/cpp) set(lib_build_DIR $ENV{HOME}/tmp) #创建lib_build_DIR目录(打包临时目录) file(MAKE_DIRECTORY ${lib_build_DIR}) #add_subdirectory构建添加一个子路径。子路径中的 CMakeLists.txt 也会被执行 #注意2个参数,第一个为子路径cmake的位置,第二个为引入的外部路径(用来放编译中间文件夹) #add_subdirectory(${lib_src_DIR}/one ${lib_build_DIR}/one) add_subdirectory(${lib_src_DIR}/two ${lib_build_DIR}/two)
注意俩个cmake{}的位置,上面一个控制编译信息,下面一个主要说明文件的位置。基本上就这2个改动点
下面附上我module级build.gradle的配置信息。注意俩个配置信息cmke{}的位置,跳坑欲哭无泪的我~~~
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion 27
defaultConfig {
applicationId "com.heima.buildlib"
minSdkVersion 23
targetSdkVersion 27
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
/* DANDROID_PLATFORM android 编译平台-minSdkVersion
* targets 编译库名称 */
arguments '-DANDROID_PLATFORM=android-23'
targets 'lib_so'
cppFlags "-frtti -fexceptions"
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
externalNativeBuild {
cmake {
//cmake文件引用路径
path "CMakeLists.txt"
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation"org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
implementation 'com.android.support:appcompat-v7:27.1.1'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
1.project级别的build.gradle在使用tagets‘库名称’,一定注意是生成库的名称,而不是文件夹的名称。这个需要与子cmke脚本构建库的名称一致。
2.tagets不配置的话,默认全部构建。但是经过本工程试验,发现.a跟.so只能同时构建一个,所以这里要选择自己需要构建的库名!
3.顺便提示一下,有时候编译不通过可能是犹豫你的cmake文件路径或者文件名没有写对,耐心一些,并且仔细观察Android studio的Gradle Control窗口,有时候
会提示你具体的错误信息。
通过Cake的方式,我们可以轻松引用c/c++源代码编译库文件,通过本人一步一步跳坑教学,有了源码的支持,是不是感觉简单多了。
以上都搞好了话,build就OK了~在目标路径(buildLib文件夹)就会找到头文件和俩种库文件了。至此,库文件的编译过程全部完毕。
PS:完事开头难,本人不知道这个破玩意陆陆续续坑了多少次,不是库文件没创建成功就是头文件根本没有复制,要了干脆了编译失败,痛苦啊痛苦,但是迈出了第一次,
后面的就水到渠成了。
附上资源地址:CSDN资源
GitHUb传送门