• 推荐 0 推荐
  • 收藏 4 收藏,5.7k 浏览

---------------- If you do NOT know Chinese, you can just skip this part ----------------

一直打算将原来的XFace进行改进,最近终于有了些时间可以动手了,改进计划如下:开发上使用Android Studio作为新的开发环境,配上新的构建系统Gradle;应用上将修改原来的UI设计,内部代码也将有很大的变化,可能会用上ContentProvider和Service等略高级内容;算法上打算让应用扩展性增强以适应不同的算法,并结合强大的Android Studio和Gradle让这个项目变得更加丰富。说了一堆废话,言归正传,本文的重点是介绍如何在Android Studio中进行NDK开发(目前它还不完全支持NDK开发),难点是NDK中还包含OpenCV的动态库。最后的最后,本文剩下部分将使用英文,因为它要成为我在StackOverflow上的处女答,么么哒 ~O(∩_∩)O~

---------------------------- Here is the right stuff you may need --------------------------------

This post shows how to develop an Android NDK application with OpenCV included using Android Studio and Gradle. If you're working on migrating your original Eclipse Project to Android Studio, you may find this post is what exactly you want!

OK,Let's start!

Section 1: Three things you must know

1.Firstly, if you are not familiar with Android Studio and Gradle, you may find these links useful. (if you already know these well, skip this part)

Creating a new Project with Android Studio

Building Your Project with Gradle

Gradle Plugin User Guide or you may want to read a Chinese commented version in my blog here.

2.Secondly, if your android ndk project is not that complicated(for example, having no opencv included), you may wanna see ph0b 's introduction here, it's quite a nice job with a video recorded! (you can also follow Section 2 in this post to get a simple Android NDK demo application)

ph0b's post: ANDROID STUDIO, GRADLE AND NDK INTEGRATION

3.Thirdly, if those above two do not meet your needs, then I think you may want to customize the Android.mk with Gradle in Android Studio. Thanks to Gaku Ueda , he had made a great job explaining how to achieve that goal. Actually I have found another nicer solution without adding that many codes and also achieve that goal. :-) Find it out in the next sections.

Gaku Ueda's post: Using custom Android.mk with Gradle/Android Studio

OK, I will cover all above and give another nice solution in the end, have fun!

Section 2: A simple Android NDK demo application

This section shows creating a simple Android NDK demo application, if you already know, you can directly go the section 3.

1.Create a new Android project named NDKDemo with a blank Activity in AS(=Android Studio).

2.Give an id to the TextView in activity_my.xml such as android:id="@+id/textview", then add these codes in MyActivity.java.

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my); TextView textView = (TextView) findViewById(R.id.textview);
textView.setText(hello());
} static {
System.loadLibrary("hello");
} public native String hello();

3.Create a new directory jni in folder app/src/main, then you have javajni and res in this folder.

4.This step is very important! You can add a external tool to run the javah command without typing that much code!

Open AS's Preferences, then find External Tools in IDE Settings, click + to add one tool with the following configurations. (Make sure you have add JDK tools in your system path, if you don't know how, click here)

With the help of this tool, each time we right click on a class file, then choose Android Tools -> javah to run this tool, it will automatically generate a C head file for us in the target folder $ModuleFileDir$/src/main/jni , in this case, it is app/src/main/jni. Try this on MyActivity.java file now! The console will print out a log like:

/usr/bin/javah -v -jni -d /Users/hujiawei/AndroidStudioProjects/NDKDemo/app/src/main/jni com.android.hacks.ndkdemo.MyActivity [Creating file RegularFileObject[/Users/hujiawei/AndroidStudioProjects/NDKDemo/app/src/main/jni/ com_android_hacks_ndkdemo_MyActivity.h]]

Then you get a com_android_hacks_ndkdemo_MyActivity.h file in jni folder with the following content.

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_android_hacks_ndkdemo_MyActivity */ #ifndef _Included_com_android_hacks_ndkdemo_MyActivity
#define _Included_com_android_hacks_ndkdemo_MyActivity
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_android_hacks_ndkdemo_MyActivity
* Method: hello
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_android_hacks_ndkdemo_MyActivity_hello
(JNIEnv *, jobject); #ifdef __cplusplus
}
#endif
#endif

5.Write a simple C implementation file named main.c in jni folder

#include <jni.h>
#include "com_android_hacks_ndkdemo_MyActivity.h" JNIEXPORT jstring JNICALL Java_com_android_hacks_ndkdemo_MyActivity_hello
(JNIEnv * env, jobject obj){
return (*env)->NewStringUTF(env, "Hello from JNI");
}

6.In the build.gradle file under app module, add the following codes to configure ndk in defaultConfig element, here we just give the uni module a name hello, you can find other configurations in Gradle Plugin User Guide.

defaultConfig {
applicationId "com.android.hacks.ndkdemo"
minSdkVersion 16
targetSdkVersion 20
versionCode 1
versionName "1.0" ndk{
moduleName "hello"
}
}

7.In order to let Gradle run ndk-build command (in some task, maybe NdkCompile task), we should configure the ndk.dir in local.properties file in Project root.

sdk.dir=/Volumes/hujiawei/Users/hujiawei/Android/android_sdk
ndk.dir=/Volumes/hujiawei/Users/hujiawei/Android/android_ndk

8.OK, everything is ready, click Run to give it a try, you will see the result like

All right, so what's happening inside?

Since you have a jni folder, Gradle will consider it as a default native code folder. When Gradle builds the app, it will run ndk-build command(since you have configured ndk.dir, Gradle knows where to find it) with a generated Android.mk file(locates in app/build/intermediates/ndk/debug/Android.mk), after compiling the native codes, it will generate the libs and obj folder into folder  app/build/intermediates/ndk/debug/. Gradle will then package the libs into final apk file in folder app/build/outputs/apk/app-debug.apk(you can unarchive this file to check whether libs is contained)

app/build/intermediates/ndk/debug (lib and obj folders)

app/build/outputs/apk/app-debug.apk (and files within it)

Secontion 3: Using OpenCV

If your project do not use OpenCV, then the section 2 is just enough. But what if you wanna use OpenCV to do other stuff? Of course, we want to use OpenCV for Android instead of  JavaCV here, and Of course, we need to package OpenCV library for Android into our application's APK file (then users who use this app does not have to install OpenCV Manager). So, how can we achieve these goals?

The simplest way has been posted by TGMCians on Stack Overflow here, that is, let the main app include the OpenCV library as a dependency, and copy all <abi>/*.so files in OpenCV for Android SDK to jniLibs folder under app/src/main/, Gradle will automatically package these <abi>/*.so files into libs folder within the final APK file. Of course, this method will work, but it has a few backwards: (1) Unless you only copy the needed *.so files, you will always have a large APK due to this reason; (2) How about the building of the jni files? How to run ndk-build if these files contain opencv related codes?

So, here comes to our Using custom Android.mk with Gradle and Android Studio part. For testing, we first creat an Android.mk and an Application.mk file under jni folder.

Android.mk

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_SRC_FILES := main.c
LOCAL_LDLIBS += -llog
LOCAL_MODULE := hello include $(BUILD_SHARED_LIBRARY)

Application.mk

APP_ABI := armeabi
APP_PLATFORM := android-16

Thanks to Gaku Ueda, he had made a great job explaining how to achieve that goal with this post. The core idea of his method is to run ndk-build command in some task, then zip the <abi>/*.so files under the output app/build/libs/folder into a jar file which is finally put in app/build/libs/ folder, then add a compile dependency to this jar file. The key code for his method listed below

Notice 1: When using custom Android.mk, we should first disable Gradle to build the jni folder as before, and sourceSets.main.jni.srcDirs = [] just does this job!

Notice 2: The code is not exactly the same with Gaku Ueda's code: tasks.withType(Compile) to tasks.withType(JavaCompile), because Compile is deprecated.

Notice 3: You can get  $ndkDir variable with project.plugins.findPlugin('com.android.application').getNdkFolder() or you can define it in grade.properties file under Project root, so you need to add ndkDir=path/to/your/ndk in that file, if the file is not created, simply create a new one.

android{
...
sourceSets.main.jni.srcDirs = [] task ndkBuild(type: Exec, description: 'Compile JNI source via NDK') {
ndkDir = project.plugins.findPlugin('com.android.application').getNdkFolder()
commandLine "$ndkDir/ndk-build",
'NDK_PROJECT_PATH=build',
'APP_BUILD_SCRIPT=src/main/jni/Android.mk',
'NDK_APPLICATION_MK=src/main/jni/Application.mk'
} task ndkLibsToJar(type: Zip, dependsOn: 'ndkBuild', description: 'Create a JAR of the native libs') {
destinationDir new File(buildDir, 'libs')
baseName 'ndk-libs'
extension 'jar'
from(new File(buildDir, 'libs')) { include '**/*.so' }
into 'lib/'
} tasks.withType(JavaCompile) {
compileTask -> compileTask.dependsOn ndkLibsToJar
}
...
} dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
// add begin
compile fileTree(dir: new File(buildDir, 'libs'), include: '*.jar')
// add end
}

But we can still do a little improvements here. We have already know that Gradle will take jniLibs folder as its default native libraries folder, so we can simply output the libs/<abi>/*.so files generated by ndk-build command into jniLibs folder, so there's no need to zip these *.so files into a jar file.

The final build.gradle file under app module

apply plugin: 'com.android.application'

android {
compileSdkVersion 20
buildToolsVersion "20.0.0" defaultConfig {
applicationId "com.android.hacks.ndkdemo"
minSdkVersion 16
targetSdkVersion 20
versionCode 1
versionName "1.0"
} // add begin
sourceSets.main.jni.srcDirs = [] task ndkBuild(type: Exec, description: 'Compile JNI source via NDK') {
ndkDir = project.plugins.findPlugin('com.android.application').getNdkFolder()
commandLine "$ndkDir/ndk-build",
'NDK_PROJECT_PATH=build/intermediates/ndk',
'NDK_LIBS_OUT=src/main/jniLibs',
'APP_BUILD_SCRIPT=src/main/jni/Android.mk',
'NDK_APPLICATION_MK=src/main/jni/Application.mk'
} tasks.withType(JavaCompile) {
compileTask -> compileTask.dependsOn ndkBuild
}
// add end buildTypes {
release {
runProguard false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
} dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
}

So simple, right? 'NDK_LIBS_OUT=src/main/jniLibs' helps us do the right job!

For testing, you can also add some lines relating with OpenCV in your Android.mk file and some line in your main.c to check whether everything is readlly working. For example, add #include <opencv2/core/core.hpp> in main.c file, and change  Android.mk to

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

#opencv
OPENCVROOT:= /Volumes/hujiawei/Users/hujiawei/Android/opencv_sdk
OPENCV_CAMERA_MODULES:=on
OPENCV_INSTALL_MODULES:=on
OPENCV_LIB_TYPE:=SHARED
include ${OPENCVROOT}/sdk/native/jni/OpenCV.mk LOCAL_SRC_FILES := main.c
LOCAL_LDLIBS += -llog
LOCAL_MODULE := hello include $(BUILD_SHARED_LIBRARY)

In Gradle Console window, you can see these similar lines

*.so files relating with OpenCV has been packaged into the final APK

One More Thing

Of course, maybe you don't want to change your build.grale file with that much code, and Of course, you also don't want to run ndk-build outside the IDE, then copy the <abi>/*.so files into jniLibs folder each time you want to rebuild the native codes!

At last, I came out another nicer solution, if you like, that is to create a ndk-build external tool in Android Studio, and every time you want to rebuild the native codes, simply run the external tool, then it automatically generates the libs/<abi>/*.so files into jniLibs folder, so everything is ready to run this app, :-)

The configuration is simple

Parameters:  NDK_PROJECT_PATH=$ModuleFileDir$/build/intermediates/ndk NDK_LIBS_OUT=$ModuleFileDir$/src/main/jniLibs NDK_APPLICATION_MK=$ModuleFileDir$/src/main/jni/Application.mk APP_BUILD_SCRIPT=$ModuleFileDir$/src/main/jni/Android.mk V=1

OK, I hope it is helpful. Let me know if it is really helpful, or tell me what's your problem. :-)

Android NDK and OpenCV Development With Android Studio的更多相关文章

  1. Android NDK 和 OpenCV 整合开发总结(3)

    Android NDK 和 OpenCV 整合开发总结(3) http://hujiaweibujidao.github.io/blog/2013/11/18/android-ndk-and-open ...

  2. 基于 Android NDK 的学习之旅-----Android.mk 介绍

    一个Android.mk file用来向编译系统描述你的源代码.具体来说:该文件是GNU Makefile的一小部分,会被编译系统解析一次或多次.你可以在每一个Android.mk file中定义一个 ...

  3. android ndk编译x264开源(用于android的ffmpeg中进行软编码)

    http://blog.csdn.net/u012917616/article/details/40921833 不废话,直接上.sh脚本: export NDK=/home/xxx/my_softw ...

  4. !! 2.对十份论文和报告中的关于OpenCV和Android NDK开发的总结

    http://hujiaweibujidao.github.io/blog/2013/11/18/android-ndk-and-opencv-development-3/ Android Ndk a ...

  5. Android SDK Android NDK Android Studio 官方下载地址

    2016.12 Android Studio Windows Includes Android SDK https://dl.google.com/dl/android/studio/install/ ...

  6. Android SDK Android NDK Android Studio 官方下载地址<转>

    转自:http://www.cnblogs.com/yaotong/archive/2011/01/25/1943615.html 2016.10Android Studio 2.2.1.0https ...

  7. [android ndk] -android studio中编译生成so文件

    1.android.useDeprecatedNdk=true Error:Execution failed for task ':app:compileDebugNdk'.> Error: N ...

  8. android学习五---OpenCV for android环境搭建

    学习android的目的是想在手机上实现计算机视觉的算法.一般算法的研究都是在Matlab上进行,但是手机平台没有那么多的计算资源,用matlab显然是不太现实的.而OpenCV是基于C++语言编写的 ...

  9. Android NDK R9d 安装

    NDK是一个工具集,可让您实现您的应用程序使用本机代码的语言,如C和C + +.Android NDK 是在SDK前面又加上了“原生”二字,即Native Development Kit,因此又被Go ...

随机推荐

  1. [ An Ac a Day ^_^ ] CodeForces 601A The Two Routes 最短路

    14号就ccpc全国赛的全国赛了 而且也快东北赛的选拔赛了 现在队伍实力实在不行 参加了也是边缘化的队伍 虽然有新生保护的设置 但实话说 机会还是不大 所以不如趁现在开始好好努力 明年也许还有机会 A ...

  2. Html加载swf 兼容IE8 (含以下)显示

    嵌入参数说明: 1,AllowScriptAccess  参数: sameDomain:仅当 SWF 文件和网页位于同一域中时才允许执行外出脚本访问.这是 AVM2 内容的默认值----播放网络视频, ...

  3. MVC DisplayTemplates and EdiotrTemplates.

    我们在mvc项目里经常要对枚举,日期,副文本输入,我们可以用笨拙的方法去view页面里绑定呈现的html内容,而且这种办法不能重用,也就是在不同的view里还是需要做相同的事情,给个日期空间选择例子吧 ...

  4. POJ1410 Intersection 计算几何

    题目大意:给出一个线段的两端,和矩形两端(不一定是左上和右下),问线段是否与矩形相交(若线段在矩形内也算相交).这题蒸鹅心-- 题目思路:判断所有情况:线段是否在矩形内,线段内一点是否在矩形内,线段是 ...

  5. 在Scholarspace可视化交互式分析中遇到的几个问题及处理方法

    1 JavaScript中的变量作用范围 JS中的变量作用域是以函数为单位的,因为在两个for循环中,不能用同一个变量命名作为循环的控制条件,不然会陷入死循环退出不来.曾经为了处理这个问题花看一天的时 ...

  6. HDU 5810 Balls and Boxes

    n*(m-1)/(m*m) #pragma comment(linker, "/STACK:1024000000,1024000000") #include<cstdio&g ...

  7. FZU 1502 Letter Deletion

    最长公共子序列. #pragma comment(linker, "/STACK:1024000000,1024000000") #include<cstdio> #i ...

  8. Xcode8.2 继续使用插件

    网上参考了文章:http://www.jianshu.com/p/ab819babf2c3 使用的是:update_xcode_plugins . 但要注意的是,在Xcode 8.2下安装,并没有给我 ...

  9. webapp 微信开发适配问题

    文章摘自:http://www.cnblogs.com/oksite/p/4630462.html 前段时间由于公司要做微信app 前端主要有我一个人独立开发 分享一下自己独立开发微信app的一些经验 ...

  10. 更改web project 访问项目名称

    1.新建web project 2.右键该项目名称------properties 3.访问该项目的URL http://localhost:8806/ssm/.......... 相比书写整个项目名 ...