Android Studio 2上利用NDK进行OpenCV 3.1开发
2017-07-13 18:01
701 查看
—开发环境
—准备工作
—初级NDK应用
——实验性Gradle插件
——创建项目
——文件说明及修改
——编译及运行
—OpenCV NDK应用
——创建项目
——文件的修改
——编译及运行
—Instant Run
—小结
——两次app\build.gradle文件的区别
——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下载软件包的。所幸,现在网络直连也可以了,而且下载速度不低:)
初级NDK应用
实验性Gradle插件:
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代码,内容修改为:
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代码,内容修改为:
#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,内容修改如下:
<?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平台、编译工具及选项、依赖库等重要信息,修改如下:
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',表示采用实验性插件:
// 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的路径:
ndk.dir=E\:\\dev-lib\\android-sdk\\ndk-bundle
sdk.dir=E\:\\dev-lib\\android-sdk
编译及运行:
先点击一下
按钮,检查一下Gradle配置是否正确。如果没有错误信息,确认虚拟设备已经在运行或者已经通过USB线连接了Android手机,则点击
按钮,Android
Studio就会开始自动编译、打包、安装及运行。运行效果如下图:
OpenCV NDK应用
创建项目:
新建项目的过程与前文类似,这里就不赘述了。应用名称为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内容修改为:
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文件的内容为:
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文件的内容为:
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文件内容如下:
#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的内容修改为:
<?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的内容为:
<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文件的内容改为:
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那一行,如下:
dependencies {
classpath 'com.android.tools.build:gradle:2.1.2'
}
9.与初级NDK应用中相同地,gradle.properties,local.properties与app\src\main\AndroidManifest.xml文件保持默认不变。
编译及运行:
与前一节相同,点击
按钮,开始编译和运行。运行效果如下图所示:
Instant Run
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/
—准备工作
—初级NDK应用
——实验性Gradle插件
——创建项目
——文件说明及修改
——编译及运行
—OpenCV NDK应用
——创建项目
——文件的修改
——编译及运行
—Instant Run
—小结
——两次app\build.gradle文件的区别
——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下载软件包的。所幸,现在网络直连也可以了,而且下载速度不低:)
初级NDK应用
实验性Gradle插件:
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代码,内容修改为:
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");
}
}
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"); } } |
#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 ".");
}
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 "."); } |
<?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>
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> |
5.app\src\main\AndroidManifest.xml用来规定Activity之间的关系,以及申请摄像头、内存读写权限等。可以不作修改。
6.app\build.gradle文件确定Android平台、编译工具及选项、依赖库等重要信息,修改如下:
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"
}
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" } |
7.项目根目录下的build.gradle只需要修改一处,把buildscript.dependencies中的classpath改为'com.android.tools.build:gradle-experimental:0.7.0',表示采用实验性插件:
// 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
}
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 } |
9.local.properties文件规定了Android SDK和Android NDK的路径,默认不需要修改。如果是手动下载的NDK,则需要在这里设置NDK的路径:
ndk.dir=E\:\\dev-lib\\android-sdk\\ndk-bundle
sdk.dir=E\:\\dev-lib\\android-sdk
1 2 | ndk.dir=E\:\\dev-lib\\android-sdk\\ndk-bundle sdk.dir=E\:\\dev-lib\\android-sdk |
先点击一下
按钮,检查一下Gradle配置是否正确。如果没有错误信息,确认虚拟设备已经在运行或者已经通过USB线连接了Android手机,则点击
按钮,Android
Studio就会开始自动编译、打包、安装及运行。运行效果如下图:
OpenCV NDK应用
创建项目:
新建项目的过程与前文类似,这里就不赘述了。应用名称为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内容修改为:
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();
}
}
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(); } } |
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)
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) |
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文件的内容为:
APP_STL := gnustl_shared
APP_CPPFLAGS := -frtti -fexceptions -std=c++11
APP_ABI := armeabi-v7a x86
APP_PLATFORM := android-15
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_ABI规定了NDK编译的目标平台,由于我只想测试这两个架构,所以只写了两个,不过当然是越齐全越好:)
APP_PLATFORM指定了NDK编译针对的平台,其取值应该尽量与app\build.gradle中的minSdkVersion相一致。参考这里:http://stackoverflow.com/a/21982908/3829845。
4.app\src\main\jni\gray-process.cpp文件内容如下:
#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;
}
}
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; } } |
<?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>
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> |
<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>
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> |
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')
}
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') } |
请注意文件末尾被注释掉的compile project(':openCVLibrary310'),这一点前面提到过,如果在Java中调用了OpenCV,这里就要取消掉注释,以编译项目中所导入的OpenCV Java部分。
8.根目录的build.gradle文件中也只有一行需要注意,就是classpath那一行,如下:
dependencies {
classpath 'com.android.tools.build:gradle:2.1.2'
}
1 2 3 | dependencies { classpath 'com.android.tools.build:gradle:2.1.2' } |
编译及运行:
与前一节相同,点击
按钮,开始编译和运行。运行效果如下图所示:
Instant Run
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/
相关文章推荐
- 【Android】Android Studio 2上利用NDK进行OpenCV 3.1开发(注意点)
- 【Android】Android Studio 2上利用NDK进行OpenCV 3.1开发
- Android Studio 2.3利用CMAKE进行OpenCV 3.2的NDK开发
- Android Studio 2.2 中利用CAMKE进行OpenCV的NDK开发
- android studio 3.0 Ndk 开发- 利用增量更新进行 apk的覆盖安装
- 【Android Studio】在Android Studio上进行OpenCV 3.1开发
- 在Android Studio上进行OpenCV3.1开发环境的配置
- 在Android Studio上进行OpenCV 3.1开发
- 用Android studio进行 OpenCV 开发的第一个项目
- 用Android Studio进行NDK开发(简化流程)
- Android Studio使用JNI和NDK进行开发
- 使用Android Studio 1.3 版本进行NDK开发
- Android Studio利用Gradle进行NDK开发
- 使用Android Studio 1.3 版本进行NDK开发 编译运行hello-jni项目及分析
- 在Android Studio中进行NDK开发的一般流程
- Android Studio进行NDK开发笔记
- 使用Android Studio 进行NDK开发和Debug
- 在Android studio中进行NDK开发
- 基于mac,使用android studio进行ndk开发(1)
- 使用Android Studio 1.3 版本进行NDK开发准备工作