采用C++开发Android APP
作为一名算法开发人员,一定会有大量项目是用C/C++开发完成的。随着移动设备功能日益强大,总会有这么一天,你需要把曾经的C++代码移植到Android系统中运行。作为一名从未写过JAVA的业余程序员,如何解决这个问题?
作为一名算法开发人员,一定会有大量项目是用C/C++开发完成的。随着移动设备功能日益强大,总会有这么一天,你需要把曾经的C++代码移植到Android系统中运行。这正是我最近面临的状况。作为一名从未写过JAVA的业余程序员,如何解决这个问题?
问题分析
遇到困难先不要慌,镇静下来仔细分析一下情况。首先,需要移植的工程从功能上来分析大致可以分为三大模块:
- 调用摄像头进行人脸检测;
- 根据检测到的信息进行进一步计算;
- 将结果输出,控制后端的硬件。
第一部分调用了第三方视觉库OpenCV。第二和第三部分是自己编写的C++代码。考虑到Android系统本身提供了人脸检测的模块可供调用,第一部分的问题算不上棘手。在之前的项目中,第二部分内容的原理已经非常清晰,大不了用JAVA再实现一遍。第三部分因为平台与控制硬件发生了变化,有同事可以提供支持,在此可以不予考虑。所以我需要完成的事项就变为:
- 在Android系统下实现摄像头调用与人脸检测;
- 尝试将第二个模块移植到Android系统。
制定移植计划
这两项事情对于我来说有一定的难度,主要原因是我从未写过JAVA代码,没有做过Android下的APK开发,更别提将C++代码移植到Android运行。但是任务已经安排了下来,只能想办法克服这些困难,尽力而为。虽然没有写过JAVA代码,但是C++的基础应该可以借鉴;没做过Android下的APK开发,但是iOS下的APP开发经验应该会提供一部分帮助;C++代码移植万一不可行,就重新用JAVA写一遍,毕竟原理已经了然于胸,可以根据需求做相应的功能裁剪,尽可能减少工作量,以保证按期交付。所以我需要做的事项变成了:
- 尽快入门Android下的APP开发;
- 尝试寻找C++移植方法,如果不行,则采用JAVA重新实现。
Android应用开发入门
由于整个移植过程只有一周的时间,不可能做到真正的入门,最快的方法就是从Google提供的官方sample code入手。于是打开浏览器,访问Google开发者中国站:http://developer.android.google.cn. 从官方介绍来看,Google提供了一款类似Xcode的工具:Android Studio。于是一边下载安装Android Studio,一边浏览官方提供的示例代码,尤其是人脸检测相关部分。经过一番检索,发现Android下的人脸识别有两种实现途径:
幸运的是,Google针对Mobile Vision API
还提供了7份示例代码,其中包括一份名为FaceTracker
人脸检测代码。发现这段代码让我又惊又喜,这起码节省了2天的时间。二话不说下载用Android Studio导入,编译运行,一切都很完美。
C++代码支持情况
我使用的Android Studio版本为2.2.3,在建立工程的时候有一个可选项Include C++ Support
,这表明Android Studio是支持C++的。
浏览官方站点也发现Android提供NDK来将C++代码编译为JNI库[1:2],然后可在Java环境下调用。于是尝试建立一个支持C++的工程,将自己的C++代码添加到默认生成的native-lib.cpp
文件中,编译运行,OK。可是,如何让现有基于JAVA的工程来支持C++?
为了弄清楚这一点,我选择从分析两种工程之间的差异入手。分别新建了一个JAVA工程,一个C++工程。将两个项目目录结构展开,并排放在一起比较:
左侧为JAVA工程,右侧为C++工程。仔细比较发现,支持C++的工程主要发生了如下变化:
build.gradle
和MainActivity
的文件内容发生了变化。- 在
src
目录下多了一个cpp
目录,内有一个native-lib.cpp
文件; - 在
src
目录下多了一个CMakeLists.txt
文件;
接下来详细看看变化的细节。
build.gradle
build.gradle
文件中,android{...}
代码块中增加了如下内容:
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
在defaultConfig{...}
代码块中增加了如下内容:
externalNativeBuild {
cmake {
cppFlags ""
}
}
从内容可以看出,这是在告诉gradle采用CMake的方式对C++代码进行编译,并且给出了CMake需要的文件CMakeLists.txt
。
CMakeLists.txt
CMakeLists.txt
文件中内容很长,且有非常详细的注释,这里摘取其中重要的内容如下:
# ...
cmake_minimum_required(VERSION 3.4.1)
# ...
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).
# Associated headers in the same location as their source
# file are automatically included.
src/main/cpp/native-lib.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 )
# ...
target_link_libraries( # Specifies the target library.
native-lib
# Links the target library to the log library
# included in the NDK.
${log-lib} )
从注释可以看出,CMakeLists.txt
做了以下事项:
- 通过
add_library()
的方式添加C++源代码文件native-lib.cpp
,采用SHARED
方式编译,生成native-lib
库文件; - 通过
find_library()
的方式包含要链接的外部库文件log
; - 通过
target_link_libraries()
的方式链接native-lib
库文件。
而native-lib.cpp
正是C++源代码。
native-lib.cpp
作为C++源代码所在,native-lib.cpp
内容如下:
#include <jni.h>
#include <string>
extern "C"
jstring
Java_com_yinguobing_robin_cpp_1application_MainActivity_stringFromJNI(
JNIEnv *env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
在其中终于看到熟悉的std::string hello = "Hello from C++";
显然,这里声明了一个名为Java_com_yinguobing_robin_cpp_1application_MainActivity_stringFromJNI
的函数,该函数返回一个类型为jstring
的字符串,字符串内容为Hello from C++
。那这个函数又是在哪里调用的呢?
MainActivity
在iOS APP开发过程中,程序的入口一般为UIViewController
,同样,放在src/java
路径下的这个MainActivity
一定是JAVA程序的入口了。仔细比较该文件,发现其中增加了如下内容:
// Used to load the 'native-lib' library on application startup.
static {
System.loadLibrary("native-lib");
}
/**
* A native method that is implemented by the 'native-lib' native library,
* which is packaged with this application.
*/
public native String stringFromJNI();
正如注释所述,System.loadLibrary("native-lib")
负责加载native-lib
库文件;而stringFromJNI()
正是在native-lib.cpp
中实现的C++函数。
C++代码移植
既然原理已经清楚,接下来的事情就很简单了。整个代码移植的过程大概分以下几步:
- 在
build.gradle
中添加支持C++外部编译的代码; - 建立
CMakeList.txt
文件,并添加要编译的源代码文件; - 在程序的入口文件中添加要调用的C++函数原型;
- 在C++源代码中更新被调用函数的名称。
以上几步完成,你用C++实现的函数即可在Java环境下进行调用了。
最后附上Android官方文档:向您的项目添加 C 和 C++ 代码, 地址是 https://developer.android.google.cn/studio/projects/add-native-code.html
Comments ()