——创建项目
——文件说明及修改
——编译及运行
——创建项目
——文件的修改
——编译及运行
—小结
——NDK,OpenCV Java API,OpenCV JNI的关系
—参考资料
Windows 7 x64 旗舰版
Android Studio 2.1.2
JDK 1.8.0
Android 6.0(API 23)
OpenCV 3.1.0 Android SDK
Android NDK r12b
1.按照《在Android Studio上进行OpenCV 3.1开发》中环境配置和Android模拟器两部分所介绍的步骤下载Android SDK并创建一个虚拟设备。
2.打开Android Studio 2.1.2,点击图标打开SDK Manager。选择SDK Tools,勾选NDK如下图(我已经安装了NDK,所以细节上会有些不同,不过步骤是正确的):
再点击Apply,软件就会自动下载NDK。目前(2016.07.08)最新版本为r12b。下载完成后会自动保存在<Android SDK>\ndk-bundle目录下,比如我的是E:\dev-lib\android-sdk\ndk-bundle。也可以到官网手动下载所需的版本(https://developer.android.com/ndk/downloads/index.html),然后解压到一个合适的位置。此外,推荐一个开源的NDK:CrystaX NDK,据说在对C++11的支持方面以及与Boost的配合上更胜一筹(与Android NDK r10e相比,更新版本的比较情况暂时未知)。
在ndk-bundle文件夹的CHANGELOG.md文件中可以查看目前的版本以及对应过去版本所产生的修改。几个月前我在使用Android Studio 1.5.1时,是不能直接通过SDK Manager下载软件包的。所幸,现在网络直连也可以了,而且下载速度不低:)
Gradle Experimental Plugin基于Gradle的新特性开发,目的在于减少项目配置时间,并提供更好的NDK支持。由于仍然处在开发阶段,所以有些功能暂不支持,而且已有的功能也在频繁地修改。所以尝尝鲜就好,如果要结合OpenCV这种量级的函数库来进行NDK开发,目前我还是推荐采用稳定的Gradle Plugin,详见OpenCV NDK应用一节。
实验性插件对Gradle版本的要求见下表(摘自http://tools.android.com/tech-docs/new-build-system/gradle-experimental):
Gradle的版本可以在File->Project Structure中看到:
1.打开Android Studio,新建一个项目,应用名(Application name)为NDKdemo,包名(Package name)为net.johnhany.ndkdemo。我的项目路径(Project location)为E:\projects\Android\NDKdemo,如下图所示:
2.点击Next,保持默认不变,再点击Next:
3.选择默认的Empty Activity,再点击Next:
4.保持默认Activity和Layout不变,点击Finish,建立项目:
5.项目创建好后,在下图所示的位置选择Project:
6.在源码目录创建一个jni文件夹。右击main文件夹,选择New->Directory:
在弹出的窗口中输入“jni”,然后点击OK:
7.在jni目录创建一个c文件。右击jni文件夹,选择New->C/C++ Source File:
在弹出的窗口中输入“hello-jni”,并选择.c类型:
此时项目的文件结构如下图所示,比较重要的文件用红色框标记:
1.app\src\main\java\net\johnhany\ndkdemo\MainActivity.java包含主要的Java代码,内容修改为:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
package
net
.
johnhany
.
ndkdemo
;
import
android
.
support
.
v7
.
app
.
AppCompatActivity
;
import
android
.
widget
.
TextView
;
import
android
.
os
.
Bundle
;
public
class
MainActivity
extends
AppCompatActivity
{
@
Override
protected
void
onCreate
(
Bundle
savedInstanceState
)
{
super
.
onCreate
(
savedInstanceState
)
;
setContentView
(
R
.
layout
.
activity_main
)
;
TextView
tv
=
(
TextView
)
findViewById
(
R
.
id
.
textview
)
;
tv
.
setText
(
stringFromJNI
(
)
)
;
}
public
native
String
stringFromJNI
(
)
;
static
{
System
.
loadLibrary
(
"hello-jni"
)
;
}
}
|
2.app\src\main\jni\hello-jni.c包含主要的C代码,内容修改为:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
#include <jni.h>
jstring
Java_net_johnhany_ndkdemo_MainActivity_stringFromJNI
(
JNIEnv*
env
,
jobject
thiz
)
{
#if defined(__arm__)
#if defined(__ARM_ARCH_7A__)
#if defined(__ARM_NEON__)
#if defined(__ARM_PCS_VFP)
#define ABI "armeabi-v7a/NEON (hard-float)"
#else
#define ABI "armeabi-v7a/NEON"
#endif
#else
#if defined(__ARM_PCS_VFP)
#define ABI "armeabi-v7a (hard-float)"
#else
#define ABI "armeabi-v7a"
#endif
#endif
#else
#define ABI "armeabi"
#endif
#elif defined(__i386__)
#define ABI "x86"
#elif defined(__x86_64__)
#define ABI "x86_64"
#elif defined(__mips64) /* mips64el-* toolchain defines __mips__ too */
#define ABI "mips64"
#elif defined(__mips__)
#define ABI "mips"
#elif defined(__aarch64__)
#define ABI "arm64-v8a"
#else
#define ABI "unknown"
#endif
return
(
*
env
)
->
NewStringUTF
(
env
,
"Hello from JNI! Compiled with ABI "
ABI
"."
)
;
}
|
3.app\src\main\res\layout\activity_main.xml用来设置应用的Layout,内容修改如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
<?
xml
version
=
"1.0"
encoding
=
"utf-8"
?>
<
RelativeLayout
xmlns
:
android
=
"http://schemas.android.com/apk/res/android"
xmlns
:
tools
=
"http://schemas.android.com/tools"
android
:
layout_width
=
"match_parent"
android
:
layout_height
=
"match_parent"
android
:
paddingBottom
=
"@dimen/activity_vertical_margin"
android
:
paddingLeft
=
"@dimen/activity_horizontal_margin"
android
:
paddingRight
=
"@dimen/activity_horizontal_margin"
android
:
paddingTop
=
"@dimen/activity_vertical_margin"
tools
:
context
=
"net.johnhany.ndkdemo.MainActivity"
>
<
TextView
android
:
id
=
"@+id/textview"
android
:
layout_width
=
"match_parent"
android
:
layout_height
=
"match_parent"
/
>
<
/
RelativeLayout
>
|
4.app\src\main\res\values\strings.xml规定了应用Layout中会引用的字符常量,比如应用名称、标题栏名称、按钮上的文字等等。可以不作修改。
5.app\src\main\AndroidManifest.xml用来规定Activity之间的关系,以及申请摄像头、内存读写权限等。可以不作修改。
6.app\build.gradle文件确定Android平台、编译工具及选项、依赖库等重要信息,修改如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
apply
plugin
:
'com.android.model.application'
model
{
android
{
compileSdkVersion
23
buildToolsVersion
"24.0.0"
defaultConfig
{
applicationId
"net.johnhany.ndkdemo"
minSdkVersion
.
apiLevel
15
targetSdkVersion
.
apiLevel
23
versionCode
1
versionName
"1.0"
}
ndk
{
moduleName
=
"hello-jni"
toolchain
=
"clang"
ldLibs
.
addAll
(
[
'log'
]
)
cppFlags
.
add
(
"-std=c++11"
)
cppFlags
.
add
(
"-fexceptions"
)
platformVersion
=
23
stl
=
'gnustl_shared'
}
buildTypes
{
release
{
minifyEnabled
false
proguardFiles
.
add
(
file
(
"proguard-rules.pro"
)
)
}
}
}
}
dependencies
{
compile
fileTree
(
dir
:
"libs"
,
include
:
[
"*.jar"
]
)
testCompile
'junit:junit:4.12'
compile
"com.android.support:appcompat-v7:23.2.1"
}
|
由于我在ndk-bundle\CHANGELOG.md中发现有这样一行'ndk-build' will default to using Clang in r13. GCC will be removed in a later release.,所以我这里工具链也采用了Clang。
7.项目根目录下的build.gradle只需要修改一处,把buildscript.dependencies中的classpath改为'com.android.tools.build:gradle-experimental:0.7.0',表示采用实验性插件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript
{
repositories
{
jcenter
(
)
}
dependencies
{
classpath
'com.android.tools.build:gradle-experimental:0.7.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects
{
repositories
{
jcenter
(
)
}
}
task
clean
(
type
:
Delete
)
{
delete
rootProject
.
buildDir
}
|
8.gradle.properties文件暂时不需要修改。在某些情况下,Android Studio会提示你在该文件内添加android.useDeprecatedNdk=true,以启用老版本NDK的某些功能,不过我们这里给出的两个项目都不需要。
9.local.properties文件规定了Android SDK和Android NDK的路径,默认不需要修改。如果是手动下载的NDK,则需要在这里设置NDK的路径:
1
2
|
ndk
.
dir
=
E
\
:
\
\
dev
-
lib
\
\
android
-
sdk
\
\
ndk
-
bundle
sdk
.
dir
=
E
\
:
\
\
dev
-
lib
\
\
android
-
sdk
|
先点击一下按钮,检查一下Gradle配置是否正确。如果没有错误信息,确认虚拟设备已经在运行或者已经通过USB线连接了Android手机,则点击按钮,Android Studio就会开始自动编译、打包、安装及运行。运行效果如下图:
新建项目的过程与前文类似,这里就不赘述了。应用名称为OpenCV3JNI,包名为net.johnhany.opencv3jni,项目路径为E:\projects\Android\OpenCV3JNI。
只不过要在jni文件夹中创建3个文件:Android.mk,Application.mk以及gray-process.cpp:
还要在app\src\main\res\drawable中准备一幅名为pic.png的图片,我使用的图片如下:
此外,需要注意的是,如果你的Java代码中没有出现任何OpenCV相关的变量和函数,则不需要向项目中导入OpenCV库,也不需要在设备上安装OpenCV Manager,在后续步骤中也不需要把OpenCV Android SDK中的opencv_java3.so手动拷贝到项目目录下。如果在Java代码中调用了OpenCV的Java接口,则请按照《在Android Studio上进行OpenCV 3.1开发》为项目配置OpenCV的完整过程将OpenCV库导入到项目中。
1.app\src\main\java\net\johnhany\opencv3jni\MainActivity.java内容修改为:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
|
package
net
.
johnhany
.
opencv3jni
;
import
android
.
support
.
v7
.
app
.
AppCompatActivity
;
import
android
.
os
.
Bundle
;
import
android
.
graphics
.
Bitmap
;
import
android
.
graphics
.
BitmapFactory
;
import
android
.
graphics
.
Bitmap
.
Config
;
import
android
.
view
.
View
;
import
android
.
view
.
View
.
OnClickListener
;
import
android
.
widget
.
Button
;
import
android
.
widget
.
ImageView
;
public
class
MainActivity
extends
AppCompatActivity
implements
OnClickListener
{
private
Button
btnProc
;
private
ImageView
imageView
;
private
Bitmap
bmp
;
public
static
native
int
[
]
grayProc
(
int
[
]
pixels
,
int
w
,
int
h
)
;
static
{
System
.
loadLibrary
(
"gray-process"
)
;
}
@
Override
public
void
onCreate
(
Bundle
savedInstanceState
)
{
super
.
onCreate
(
savedInstanceState
)
;
setContentView
(
R
.
layout
.
activity_main
)
;
btnProc
=
(
Button
)
findViewById
(
R
.
id
.
btn_gray_process
)
;
imageView
=
(
ImageView
)
findViewById
(
R
.
id
.
image_view
)
;
bmp
=
BitmapFactory
.
decodeResource
(
getResources
(
)
,
R
.
drawable
.
pic
)
;
imageView
.
setImageBitmap
(
bmp
)
;
btnProc
.
setOnClickListener
(
this
)
;
}
@
Override
public
void
onClick
(
View
v
)
{
int
w
=
bmp
.
getWidth
(
)
;
int
h
=
bmp
.
getHeight
(
)
;
int
[
]
pixels
=
new
int
[
w*
h
]
;
bmp
.
getPixels
(
pixels
,
0
,
w
,
0
,
0
,
w
,
h
)
;
int
[
]
resultInt
=
grayProc
(
pixels
,
w
,
h
)
;
Bitmap
resultImg
=
Bitmap
.
createBitmap
(
w
,
h
,
Config
.
ARGB_8888
)
;
resultImg
.
setPixels
(
resultInt
,
0
,
w
,
0
,
0
,
w
,
h
)
;
imageView
.
setImageBitmap
(
resultImg
)
;
}
@
Override
public
void
onResume
(
)
{
super
.
onResume
(
)
;
}
}
|
2.app\src\main\jni\Android.mk文件的内容为:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
LOCAL_PATH
:
=
$
(
call
my
-
dir
)
include
$
(
CLEAR_VARS
)
#opencv
include
E
:
\
\
dev
-
lib
\
\
OpenCV
-
android
-
sdk
\
\
sdk
\
\
native
\
\
jni
\
\
OpenCV
.
mk
OPENCV_CAMERA_MODULES
:
=
on
OPENCV_INSTALL_MODULES
:
=
on
OPENCV_LIB_TYPE
:
=
SHARED
LOCAL_SRC_FILES
:
=
gray
-
process
.
cpp
LOCAL_LDLIBS
+=
-
llog
LOCAL_MODULE
:
=
gray
-
process
include
$
(
BUILD_SHARED_LIBRARY
)
|
其中,E:\\dev-lib\\OpenCV-android-sdk\\sdk\\native\\jni\\OpenCV.mk要指向你的OpenCV Android SDK中对应文件的位置。
OPENCV_CAMERA_MODULES:=on说明要使用摄像头功能,我们的demo中虽然没有用到,但是平时写程序时经常会忘记在这里添加,所以一并写上了。
OPENCV_INSTALL_MODULES:=on可以理解为,编译器自动把OpenCV-android-sdk\sdk\native\libs\x86\libopencv_java3.so拷贝并打包在最终的apk中(目标架构x86因目标设备而定),这也是不需要我们手动向项目内添加任何OpenCV相关库文件的原因。
LOCAL_SRC_FILES是需要编译的C/C++源码文件,如果有多个文件,只需要用空格将文件名分隔,如果文件名太多需要换行,则在前一行行末添加一个“\”符号即可。
LOCAL_MODULE是编译产生的.so库的名称,应该与MainActivity.java中通过System.loadLibrary("gray-process");调用的库名称一致。
3.app\src\main\jni\Application.mk文件的内容为:
1
2
3
4
|
APP_STL
:
=
gnustl_shared
APP_CPPFLAGS
:
=
-
frtti
-
fexceptions
-
std
=
c
++
11
APP_ABI
:
=
armeabi
-
v7a
x86
APP_PLATFORM
:
=
android
-
15
|
其中,APP_CPPFLAGS中的-std=c++11表示开启C++11支持。
APP_ABI规定了NDK编译的目标平台,由于我只想测试这两个架构,所以只写了两个,不过当然是越齐全越好:)
APP_PLATFORM指定了NDK编译针对的平台,其取值应该尽量与app\build.gradle中的minSdkVersion相一致。参考这里:http://stackoverflow.com/a/21982908/3829845。
4.app\src\main\jni\gray-process.cpp文件内容如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
#include <jni.h>
#include <opencv2/opencv.hpp>
extern
"C"
{
using
namespace
cv
;
using
namespace
std
;
JNIEXPORT
jintArray
JNICALL
Java_net_johnhany_opencv3jni_MainActivity_grayProc
(
JNIEnv *
env
,
jclass
obj
,
jintArray
buf
,
jint
w
,
jint
h
)
{
jboolean
ptfalse
=
false
;
jint*
srcBuf
=
env
->
GetIntArrayElements
(
buf
,
&
ptfalse
)
;
if
(
srcBuf
==
NULL
)
{
return
0
;
}
int
size
=
w *
h
;
Mat
srcImage
(
h
,
w
,
CV_8UC4
,
(
unsigned
char
*
)
srcBuf
)
;
Mat
grayImage
;
cvtColor
(
srcImage
,
grayImage
,
COLOR_BGRA2GRAY
)
;
cvtColor
(
grayImage
,
srcImage
,
COLOR_GRAY2BGRA
)
;
jintArray
result
=
env
->
NewIntArray
(
size
)
;
env
->
SetIntArrayRegion
(
result
,
0
,
size
,
srcBuf
)
;
env
->
ReleaseIntArrayElements
(
buf
,
srcBuf
,
0
)
;
return
result
;
}
}
|
5.app\src\main\res\layout\activity_main.xml的内容修改为:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
<?
xml
version
=
"1.0"
encoding
=
"utf-8"
?>
<
RelativeLayout
xmlns
:
android
=
"http://schemas.android.com/apk/res/android"
xmlns
:
tools
=
"http://schemas.android.com/tools"
android
:
layout_width
=
"match_parent"
android
:
layout_height
=
"match_parent"
android
:
paddingBottom
=
"@dimen/activity_vertical_margin"
android
:
paddingLeft
=
"@dimen/activity_horizontal_margin"
android
:
paddingRight
=
"@dimen/activity_horizontal_margin"
android
:
paddingTop
=
"@dimen/activity_vertical_margin"
tools
:
context
=
"net.johnhany.opencv3jni.MainActivity"
>
<
Button
android
:
id
=
"@+id/btn_gray_process"
android
:
layout_width
=
"fill_parent"
android
:
layout_height
=
"wrap_content"
android
:
text
=
"@string/str_proc"
/
>
<
ImageView
android
:
id
=
"@+id/image_view"
android
:
layout_width
=
"wrap_content"
android
:
layout_height
=
"wrap_content"
android
:
contentDescription
=
"@string/str_proc"
android
:
layout_marginTop
=
"60dp"
android
:
layout_centerHorizontal
=
"true"
/
>
<
/
RelativeLayout
>
|
6.app\src\main\res\values\strings.xml的内容为:
1
2
3
4
5
6
|
<
resources
>
<
string
name
=
"app_name"
>
Gray
Process
-
OpenCV3
JNI
<
/
string
>
<
string
name
=
"str_proc"
>
gray
process
<
/
string
>
<
string
name
=
"str_desc"
>
image
description
<
/
string
>
<
/
resources
>
|
7.app\build.gradle文件的内容改为:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
|
import
org
.
apache
.
tools
.
ant
.
taskdefs
.
condition
.
Os
apply
plugin
:
'com.android.application'
android
{
compileSdkVersion
23
buildToolsVersion
"24.0.0"
defaultConfig
{
applicationId
"net.johnhany.opencv3jni"
minSdkVersion
15
targetSdkVersion
23
versionCode
1
versionName
"1.0"
}
sourceSets
.
main
{
jniLibs
.
srcDirs
=
[
'src/main/libs'
,
'src/main/jniLibs'
]
jni
.
srcDirs
=
[
]
//disable automatic ndk-build call
}
project
.
ext
.
versionCodes
=
[
'armeabi'
:
1
,
'armeabi-v7a'
:
2
,
'arm64-v8a'
:
3
,
'mips'
:
5
,
'mips64'
:
6
,
'x86'
:
8
,
'x86_64'
:
9
]
//versionCode digit for each supported ABI, with 64bit>32bit and x86>armeabi-*
android
.
applicationVariants
.
all
{
variant
->
// assign different version code for each output
variant
.
outputs
.
each
{
output
->
output
.
versionCodeOverride
=
project
.
ext
.
versionCodes
.
get
(
output
.
getFilter
(
com
.
android
.
build
.
OutputFile
.
ABI
)
,
0
)
*
1000000
+
defaultConfig
.
versionCode
}
}
// call regular ndk-build(.cmd) script from app directory
task
ndkBuild
(
type
:
Exec
)
{
Properties
properties
=
new
Properties
(
)
properties
.
load
(
project
.
rootProject
.
file
(
'local.properties'
)
.
newDataInputStream
(
)
)
def
ndkDir
=
properties
.
getProperty
(
'ndk.dir'
)
if
(
Os
.
isFamily
(
Os
.
FAMILY_WINDOWS
)
)
{
commandLine
"$ndkDir/ndk-build.cmd"
,
'-C'
,
file
(
'src/main/jni'
)
.
absolutePath
}
else
{
commandLine
"$ndkDir/ndk-build"
,
'-C'
,
file
(
'src/main/jni'
)
.
absolutePath
}
}
tasks
.
withType
(
JavaCompile
)
{
compileTask
->
compileTask
.
dependsOn
ndkBuild
}
buildTypes
{
release
{
minifyEnabled
false
proguardFiles
getDefaultProguardFile
(
'proguard-android.txt'
)
,
'proguard-rules.pro'
}
}
}
dependencies
{
compile
fileTree
(
dir
:
"libs"
,
include
:
[
"*.jar"
]
)
testCompile
'junit:junit:4.12'
compile
'com.android.support:appcompat-v7:23.2.1'
// compile project(':openCVLibrary310')
}
|
有些文章中在android.defaultConfig中添加了一个ndk{},用来声明本地库的名称,其实它的作用与Android.mk中的LOCAL_MODULE是相同的,这样不免有些重复,而且在修改文件名时容易出错,所以我把它去掉了。调用ndk-build.cmd的部分参考了NDK的官方样例。
请注意文件末尾被注释掉的compile project(':openCVLibrary310'),这一点前面提到过,如果在Java中调用了OpenCV,这里就要取消掉注释,以编译项目中所导入的OpenCV Java部分。
8.根目录的build.gradle文件中也只有一行需要注意,就是classpath那一行,如下:
1
2
3
|
dependencies
{
classpath
'com.android.tools.build:gradle:2.1.2'
}
|
9.与初级NDK应用中相同地,gradle.properties,local.properties与app\src\main\AndroidManifest.xml文件保持默认不变。
与前一节相同,点击按钮,开始编译和运行。运行效果如下图所示:
Instant Run的优势在于,对已经编译的项目,如果项目源码改动不大,重编译和部署到设备的时间就会显著缩短。只要在File->Project Structure中把Android Plugin Version设为2.1.2,Instant Run就会自动开启。不过这也意味着Instant Run与Gradle Experimental Plugin是不可兼得的。
对于NDK开发,Instant Run的限制在于,NDK编译产生的.so库属于resource文件,而当resource文件或者Layout发生改变时,项目是需要强制重新编译的。所以对C++代码的任何改动都不属于Instant Run的支持范围,需要重新编译。
通过比较前面两个项目的配置文件你会发现app\build.gradle文件的区别:
1.采用gradle-experimental:0.7.0时,启用实验性Gradle插件,此时:
(1)apply plugin为'com.android.model.application';
(2)android{}被包含在model{}内;
(3)ndk{}与defaultConfig{}呈平等关系;
(4)API版本需要用.apiLevel来指定;
(5)属性和标记需要用.add()或.addAll()来添加;
(6)可以直接指定工具链(toolchain);
(7)可以直接指定ldLibs,cppFlags,stl等flag。
2.采用gradle:2.1.2时,可以使用Instant Run,此时:
(1)apply plugin为'com.android.application';
(2)没有model{},只有android{};
(3)ndk{}被包含在defaultConfig{}内;
(4)API版本用targetSdkVersion 23这样的语句来指定,不需要用.apiLevel;
(5)不能指定工具链,ldLibs,cppFlags,stl等,而且不能使用.add()或.addAll()等语句。
关于NDK,OpenCV Java API,OpenCV JNI之间的关系可以这样理解:
(1)Android项目的Java代码中调用了OpenCV,就需要使用OpenCV Java API,需要向项目中导入OpenCV-Android-SDK\sdk\java库,需要在设备上安装OpenCV Manager,需要在app\build.gradle的dependencies中添加compile project(':openCVLibrary310')来明确编译OpenCV的Java源码,在Java代码中需要用OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_3_1_0, this, mOpenCVCallBack);来明确调用OpenCV库;如果项目的Java代码中没有调用OpenCV,以上所有工作都不是必需的。
(2)Android项目的C/C++代码中调用了OpenCV,就需要使用NDK对OpenCV的JNI进行编译,需要在build.gradle和app\build.gradle(根据需要,添加Android.mk以及Application.mk)中提供编译工具(ndk-build.cmd),工具链(Clang或GCC),依赖库(OpenCV.mk中定义),源码文件,库名称等信息。
http://johnhany.net/2016/07/opencv-3-1-ndk-dev-on-android-studio-2/