Android Studio中经过CMake使用NDK并编译自定义库和增进预编译库

Note:那篇文章是基于Android Studio 3.01本子的,NDK是R16。

Note:那篇小说是基于Android Studio 3.01本子的,NDK是R16。

Note:那篇文章是基于Android Studio 3.01版本的,NDK是R16。

step1:创设一个分包C++的项目

step1:创造一个暗含C++的花色

step1:成立一个包含C++的种类

图片 1

图片 2

图片 3

图片 4

图片 5

图片 6

其余默许就足以了。

其他默许就足以了。

任何默许就足以了。

C++ Standard

C++ Standard

C++ Standard

指定编译库的条件,其中Toolchain
Default
动用的是默许的CMake环境;C++
11
也就是C++环境。两种环境都得以编库,至于不一样,后续会跟进,当前博文使用的是CMake环境

点名编译库的条件,其中Toolchain
Default
动用的是默许的CMake环境;C++
11
也就是C++环境。二种环境都足以编库,至于不相同,后续会跟进,当前博文使用的是CMake环境

点名编译库的环境,其中Toolchain
Default
动用的是默许的CMake环境;C++
11
也就是C++环境。三种环境都得以编库,至于不一致,后续会跟进,当前博文使用的是CMake环境

Exceptions Support
若果当选复选框,则意味近期项目支撑C++至极处理,若是辅助,在品种Module级其余build.gradle文件中会扩大一个标识
-fexceptionscppFlags属性中,并且在so库创设时,gradle会把该属性值传递给CMake展开营造。

Exceptions Support
即使当选复选框,则表示近期项目支撑C++卓殊处理,若是支持,在品种Module级其他build.gradle文件中会扩大一个标识
-fexceptionscppFlags属性中,并且在so库创设时,gradle会把该属性值传递给CMake举行打造。

Exceptions Support
要是当选复选框,则代表近年来项目协理C++十分处理,要是协助,在项目Module级其他build.gradle文本中会增添一个标识
-fexceptionscppFlags特性中,并且在so库营造时,gradle会把该属性值传递给CMake进展打造。

Runtime Type Information Support
同理,选中复选框,项目辅助RTTI,属性cppFlags伸张标识-frtti

Runtime Type Information Support
同理,选中复选框,项目支持RTTI,属性cppFlags日增标识-frtti

Runtime Type Information Support
同理,选中复选框,项目支持RTTI,属性cppFlags追加标识-frtti

切换到project 形式,生成的目录的结构如下:

切换来project 格局,生成的目录的结构如下:

切换来project 方式,生成的目录的结构如下:

图片 7

图片 8

图片 9

图片 10

图片 11

图片 12

3、认识CMakeLists.txt打造脚本文件

CMakeLists.txt文件用于配置JNI项目性质,主要用来表明CMake使用版本、so库名称、C/CPP文件路径等音讯,上边是该公文内容:

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

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

                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib} )
  • cmake_minimum_required(VERSION 3.4.1)
    CMake最小版本选择的是3.4.1。

  • add_library()
    布署so库音信(为近日当前剧本文件添加库)

    • native-lib
      本条是宣称引用so库的称呼,在类型中,如果急需选拔那个so文件,引用的称谓就是这么些。值得注意的是,实际上生成的so文件名称是libnative-lib。当Run连串或者build品类是,在Module级其余build文件下的intermediates\transforms\mergeJniLibs\debug\folders\2000\1f\main下会生成对应的so库文件。
  • SHARED
    其一参数表示共享so库文件,也就是在Run品类如故build项目时会在目录intermediates\transforms\mergeJniLibs\debug\folders\2000\1f\main下生成so库文。其它,so库文件都会在卷入到.apk里面,可以透过选用菜单栏的Build->Analyze
    Apk…
    *查阅apk中是或不是留存so库文件,一般它会存放在lib目录下。

  • src/main/cpp/native-lib.cpp
    构建so库的源文件。

STATIC:静态库,是目标文件的归档文件,在链接其余目的的时候使用。
SHARED:动态库,会被动态链接,在运行时被加载。
MODULE:模块库,是不会被链接到其余目的中的插件,然而可能会在运作时行使dlopen-连串的函数动态链接。
更详实的演说请参考那篇小说:C++静态库与动态库

上面的配备实际上与自定义的JNI项目(自定义的so库)没有太大关系。

  • find_library()
    本条点子与大家要创立的so库毫无干系而是使用NDK的Apis或者库,默认景况下Android平台合并了累累NDK库文件,所以那些文件是向来不须求打包到apk里面去的。直接申明想要使用的库名称即可(臆想:貌似是在Sytem/libs目录下)。在那边不须求指定库的路径,因为那几个路子已经是CMake路径搜索的一局地。如示例中采纳的是log相关的so库。

  • log-lib
    本条指定的是在NDK库中各样类其余库会存放一个特定的地点,而log库存放在log-lib中

  • log
    指定使用log库

  • target_link_libraries()
    假定您本地的库(native-lib)想要调用log库的方法,那么就须要配备那么些特性,意思是把NDK库关联到地方库。

  • native-lib
    要被波及的库名称

  • ${log-lib}
    要涉及的库名称,要用大括号包裹,后边还要有$符号去引用。

其实,大家能够协调创立CMakeLists.txt文件,而且路线不受限制,只要在build.gradle中配置externalNativeBuild.cmake.path来指定该公文路径即可。

add_subdirectory 可以执行子路径的CMakeLists.txt

3、认识CMakeLists.txt创设脚本文件

CMakeLists.txt文本用于配置JNI项目性质,首要用来注解CMake使用版本、so库名称、C/CPP文件路径等音信,上面是该文件内容:

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

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

                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib} )
  • cmake_minimum_required(VERSION 3.4.1)
    CMake最小版本采取的是3.4.1。

  • add_library()
    配备so库新闻(为当下当前剧本文件添加库)

    • native-lib
      以此是宣称引用so库的称号,在档次中,借使须要动用这么些so文件,引用的称呼就是以此。值得注意的是,实际上生成的so文件名称是libnative-lib。当Run品类仍然build项目是,在Module级其他build文件下的intermediates\transforms\mergeJniLibs\debug\folders\2000\1f\main下会生成对应的so库文件。
  • SHARED
    其一参数表示共享so库文件,也就是在Run项目仍旧build品种时会在目录intermediates\transforms\mergeJniLibs\debug\folders\2000\1f\main下生成so库文。其它,so库文件都会在卷入到.apk里面,可以透过选用菜单栏的Build->Analyze
    Apk…
    *查看apk中是或不是留存so库文件,一般它会存放在lib目录下。

  • src/main/cpp/native-lib.cpp
    打造so库的源文件。

STATIC:静态库,是目标文件的归档文件,在链接其它目的的时候利用。
SHARED:动态库,会被动态链接,在运行时被加载。
MODULE:模块库,是不会被链接到其它目的中的插件,然而可能会在运作时行使dlopen-连串的函数动态链接。
更详实的分解请参见那篇小说:C++静态库与动态库

下边的布局实际上与自定义的JNI项目(自定义的so库)没有太大关系。

  • find_library()
    那一个主意与我们要制造的so库非亲非故而是使用NDK的Apis或者库,默许景况下Android平台合并了重重NDK库文件,所以这一个文件是绝非须求打包到apk里面去的。直接注明想要使用的库名称即可(臆度:貌似是在Sytem/libs目录下)。在那边不要求指定库的门径,因为这一个途径已经是CMake路径搜索的一局地。如示例中运用的是log相关的so库。

  • log-lib
    那一个指定的是在NDK库中每个门类的库会存放一个一定的岗位,而log库存放在log-lib中

  • log
    点名使用log库

  • target_link_libraries()
    如若你本地的库(native-lib)想要调用log库的章程,那么就要求配备那一个特性,意思是把NDK库关联到地点库。

  • native-lib
    要被提到的库名称

  • ${log-lib}
    要提到的库名称,要用大括号包裹,前边还要有$符号去引用。

其实,大家得以友善成立CMakeLists.txt文件,而且路线不受限制,只要在build.gradle中配置externalNativeBuild.cmake.path来指定该公文路径即可。

add_subdirectory 可以执行子路径的CMakeLists.txt

3、认识CMakeLists.txt创设脚本文件

CMakeLists.txt文件用于配置JNI项目性质,主要用以注脚CMake使用版本、so库名称、C/CPP文件路径等新闻,上边是该文件内容:

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

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

                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib} )
  • cmake_minimum_required(VERSION 3.4.1)
    CMake最小版本采取的是3.4.1。

  • add_library()
    安插so库信息(为当前当前剧本文件添加库)

    • native-lib
      其一是宣称引用so库的称呼,在品种中,倘使急需选取这些so文件,引用的名称就是那些。值得注意的是,实际上生成的so文件名称是libnative-lib。当Run项目依旧build花色是,在Module级其他build文件下的intermediates\transforms\mergeJniLibs\debug\folders\2000\1f\main下会生成对应的so库文件。
  • SHARED
    其一参数表示共享so库文件,也就是在Run花色或者build品类时会在目录intermediates\transforms\mergeJniLibs\debug\folders\2000\1f\main下生成so库文。其余,so库文件都会在卷入到.apk里面,可以透过接纳菜单栏的Build->Analyze
    Apk…
    *查阅apk中是不是留存so库文件,一般它会存放在lib目录下。

  • src/main/cpp/native-lib.cpp
    创设so库的源文件。

STATIC:静态库,是目的文件的归档文件,在链接其余目的的时候利用。
SHARED:动态库,会被动态链接,在运作时被加载。
MODULE:模块库,是不会被链接到其余目的中的插件,不过也许会在运转时选取dlopen-体系的函数动态链接。
更详尽的解释请参考那篇作品:C++静态库与动态库

下边的配置实际上与自定义的JNI项目(自定义的so库)没有太大关系。

  • find_library()
    那个主意与我们要创制的so库无关而是使用NDK的Apis或者库,默许情状下Android平台合并了重重NDK库文件,所以这个文件是绝非需求打包到apk里面去的。直接声明想要使用的库名称即可(估量:貌似是在Sytem/libs目录下)。在那里不须要指定库的门径,因为那几个路子已经是CMake路径搜索的一有的。如示例中运用的是log相关的so库。

  • log-lib
    那个指定的是在NDK库中每个门类的库会存放一个一定的岗位,而log库存放在log-lib中

  • log
    点名使用log库

  • target_link_libraries()
    万一你本地的库(native-lib)想要调用log库的方法,那么就必要配置那么些特性,意思是把NDK库关联到当地库。

  • native-lib
    要被提到的库名称

  • ${log-lib}
    要提到的库名称,要用大括号包裹,前边还要有$符号去引用。

实则,大家得以友善创办CMakeLists.txt文件,而且路线不受限制,只要在build.gradle中配置externalNativeBuild.cmake.path来指定该公文路径即可。

add_subdirectory 可以执行子路径的CMakeLists.txt

添加自定义的C++库mathlib

添加自定义的C++库mathlib

添加自定义的C++库mathlib

成立源文件
  1. 自身的项目名称为OpenCVTest,所以右键这么些体系点击New->Module,然后选Android Library,输入库的名号MathLib,然后Finish,系统就会转移对应的模块,并构建好起来的目录树。系统将库命名为MathLib,不过目录树中要么小写的mathlib。那么些时候系统会活动在五星级settings.gradle丰裕对于那个新模块的include讲话。并且在模块目录下营造好了初步的build.gradle
  2. 前日我们开始创办和谐的C++库,首先右键mathlib目录下的src/main,然后选拔New->Directory,输入cpp并确定。那个目录就是我们要开创的库的源文件的位置。
  3. 右键add,点击New->C/C++ Source File,输入add.cpp,并选中Create an associated header
  4. .cpp文本中定义好一个简约的加法函数,并在.h文本中添加好对应申明。

add.cpp

#include "add.h"
int add(int a,int b) {
    return a + b;
}

add.h

#ifndef OPENCVTEST_ADD_H
#define OPENCVTEST_ADD_H
int add(int a,int b);
#endif //OPENCVTEST_ADD_H
创造源文件
  1. 我的项目名称为OpenCVTest,所以右键那个种类点击New->Module,然后选Android Library,输入库的称谓MathLib,然后Finish,系统就会变动对应的模块,并创设好开首的目录树。系统将库命名为MathLib,可是目录树中要么小写的mathlib。那些时候系统会活动在一流settings.gradle添加对此这些新模块的include言辞。并且在模块目录下构建好了初始的build.gradle
  2. 最近大家开头创办和谐的C++库,首先右键mathlib目录下的src/main,然后选用New->Directory,输入cpp并确定。那一个目录就是大家要创立的库的源文件的地点。
  3. 右键add,点击New->C/C++ Source File,输入add.cpp,并选中Create an associated header
  4. .cpp文本中定义好一个简易的加法函数,并在.h文本中添加好对应注脚。

add.cpp

#include "add.h"
int add(int a,int b) {
    return a + b;
}

add.h

#ifndef OPENCVTEST_ADD_H
#define OPENCVTEST_ADD_H
int add(int a,int b);
#endif //OPENCVTEST_ADD_H
创造源文件
  1. 自我的项目名称为OpenCVTest,所以右键那些项目点击New->Module,然后选Android Library,输入库的名目MathLib,然后Finish,系统就会转移对应的模块,并营造好起来的目录树。系统将库命名为MathLib,可是目录树中依旧小写的mathlib。那一个时候系统会活动在甲级settings.gradle充裕对此那么些新模块的include话语。并且在模块目录下打造好了开班的build.gradle
  2. 现行大家初叶创立自己的C++库,首先右键mathlib目录下的src/main,然后选取New->Directory,输入cpp并规定。这些目录就是大家要创立的库的源文件的地点。
  3. 右键add,点击New->C/C++ Source File,输入add.cpp,并选中Create an associated header
  4. .cpp文本中定义好一个粗略的加法函数,并在.h文本中添加好对应申明。

add.cpp

#include "add.h"
int add(int a,int b) {
    return a + b;
}

add.h

#ifndef OPENCVTEST_ADD_H
#define OPENCVTEST_ADD_H
int add(int a,int b);
#endif //OPENCVTEST_ADD_H
将源文件涉及到创设系统中
我们用CMake来构建C++库,然后CMake又要和gradle结合,在Android Studio里面协作管理C++和Java的代码。

我们在模块mathlib的根目录下创建一个名为CMakeLists.txt的文件,写入

cmake_minimum_required(VERSION 3.4.1)
add_library(add SHARED
            src/main/cpp/add.cpp)
set(distribution_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../distribution)
set_target_properties(add PROPERTIES
                      LIBRARY_OUTPUT_DIRECTORY
                      ${distribution_DIR}/libs/${ANDROID_ABI})
target_include_directories(add
                           PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src/main/cpp)

add_custom_command(TARGET add POST_BUILD
                   COMMAND "${CMAKE_COMMAND}" -E
                   copy "${CMAKE_CURRENT_SOURCE_DIR}/src/main/cpp/add.h"
                   "${distribution_DIR}/include/mathlib/add.h"
#                   **** the following 2 lines are for potential future debug purpose ****
#                   COMMAND "${CMAKE_COMMAND}" -E
#                   remove_directory "${CMAKE_CURRENT_BINARY_DIR}"
                   COMMENT "Copying gmath to output directory")

set可以自定义变量。这里定义生成so文件的目录

set_target_properties 命令的意思是设置目标的一些属性来改变它们构建的方式。这个命令中设置了 add的ARCHIVE_OUTPUT_DIRECTORY 属性。也就是改变了输出路径。

add_custom_command 命令是自定义命令。命令中把头文件也复制到了 distribution_DIR 中。

target_include_directories,它对创建的库设置include路径,针对目标来设置,可以避免与其他库的冲突,并且此时对自定义的库设置好了此路径后,后续导入这个库就不需要再次设置了。但对于预构建的库,就需要设置,稍后会有详细讲解。

接下来我们在模块mathlib的build.gradle中的defaultConfig{}中添加如下语句:

externalNativeBuild {
            cmake {
                arguments '-DANDROID_PLATFORM=android-19',
                        '-DANDROID_TOOLCHAIN=clang', '-DANDROID_STL=gnustl_static'
                targets 'add'
            }
        }

这里arguments是编译参数,而targets则是相比于add_subdirectory更高权限的方法。一般来说可以把它删去,即默认构建所有目标。

然后在android{}最后添加如下语句,将CMakeLists.txt关联起来。

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

C++库已经创制好了,接下去就要在主模块中利用它了。

为了利用自定义C++库,大家需求一个中间人,它从Android本身的Java程序中取得请求,然后采纳我们的C++库中的函数总结获得结果,并将数据传回Android本身的Java程序中。

创建一个中等文件native-math.cpp

#include <jni.h>
#include <string>
#include "mathlib/add.h"

extern "C"
JNIEXPORT jstring

JNICALL
Java_com_example_bill_opencvtest_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "Hello from C++ From Android openCVTest";
    return env->NewStringUTF(hello.c_str());
}
extern "C"
JNIEXPORT jint JNICALL
Java_com_example_bill_opencvtest_MainActivity_addFromCpp(JNIEnv *env, jobject instance, jint a,
                                                         jint b) {

    // TODO
    return add(a,b);
}

在app/CMakeLists.txt 加上这个自定义库的引用

set(distribution_DIR ${CMAKE_SOURCE_DIR}/../distribution)
include_directories(${distribution_DIR}/include)
add_library(lib_add SHARED IMPORTED)
set_target_properties(lib_add PROPERTIES IMPORTED_LOCATION
                      ${distribution_DIR}/libs/${ANDROID_ABI}/libadd.so)
add_library( # Sets the name of the library.
             native-math

             # Sets the library as a shared library.
             SHARED

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

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 )
set_target_properties(native-math PROPERTIES
                      LIBRARY_OUTPUT_DIRECTORY
                      ${distribution_DIR}/libs/${ANDROID_ABI})
target_link_libraries( # Specifies the target library.
                       native-math
                       android
                       log
                       lib_add
                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib} )

在模块app的局部build.gradle中,像以前同一添加好对应的言辞:

defaultConfig{}中:

externalNativeBuild {
            cmake {
                arguments '-DANDROID_PLATFORM=android-19',
                        '-DANDROID_TOOLCHAIN=clang', '-DANDROID_STL=gnustl_static'
            }
        }
        ndk {
            //abiFilters 'armeabi-v7a','x86_64'
        }

其中ndk指定abi平台

ABI(Application binary interface)应用程序二进制接口。不一样的CPU
与指令集的每种组伊斯兰堡有定义的 ABI
(应用程序二进制接口),一段程序唯有按照那些接口规范才能在该 CPU
上运行,所以一律的程序代码为了合营七个分裂的CPU,必要为分裂的 ABI
创设区其余库文件。当然对于CPU来说,分歧的架构并不代表一定互不兼容。

  • armeabi设备只包容armeabi;
  • armeabi-v7a设备包容armeabi-v7a、armeabi;
  • arm64-v8a装置兼容arm64-v8a、armeabi-v7a、armeabi;
  • X86设备包容X86、armeabi;
  • X86_64设备包容X86_64、X86、armeabi;
  • mips64配备万分mips64、mips;
  • mips只兼容mips;

接着在src/main/java/*/MainActivity.java中的MainActivity类上面,加载库,以及安装好相应的法子声明:

static {
        System.loadLibrary("native-math");
    }
    /**
     * A native method that is implemented by the 'native-lib' native library,
     * which is packaged with this application.
     */
    public native String stringFromJNI();

    public native int addFromCpp(int a, int b);

然后就可以在onCreate方法中使用这个C++库定义的函数,在Java中对应的函数了

super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // Example of a call to a native method
        TextView tv = (TextView) findViewById(R.id.sample_text);
        tv.setText(stringFromJNI() + "____" + addFromCpp(2,88));

最后别忘了在项目中添加模块的依赖关系才可以正常运行这个Android App。右键项目OpenCVTest,选择Open Module Settings。选择app->Dependencies,添加Module dependency,选择mathlib,确定即可

添加OpenCV库的支持
导入OpenCV进项目

从OpenCV的官网将OpenCV4Android 3.4下载下来,解压到某个目录。
点击Android Studio的File->New->Import Module,然后选择路径为OpenCV-android-sdk/sdk/java,确定。并在导入之后,修改build.gradle中的SDK版本。
在Open Module Settings中添加模块的依赖关系,使app依赖openCVLibrary340。

现在已经可以在.java文件中看得到OpenCV的自动补全了。配置OpenCV的C++预构建库

把包含文件夹OpenCV-android-sdk/sdk/native/jni/include和预构建库文件夹OpenCV-android-sdk/sdk/native/libs也复制到项目的distribution中。
由于之前已经在添加C++库时修改了app的build.gradle,所以这个步骤现在不需要再执行了。

由于OpenCV是预构建库,所以没有编译的过程,因此模块openCVLibrary320中不需要添加CMakeLists.txt等。我们直接在app模块中根目录下的CMakeLists.txt导入OpenCV的库即可。set(libs "${CMAKE_SOURCE_DIR}/src/main/jniLibs")
include_directories(${distribution_DIR}/include)
# set add lib
add_library(libopencv_java3 SHARED IMPORTED )
set_target_properties(libopencv_java3 PROPERTIES
    IMPORTED_LOCATION "${libs}/${ANDROID_ABI}/libopencv_java3.so")
add_library( # Sets the name of the library.
             native-opencv

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
             src/main/cpp/native-opencv.cpp )
set_target_properties(native-opencv PROPERTIES
                      LIBRARY_OUTPUT_DIRECTORY
                      ${distribution_DIR}/libs/${ANDROID_ABI})
target_link_libraries( # Specifies the target library.
                       native-opencv
                       android
                       log
                       libopencv_java3
                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib} )

急需专注的是.so使用SHARED.a使用STATIC

留意:预打造库:so文件和.a文件必须copy在src/main/jniLibs这一个目录,才方可自动被装进。其余途径都不可以,连source那一个命令也不起功用

方今得以拔取openCV库了,新建一个文本native-opencv.cpp

//
// Created by bill on 2018/1/13.
//
#include <jni.h>
#include <opencv2/opencv.hpp>
#include <vector>

using namespace cv;
using namespace std;

extern "C"
JNIEXPORT void JNICALL
Java_com_example_bill_opencvtest_MainActivity_nativeProcessFrame(JNIEnv *env, jobject instance,
        jlong addrGray, jlong addrRGBA) {
// TODO
    Mat& gray = *(Mat *) addrGray;
    Mat& rgba = *(Mat *) addrRGBA;
    vector<KeyPoint> v;

    Ptr<ORB> orb = ORB::create();
    orb->detect(gray, v, cv::Mat());

    for (int i = 0; i < v.size(); ++i) {
        const KeyPoint& kp = v[i];
        circle(rgba, Point(kp.pt.x, kp.pt.y), 10, Scalar(255,0,0,255));
    }
}

明日就足以在src/main/java/*/MainActivity.java中根据同样的法门,载入库,写上格局申明。最终,如下所示。

static {
        System.loadLibrary("native-opencv");
        System.loadLibrary("native-math");
    }
    /**
     * A native method that is implemented by the 'native-lib' native library,
     * which is packaged with this application.
     */
    public native String stringFromJNI();

    public native int addFromCpp(int a, int b);
    private native void nativeProcessFrame(long addrGray, long addrRGBA);

完整的MainActivity

package com.example.bill.opencvtest;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.WindowManager;
import android.widget.TextView;

import org.opencv.android.CameraBridgeViewBase;
import org.opencv.android.OpenCVLoader;
import org.opencv.core.CvType;
import org.opencv.core.Mat;

public class MainActivity extends Activity implements CameraBridgeViewBase.CvCameraViewListener2{
    static {
        System.loadLibrary("native-opencv");
        System.loadLibrary("native-math");
    }
    /**
     * A native method that is implemented by the 'native-lib' native library,
     * which is packaged with this application.
     */
    public native String stringFromJNI();

    public native int addFromCpp(int a, int b);
    private native void nativeProcessFrame(long addrGray, long addrRGBA);

    private static final String TAG = "MainActivity";

    private Mat rgba;
    private Mat gray;
    private CameraBridgeViewBase mOpenCvCameraView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // Example of a call to a native method
        TextView tv = (TextView) findViewById(R.id.sample_text);
        tv.setText(stringFromJNI() + "____" + addFromCpp(2,88));
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

        mOpenCvCameraView = (CameraBridgeViewBase) findViewById(R.id.activity_camera_view);
        mOpenCvCameraView.setVisibility(CameraBridgeViewBase.VISIBLE);
        mOpenCvCameraView.setCvCameraViewListener(this);
    }

    @Override
    public void onPause() {
        super.onPause();
        if (mOpenCvCameraView != null){
            mOpenCvCameraView.disableView();
        }
    }

    @Override
    public void onResume()
    {
        super.onResume();
        if (!OpenCVLoader.initDebug()) {
            Log.d(TAG, "Internal OpenCV library not found. Using OpenCV Manager for initialization");
        } else {
            Log.d(TAG, "OpenCV library found inside package. Using it!");
            mOpenCvCameraView.enableView();
        }
    }

    public void onDestroy() {
        super.onDestroy();
        if (mOpenCvCameraView != null){
            mOpenCvCameraView.disableView();
        }
    }

    public void onCameraViewStarted(int width, int height){
        rgba = new Mat(height, width, CvType.CV_8UC4);
        gray = new Mat(height, width, CvType.CV_8UC1);
    }

    public void onCameraViewStopped() {
        rgba.release();
        gray.release();
    }

    public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame){
        rgba = inputFrame.rgba();
        gray = inputFrame.gray();
        nativeProcessFrame(gray.getNativeObjAddr(), rgba.getNativeObjAddr());
        return rgba;
    }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:opencv="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.bill.opencvtest.MainActivity">

    <TextView
        android:id="@+id/sample_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
    <org.opencv.android.JavaCameraView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/activity_camera_view"
        opencv:show_fps="true"
        opencv:camera_id="any"/>
</android.support.constraint.ConstraintLayout>
  • 为了喜悦的使用OpenCV
    Library,可以直接在AndroidManifest.xml里面插手如下权限


    http://schemas.android.com/apk/res/android“

    package="com.example.bill.opencvtest">
    
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.CAMERA"/>
    <uses-feature android:name="android.hardware.camera"/>
    <uses-feature android:name="android.hardware.camera.autofocus"/>
    <uses-feature android:name="android.hardware.camera.front"/>
    <uses-feature android:name="android.hardware.camera.front.autofocus"/>
    
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity"
            android:screenOrientation="landscape"
            android:configChanges="keyboardHidden|orientation">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
    
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
    

将源文件涉及到营造系统中
我们用CMake来构建C++库,然后CMake又要和gradle结合,在Android Studio里面协作管理C++和Java的代码。

我们在模块mathlib的根目录下创建一个名为CMakeLists.txt的文件,写入

cmake_minimum_required(VERSION 3.4.1)
add_library(add SHARED
            src/main/cpp/add.cpp)
set(distribution_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../distribution)
set_target_properties(add PROPERTIES
                      LIBRARY_OUTPUT_DIRECTORY
                      ${distribution_DIR}/libs/${ANDROID_ABI})
target_include_directories(add
                           PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src/main/cpp)

add_custom_command(TARGET add POST_BUILD
                   COMMAND "${CMAKE_COMMAND}" -E
                   copy "${CMAKE_CURRENT_SOURCE_DIR}/src/main/cpp/add.h"
                   "${distribution_DIR}/include/mathlib/add.h"
#                   **** the following 2 lines are for potential future debug purpose ****
#                   COMMAND "${CMAKE_COMMAND}" -E
#                   remove_directory "${CMAKE_CURRENT_BINARY_DIR}"
                   COMMENT "Copying gmath to output directory")

set可以自定义变量。这里定义生成so文件的目录

set_target_properties 命令的意思是设置目标的一些属性来改变它们构建的方式。这个命令中设置了 add的ARCHIVE_OUTPUT_DIRECTORY 属性。也就是改变了输出路径。

add_custom_command 命令是自定义命令。命令中把头文件也复制到了 distribution_DIR 中。

target_include_directories,它对创建的库设置include路径,针对目标来设置,可以避免与其他库的冲突,并且此时对自定义的库设置好了此路径后,后续导入这个库就不需要再次设置了。但对于预构建的库,就需要设置,稍后会有详细讲解。

接下来我们在模块mathlib的build.gradle中的defaultConfig{}中添加如下语句:

externalNativeBuild {
            cmake {
                arguments '-DANDROID_PLATFORM=android-19',
                        '-DANDROID_TOOLCHAIN=clang', '-DANDROID_STL=gnustl_static'
                targets 'add'
            }
        }

这里arguments是编译参数,而targets则是相比于add_subdirectory更高权限的方法。一般来说可以把它删去,即默认构建所有目标。

然后在android{}最后添加如下语句,将CMakeLists.txt关联起来。

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

C++库已经创办好了,接下去就要在主模块中选用它了。

为了利用自定义C++库,大家必要一个中间人,它从Android本身的Java程序中拿走请求,然后利用我们的C++库中的函数总括得到结果,并将数据传回Android本身的Java程序中。

创办一个中路文件native-math.cpp

#include <jni.h>
#include <string>
#include "mathlib/add.h"

extern "C"
JNIEXPORT jstring

JNICALL
Java_com_example_bill_opencvtest_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "Hello from C++ From Android openCVTest";
    return env->NewStringUTF(hello.c_str());
}
extern "C"
JNIEXPORT jint JNICALL
Java_com_example_bill_opencvtest_MainActivity_addFromCpp(JNIEnv *env, jobject instance, jint a,
                                                         jint b) {

    // TODO
    return add(a,b);
}

在app/CMakeLists.txt 加上这个自定义库的引用

set(distribution_DIR ${CMAKE_SOURCE_DIR}/../distribution)
include_directories(${distribution_DIR}/include)
add_library(lib_add SHARED IMPORTED)
set_target_properties(lib_add PROPERTIES IMPORTED_LOCATION
                      ${distribution_DIR}/libs/${ANDROID_ABI}/libadd.so)
add_library( # Sets the name of the library.
             native-math

             # Sets the library as a shared library.
             SHARED

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

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 )
set_target_properties(native-math PROPERTIES
                      LIBRARY_OUTPUT_DIRECTORY
                      ${distribution_DIR}/libs/${ANDROID_ABI})
target_link_libraries( # Specifies the target library.
                       native-math
                       android
                       log
                       lib_add
                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib} )

在模块app的局部build.gradle中,像之前一样添加好对应的言语:

defaultConfig{}中:

externalNativeBuild {
            cmake {
                arguments '-DANDROID_PLATFORM=android-19',
                        '-DANDROID_TOOLCHAIN=clang', '-DANDROID_STL=gnustl_static'
            }
        }
        ndk {
            //abiFilters 'armeabi-v7a','x86_64'
        }

其中ndk指定abi平台

ABI(Application binary interface)应用程序二进制接口。区其他CPU
与指令集的每种组巴拿马城有定义的 ABI
(应用程序二进制接口),一段程序唯有依据这几个接口规范才能在该 CPU
上运行,所以一律的程序代码为了协作两个区其余CPU,须求为差别的 ABI
创设差距的库文件。当然对于CPU来说,不一样的架构并不意味着早晚互不包容。

  • armeabi设备只包容armeabi;
  • armeabi-v7a设备包容armeabi-v7a、armeabi;
  • arm64-v8a装置兼容arm64-v8a、armeabi-v7a、armeabi;
  • X86设备包容X86、armeabi;
  • X86_64设施包容X86_64、X86、armeabi;
  • mips64装置极度mips64、mips;
  • mips只兼容mips;

接着在src/main/java/*/MainActivity.java中的MainActivity类上面,加载库,以及安装好相应的主意声明:

static {
        System.loadLibrary("native-math");
    }
    /**
     * A native method that is implemented by the 'native-lib' native library,
     * which is packaged with this application.
     */
    public native String stringFromJNI();

    public native int addFromCpp(int a, int b);

然后就可以在onCreate方法中使用这个C++库定义的函数,在Java中对应的函数了

super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // Example of a call to a native method
        TextView tv = (TextView) findViewById(R.id.sample_text);
        tv.setText(stringFromJNI() + "____" + addFromCpp(2,88));

最后别忘了在项目中添加模块的依赖关系才可以正常运行这个Android App。右键项目OpenCVTest,选择Open Module Settings。选择app->Dependencies,添加Module dependency,选择mathlib,确定即可

添加OpenCV库的支持
导入OpenCV进项目

从OpenCV的官网将OpenCV4Android 3.4下载下来,解压到某个目录。
点击Android Studio的File->New->Import Module,然后选择路径为OpenCV-android-sdk/sdk/java,确定。并在导入之后,修改build.gradle中的SDK版本。
在Open Module Settings中添加模块的依赖关系,使app依赖openCVLibrary340。

现在已经可以在.java文件中看得到OpenCV的自动补全了。配置OpenCV的C++预构建库

把包含文件夹OpenCV-android-sdk/sdk/native/jni/include和预构建库文件夹OpenCV-android-sdk/sdk/native/libs也复制到项目的distribution中。
由于之前已经在添加C++库时修改了app的build.gradle,所以这个步骤现在不需要再执行了。

由于OpenCV是预构建库,所以没有编译的过程,因此模块openCVLibrary320中不需要添加CMakeLists.txt等。我们直接在app模块中根目录下的CMakeLists.txt导入OpenCV的库即可。set(libs "${CMAKE_SOURCE_DIR}/src/main/jniLibs")
include_directories(${distribution_DIR}/include)
# set add lib
add_library(libopencv_java3 SHARED IMPORTED )
set_target_properties(libopencv_java3 PROPERTIES
    IMPORTED_LOCATION "${libs}/${ANDROID_ABI}/libopencv_java3.so")
add_library( # Sets the name of the library.
             native-opencv

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
             src/main/cpp/native-opencv.cpp )
set_target_properties(native-opencv PROPERTIES
                      LIBRARY_OUTPUT_DIRECTORY
                      ${distribution_DIR}/libs/${ANDROID_ABI})
target_link_libraries( # Specifies the target library.
                       native-opencv
                       android
                       log
                       libopencv_java3
                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib} )

亟需注意的是.so使用SHARED.a使用STATIC

只顾:预打造库:so文件和.a文件必须copy在src/main/jniLibs那一个目录,才可以活动被打包。其他路线都不得以,连source这几个命令也不起功能

目前可以运用openCV库了,新建一个文本native-opencv.cpp

//
// Created by bill on 2018/1/13.
//
#include <jni.h>
#include <opencv2/opencv.hpp>
#include <vector>

using namespace cv;
using namespace std;

extern "C"
JNIEXPORT void JNICALL
Java_com_example_bill_opencvtest_MainActivity_nativeProcessFrame(JNIEnv *env, jobject instance,
        jlong addrGray, jlong addrRGBA) {
// TODO
    Mat& gray = *(Mat *) addrGray;
    Mat& rgba = *(Mat *) addrRGBA;
    vector<KeyPoint> v;

    Ptr<ORB> orb = ORB::create();
    orb->detect(gray, v, cv::Mat());

    for (int i = 0; i < v.size(); ++i) {
        const KeyPoint& kp = v[i];
        circle(rgba, Point(kp.pt.x, kp.pt.y), 10, Scalar(255,0,0,255));
    }
}

如今就足以在src/main/java/*/MainActivity.java中听从同等的艺术,载入库,写上艺术申明。最终,如下所示。

static {
        System.loadLibrary("native-opencv");
        System.loadLibrary("native-math");
    }
    /**
     * A native method that is implemented by the 'native-lib' native library,
     * which is packaged with this application.
     */
    public native String stringFromJNI();

    public native int addFromCpp(int a, int b);
    private native void nativeProcessFrame(long addrGray, long addrRGBA);

完整的MainActivity

package com.example.bill.opencvtest;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.WindowManager;
import android.widget.TextView;

import org.opencv.android.CameraBridgeViewBase;
import org.opencv.android.OpenCVLoader;
import org.opencv.core.CvType;
import org.opencv.core.Mat;

public class MainActivity extends Activity implements CameraBridgeViewBase.CvCameraViewListener2{
    static {
        System.loadLibrary("native-opencv");
        System.loadLibrary("native-math");
    }
    /**
     * A native method that is implemented by the 'native-lib' native library,
     * which is packaged with this application.
     */
    public native String stringFromJNI();

    public native int addFromCpp(int a, int b);
    private native void nativeProcessFrame(long addrGray, long addrRGBA);

    private static final String TAG = "MainActivity";

    private Mat rgba;
    private Mat gray;
    private CameraBridgeViewBase mOpenCvCameraView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // Example of a call to a native method
        TextView tv = (TextView) findViewById(R.id.sample_text);
        tv.setText(stringFromJNI() + "____" + addFromCpp(2,88));
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

        mOpenCvCameraView = (CameraBridgeViewBase) findViewById(R.id.activity_camera_view);
        mOpenCvCameraView.setVisibility(CameraBridgeViewBase.VISIBLE);
        mOpenCvCameraView.setCvCameraViewListener(this);
    }

    @Override
    public void onPause() {
        super.onPause();
        if (mOpenCvCameraView != null){
            mOpenCvCameraView.disableView();
        }
    }

    @Override
    public void onResume()
    {
        super.onResume();
        if (!OpenCVLoader.initDebug()) {
            Log.d(TAG, "Internal OpenCV library not found. Using OpenCV Manager for initialization");
        } else {
            Log.d(TAG, "OpenCV library found inside package. Using it!");
            mOpenCvCameraView.enableView();
        }
    }

    public void onDestroy() {
        super.onDestroy();
        if (mOpenCvCameraView != null){
            mOpenCvCameraView.disableView();
        }
    }

    public void onCameraViewStarted(int width, int height){
        rgba = new Mat(height, width, CvType.CV_8UC4);
        gray = new Mat(height, width, CvType.CV_8UC1);
    }

    public void onCameraViewStopped() {
        rgba.release();
        gray.release();
    }

    public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame){
        rgba = inputFrame.rgba();
        gray = inputFrame.gray();
        nativeProcessFrame(gray.getNativeObjAddr(), rgba.getNativeObjAddr());
        return rgba;
    }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:opencv="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.bill.opencvtest.MainActivity">

    <TextView
        android:id="@+id/sample_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
    <org.opencv.android.JavaCameraView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/activity_camera_view"
        opencv:show_fps="true"
        opencv:camera_id="any"/>
</android.support.constraint.ConstraintLayout>
  • 为了欢腾的运用OpenCV
    Library,可以直接在AndroidManifest.xml里面插足如下权限


    http://schemas.android.com/apk/res/android“

    package="com.example.bill.opencvtest">
    
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.CAMERA"/>
    <uses-feature android:name="android.hardware.camera"/>
    <uses-feature android:name="android.hardware.camera.autofocus"/>
    <uses-feature android:name="android.hardware.camera.front"/>
    <uses-feature android:name="android.hardware.camera.front.autofocus"/>
    
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity"
            android:screenOrientation="landscape"
            android:configChanges="keyboardHidden|orientation">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
    
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
    

将源文件涉及到打造系统中
我们用CMake来构建C++库,然后CMake又要和gradle结合,在Android Studio里面协作管理C++和Java的代码。

我们在模块mathlib的根目录下创建一个名为CMakeLists.txt的文件,写入

cmake_minimum_required(VERSION 3.4.1)
add_library(add SHARED
            src/main/cpp/add.cpp)
set(distribution_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../distribution)
set_target_properties(add PROPERTIES
                      LIBRARY_OUTPUT_DIRECTORY
                      ${distribution_DIR}/libs/${ANDROID_ABI})
target_include_directories(add
                           PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src/main/cpp)

add_custom_command(TARGET add POST_BUILD
                   COMMAND "${CMAKE_COMMAND}" -E
                   copy "${CMAKE_CURRENT_SOURCE_DIR}/src/main/cpp/add.h"
                   "${distribution_DIR}/include/mathlib/add.h"
#                   **** the following 2 lines are for potential future debug purpose ****
#                   COMMAND "${CMAKE_COMMAND}" -E
#                   remove_directory "${CMAKE_CURRENT_BINARY_DIR}"
                   COMMENT "Copying gmath to output directory")

set可以自定义变量。这里定义生成so文件的目录

set_target_properties 命令的意思是设置目标的一些属性来改变它们构建的方式。这个命令中设置了 add的ARCHIVE_OUTPUT_DIRECTORY 属性。也就是改变了输出路径。

add_custom_command 命令是自定义命令。命令中把头文件也复制到了 distribution_DIR 中。

target_include_directories,它对创建的库设置include路径,针对目标来设置,可以避免与其他库的冲突,并且此时对自定义的库设置好了此路径后,后续导入这个库就不需要再次设置了。但对于预构建的库,就需要设置,稍后会有详细讲解。

接下来我们在模块mathlib的build.gradle中的defaultConfig{}中添加如下语句:

externalNativeBuild {
            cmake {
                arguments '-DANDROID_PLATFORM=android-19',
                        '-DANDROID_TOOLCHAIN=clang', '-DANDROID_STL=gnustl_static'
                targets 'add'
            }
        }

这里arguments是编译参数,而targets则是相比于add_subdirectory更高权限的方法。一般来说可以把它删去,即默认构建所有目标。

然后在android{}最后添加如下语句,将CMakeLists.txt关联起来。

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

C++库已经创办好了,接下去就要在主模块中拔取它了。

为了接纳自定义C++库,我们必要一个中间人,它从Android本身的Java程序中获取请求,然后利用大家的C++库中的函数计算获得结果,并将数据传回Android本身的Java程序中。

创制一个当汉语件native-math.cpp

#include <jni.h>
#include <string>
#include "mathlib/add.h"

extern "C"
JNIEXPORT jstring

JNICALL
Java_com_example_bill_opencvtest_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "Hello from C++ From Android openCVTest";
    return env->NewStringUTF(hello.c_str());
}
extern "C"
JNIEXPORT jint JNICALL
Java_com_example_bill_opencvtest_MainActivity_addFromCpp(JNIEnv *env, jobject instance, jint a,
                                                         jint b) {

    // TODO
    return add(a,b);
}

在app/CMakeLists.txt 加上这个自定义库的引用

set(distribution_DIR ${CMAKE_SOURCE_DIR}/../distribution)
include_directories(${distribution_DIR}/include)
add_library(lib_add SHARED IMPORTED)
set_target_properties(lib_add PROPERTIES IMPORTED_LOCATION
                      ${distribution_DIR}/libs/${ANDROID_ABI}/libadd.so)
add_library( # Sets the name of the library.
             native-math

             # Sets the library as a shared library.
             SHARED

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

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 )
set_target_properties(native-math PROPERTIES
                      LIBRARY_OUTPUT_DIRECTORY
                      ${distribution_DIR}/libs/${ANDROID_ABI})
target_link_libraries( # Specifies the target library.
                       native-math
                       android
                       log
                       lib_add
                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib} )

在模块app的局部build.gradle中,像此前一样添加好对应的语句:

defaultConfig{}中:

externalNativeBuild {
            cmake {
                arguments '-DANDROID_PLATFORM=android-19',
                        '-DANDROID_TOOLCHAIN=clang', '-DANDROID_STL=gnustl_static'
            }
        }
        ndk {
            //abiFilters 'armeabi-v7a','x86_64'
        }

其中ndk指定abi平台

ABI(Application binary interface)应用程序二进制接口。分化的CPU
与指令集的每种组天津有定义的 ABI
(应用程序二进制接口),一段程序唯有依据那些接口规范才能在该 CPU
上运行,所以同样的程序代码为了合营四个不等的CPU,须求为不一致的 ABI
营造分裂的库文件。当然对于CPU来说,不相同的架构并不意味一定互不包容。

  • armeabi设备只包容armeabi;
  • armeabi-v7a设备包容armeabi-v7a、armeabi;
  • arm64-v8a设施包容arm64-v8a、armeabi-v7a、armeabi;
  • X86设备兼容X86、armeabi;
  • X86_64装备包容X86_64、X86、armeabi;
  • mips64设备万分mips64、mips;
  • mips只兼容mips;

接着在src/main/java/*/MainActivity.java中的MainActivity类下边,加载库,以及安装好相应的措施声明:

static {
        System.loadLibrary("native-math");
    }
    /**
     * A native method that is implemented by the 'native-lib' native library,
     * which is packaged with this application.
     */
    public native String stringFromJNI();

    public native int addFromCpp(int a, int b);

然后就可以在onCreate方法中使用这个C++库定义的函数,在Java中对应的函数了

super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // Example of a call to a native method
        TextView tv = (TextView) findViewById(R.id.sample_text);
        tv.setText(stringFromJNI() + "____" + addFromCpp(2,88));

最后别忘了在项目中添加模块的依赖关系才可以正常运行这个Android App。右键项目OpenCVTest,选择Open Module Settings。选择app->Dependencies,添加Module dependency,选择mathlib,确定即可

添加OpenCV库的支持
导入OpenCV进项目

从OpenCV的官网将OpenCV4Android 3.4下载下来,解压到某个目录。
点击Android Studio的File->New->Import Module,然后选择路径为OpenCV-android-sdk/sdk/java,确定。并在导入之后,修改build.gradle中的SDK版本。
在Open Module Settings中添加模块的依赖关系,使app依赖openCVLibrary340。

现在已经可以在.java文件中看得到OpenCV的自动补全了。配置OpenCV的C++预构建库

把包含文件夹OpenCV-android-sdk/sdk/native/jni/include和预构建库文件夹OpenCV-android-sdk/sdk/native/libs也复制到项目的distribution中。
由于之前已经在添加C++库时修改了app的build.gradle,所以这个步骤现在不需要再执行了。

由于OpenCV是预构建库,所以没有编译的过程,因此模块openCVLibrary320中不需要添加CMakeLists.txt等。我们直接在app模块中根目录下的CMakeLists.txt导入OpenCV的库即可。set(libs "${CMAKE_SOURCE_DIR}/src/main/jniLibs")
include_directories(${distribution_DIR}/include)
# set add lib
add_library(libopencv_java3 SHARED IMPORTED )
set_target_properties(libopencv_java3 PROPERTIES
    IMPORTED_LOCATION "${libs}/${ANDROID_ABI}/libopencv_java3.so")
add_library( # Sets the name of the library.
             native-opencv

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
             src/main/cpp/native-opencv.cpp )
set_target_properties(native-opencv PROPERTIES
                      LIBRARY_OUTPUT_DIRECTORY
                      ${distribution_DIR}/libs/${ANDROID_ABI})
target_link_libraries( # Specifies the target library.
                       native-opencv
                       android
                       log
                       libopencv_java3
                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib} )

需求留意的是.so使用SHARED.a使用STATIC

留神:预营造库:so文件和.a文件必须copy在src/main/jniLibs这么些目录,才得以自动被卷入。其余途径都不可以,连source那几个命令也不起成效

现行得以应用openCV库了,新建一个文本native-opencv.cpp

//
// Created by bill on 2018/1/13.
//
#include <jni.h>
#include <opencv2/opencv.hpp>
#include <vector>

using namespace cv;
using namespace std;

extern "C"
JNIEXPORT void JNICALL
Java_com_example_bill_opencvtest_MainActivity_nativeProcessFrame(JNIEnv *env, jobject instance,
        jlong addrGray, jlong addrRGBA) {
// TODO
    Mat& gray = *(Mat *) addrGray;
    Mat& rgba = *(Mat *) addrRGBA;
    vector<KeyPoint> v;

    Ptr<ORB> orb = ORB::create();
    orb->detect(gray, v, cv::Mat());

    for (int i = 0; i < v.size(); ++i) {
        const KeyPoint& kp = v[i];
        circle(rgba, Point(kp.pt.x, kp.pt.y), 10, Scalar(255,0,0,255));
    }
}

近期就可以在src/main/java/*/MainActivity.java中根据同样的方法,载入库,写上方式表明。最终,如下所示。

static {
        System.loadLibrary("native-opencv");
        System.loadLibrary("native-math");
    }
    /**
     * A native method that is implemented by the 'native-lib' native library,
     * which is packaged with this application.
     */
    public native String stringFromJNI();

    public native int addFromCpp(int a, int b);
    private native void nativeProcessFrame(long addrGray, long addrRGBA);

完整的MainActivity

package com.example.bill.opencvtest;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.WindowManager;
import android.widget.TextView;

import org.opencv.android.CameraBridgeViewBase;
import org.opencv.android.OpenCVLoader;
import org.opencv.core.CvType;
import org.opencv.core.Mat;

public class MainActivity extends Activity implements CameraBridgeViewBase.CvCameraViewListener2{
    static {
        System.loadLibrary("native-opencv");
        System.loadLibrary("native-math");
    }
    /**
     * A native method that is implemented by the 'native-lib' native library,
     * which is packaged with this application.
     */
    public native String stringFromJNI();

    public native int addFromCpp(int a, int b);
    private native void nativeProcessFrame(long addrGray, long addrRGBA);

    private static final String TAG = "MainActivity";

    private Mat rgba;
    private Mat gray;
    private CameraBridgeViewBase mOpenCvCameraView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // Example of a call to a native method
        TextView tv = (TextView) findViewById(R.id.sample_text);
        tv.setText(stringFromJNI() + "____" + addFromCpp(2,88));
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

        mOpenCvCameraView = (CameraBridgeViewBase) findViewById(R.id.activity_camera_view);
        mOpenCvCameraView.setVisibility(CameraBridgeViewBase.VISIBLE);
        mOpenCvCameraView.setCvCameraViewListener(this);
    }

    @Override
    public void onPause() {
        super.onPause();
        if (mOpenCvCameraView != null){
            mOpenCvCameraView.disableView();
        }
    }

    @Override
    public void onResume()
    {
        super.onResume();
        if (!OpenCVLoader.initDebug()) {
            Log.d(TAG, "Internal OpenCV library not found. Using OpenCV Manager for initialization");
        } else {
            Log.d(TAG, "OpenCV library found inside package. Using it!");
            mOpenCvCameraView.enableView();
        }
    }

    public void onDestroy() {
        super.onDestroy();
        if (mOpenCvCameraView != null){
            mOpenCvCameraView.disableView();
        }
    }

    public void onCameraViewStarted(int width, int height){
        rgba = new Mat(height, width, CvType.CV_8UC4);
        gray = new Mat(height, width, CvType.CV_8UC1);
    }

    public void onCameraViewStopped() {
        rgba.release();
        gray.release();
    }

    public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame){
        rgba = inputFrame.rgba();
        gray = inputFrame.gray();
        nativeProcessFrame(gray.getNativeObjAddr(), rgba.getNativeObjAddr());
        return rgba;
    }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:opencv="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.bill.opencvtest.MainActivity">

    <TextView
        android:id="@+id/sample_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
    <org.opencv.android.JavaCameraView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/activity_camera_view"
        opencv:show_fps="true"
        opencv:camera_id="any"/>
</android.support.constraint.ConstraintLayout>
  • 为了兴奋的利用OpenCV
    Library,可以一向在AndroidManifest.xml里面加入如下权限


    http://schemas.android.com/apk/res/android“

    package="com.example.bill.opencvtest">
    
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.CAMERA"/>
    <uses-feature android:name="android.hardware.camera"/>
    <uses-feature android:name="android.hardware.camera.autofocus"/>
    <uses-feature android:name="android.hardware.camera.front"/>
    <uses-feature android:name="android.hardware.camera.front.autofocus"/>
    
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity"
            android:screenOrientation="landscape"
            android:configChanges="keyboardHidden|orientation">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
    
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
    

相关文章