Android开发——Android Studio下使用Cmake在NDK环境下移植Dlib库

4 篇文章 1 订阅
1 篇文章 0 订阅

环境要求

使用AS2015平台,SDK Manager中已经安装了Cmake(编译工具),NDK(环境),LLDB(调试器)。没有装的话,打开SDK Manager选中安装重启AS就可以了。

AS项目结构:

我从不勾选C++ Support的项目开始,最终的项目结构如下:

-projectName
-----app
----------src
-------------main
------------------cpp(*自己写的JNI文件)
------------------dlib(*dlibX.X目录下的dlib文件夹,dlib源码)
------------------java
------------------res
------------------AndroidManifest.xml
----------build.gradle(app的gradle配置文件)
----------CMakeLists.txt(*CMake的配置文件,这块不太熟悉,照葫芦画瓢)
-----distribution(*依赖环境)
----------include(*存放头文件,dlib用不到,Opencv可能用到)
----------libs(*存放依赖的库文件,比如.a的静态库,或者.so的共享库,生成的so库也放在了这里)
---------------ANDROID_ABI}(*不同架构下的库,名字也不一样,例如armeabi/x86/等等)

*的条目为自己所添加的目录。

第一步 获取dlib源码

下载Dlib源码,官网地址:http://dlib.net/,官网下载地址http://dlib.net/files/dlib-19.8.zip

第二步 项目添加dlib源码

下面所有的视图,都是基于Project视图下进行操作。

解压缩Dlib后,得到dlib-XX.X目录,进入该目录,复制dlib文件夹(该文件下都是源码)到AS工程目录/YourProjectName/app/src/main/下(其实这个目录随意,后面要在CMakeList.txt中指定,找的到就行)。

这里写图片描述

第三步 构建JNI目录结构

(1)YourProjectName/app/src/main/下新建名为cpp的目录。即在左侧main上右键,新建Directory。
这里写图片描述
(2)新建一个测试cpp,随意起名,这里命名为native-test.cpp,这个名字随意,没有要求。先不用写内容,是空文件即可。此时目录结构是这样的。
这里写图片描述

(3)在app文件夹下添加CMakeLists.txt(app其实在AS下是一个Module,CMakeList.txt可以有多个,以后作深入了解)。

内容如下:

cmake_minimum_required(VERSION 3.4.1)

set(distribution_DIR ${CMAKE_SOURCE_DIR}/../distribution)
set(pathToDlib ${CMAKE_SOURCE_DIR}/src/main/)

include(${pathToDlib}/dlib/cmake)

add_library(native-my SHARED
            src/main/cpp/native-test.cpp)

set_target_properties(native-my PROPERTIES
                      LIBRARY_OUTPUT_DIRECTORY
                      ${distribution_DIR}/libs/${ANDROID_ABI})

target_link_libraries(native-my
                      dlib::dlib
                      log)
SET(CMAKE_BUILE_TYPE "RELEASE")
SET(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall")
SET(CMAKE_BUILE_TYPE "DEBUG")
SET(CMAKE_CXX_FLAGS_DEBUG "-g -Wall")

大概解释下:很大可能不正确,不熟悉Cmake。。。

  • 第1行,需求的Cmake最小版本号
  • 第2、3行,设置变量
  • 第4行,包含别的CmakeLists.txt
  • 第5行(看做一行),编译成library,参数是库的名字、库的类型、源码s(自己写的JNI文件,可以有多个)
  • 第6行,设置输出库文件的位置
  • 第7行,库需要链接(依赖)的其他的库,一起打包成第一个参数的名字下面都是链接的库

最下面四行是设置Debug/Release的编译选项,用过Dlib的都知道Debug版本慢的要死,不过也是有用的,调试时使用

注意这里的${CMAKE_SOURCE_DIR}的值,挺复杂的,可以百度一下,若有多个则指向顶级CMakeLists.txt的路径。

此时目录结构如下:
这里写图片描述
(4)在build.gradle中关联上该CMakeLists.txt。打开app模块下的build.gradle,内容增加如下:

几个新增项:
  • android.defaultConfig下增加
        externalNativeBuild {
            cmake {
                arguments '-DANDROID_PLATFORM=android-19',
                        '-DANDROID_TOOLCHAIN=clang', '-DANDROID_STL=c++_shared', '-DCMAKE_BUILD_TYPE=Release ..'
                cppFlags "-std=c++11 -O3"
            }
        }
        ndk {
            abiFilters 'armeabi'
        }

arguments设置了cmake的平台,编译器,STL版本库的支持(很多,可以百度区别,STL支持程度不同),Release模式(dlib必须使用的东西)。

cppFlags设置了编译参数(一些支持)。

ndk.abiFilters设置了编译出的不同平台库文件过滤器,使用对应CPU的平台,可以缩小APK体积。

  • android下增加(与defaultConfig并列):
    externalNativeBuild {
        cmake {
            path 'CMakeLists.txt'
        }
    }

这一段,由于build.gradle与CMakeLists.txt同目录,故直接使用相对路径关联我们编写的CMakeLists.txt

  • 增加jniLibs源文件夹,可以打包入APK(与defaultConfig并列)
    sourceSets {
        main {
            jniLibs.srcDirs = ['../distribution/libs']
        }
    }

整个build.gradle内容如下:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 26
    defaultConfig {
        applicationId "com.example.shen.testdlib0102"
        minSdkVersion 19
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        externalNativeBuild {
            cmake {
                arguments '-DANDROID_PLATFORM=android-19',
                        '-DANDROID_TOOLCHAIN=clang', '-DANDROID_STL=c++_shared', '-DCMAKE_BUILD_TYPE=Release ..'
                cppFlags "-std=c++11 -O3"
            }
        }
        ndk {
            abiFilters 'armeabi'
        }
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    externalNativeBuild {
        cmake {
            path 'CMakeLists.txt'
        }
    }
    sourceSets {
        main {
            jniLibs.srcDirs = ['../distribution/libs']
        }
    }
}

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'
}

可以build/Rebuild或者make module app一下,然后看一下Android视图,是什么样子。我的如下图。

这里写图片描述
可以看到dlib被当做了静态库使用,native-my使我们自己写的JNI编译成共享库,而JniLibs中有我们JNI代码生成的so文件。

至此,Dlib移植完毕。下面编写代码进行测试,能运行就行-.-。


第四步 测试代码

接下来,在src/main/cpp中使用JNI的格式去编写我们需求的本地代码,我们可以使用Dlib的头文件以及各种实现。
这里写图片描述
解决办法:暴力解决,后果未知 0.0
打开src/main/dlib文件夹,找到CmakeLists.txt,ctrl+F找到如下代码段:
这里写图片描述
直接用#号注释掉。
然后Rebuild,会出现如下错误:

这里写图片描述
Ctrl+A,Ctrl+/,全部注释(我的KeyMap是Eclipse风格)。
然后再Rebuild,会出现如下错误:
这里写图片描述
然后再Rebuild,就又出错了,如下:
这里写图片描述
Ctrl+A,Ctrl+/,全部注释。
然后再Rebuild,就。。大功告成了!


可能出现的问题

(1)More than one file was found with OS independent path ‘XXXXX’
这里写图片描述
在出现问题的module中的build.gradle的android下(与defaultConfig同级别)加上如下代码:

    packagingOptions {
        pickFirst 'lib/armeabi/libnative-my.so'
    }

2018-1-22批注:该原因由于CmakeLists中设置了SO库文件的输出目录,在Build.gradle中指定了JniLibs.srcDir,又由于在Build/intermediates/cmake中有中间文件。AS默认会自动复制临时目录的库文件到APK,但由指定了预编译的库,造成SO库文件重复,AS不知道去复制哪个,该句话就是指定优先级。


遗留问题

(1)上面的做法的隐患也可想而知,等遇到错误再说吧。
(2)还有VS环境下要用到source.cpp,这里没有用到。里面都是宏定义定义的编译参数,C++和Cmake不熟悉,不知道该如何处理。
实验1:用到source.cpp的话加上编译参数还是报DLIB_NO_GUI_SUPPORT的错误以及X11相关错误,放弃了。
(3)VS环境下,还需要加上
“DLIB_PNG_SUPPORT”,”DLIB_JPEG_SUPPORT”,”DLIB_PNG_STATIC”等编译参数,这里都没用上(和问题2一样,应该也需要加上source.cpp才生效),因为项目是从cv::Mat读取的,不必读取文件(主要原因还是Cmake中不知道加在哪个位置..哈哈哈..)。

  • 7
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 22
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值