目前基于OpenCV 图像处理的开发都是Windows或LInux下直接C++算法实现,如何将已有的C++实现用在Android上,这就需要用到交叉编译了。本节主要对CMake V3.10 官网教程 Cross Compiling for Android 的简单翻译以及如何将交叉编译后部署在Android上的讲解。
Cross Compiling for Android
通过在编译工具链配置文件中设置CMAKE_SYSTEM_NAME变量为Android可以配置Android交叉编译,更多配置取决与Android开发环境的使用。
对于用CMake生成Makefile或Ninja,CMake要求配置NDK或Standalone Toolchain。
CMake通过如下步骤来配置NDK或NinJa:
1.假如CMake中设置了CMAKE_ANDROID_NDK的变量,CMake将会使用指定路径的NDK。
2.假如CMake中设置了CMAKE_ANDROID_STANDALONE_TOOLCHAIN的变量,CMake将会使用指定路径的Standalone Toolchain。
3.假如以
4.假如以
6.假如cmkae中设置了ANDROID_STANDALONE_TOOLCHAIN的变量,这将被当成CMAKE_ANDROID_STANDALONE_TOOLCHAIN使用,将使用Standalone Toolchain编译。
7.假如设置了环境变量ANDROID_NDK_ROOT或ANDROID_NDK,这将被当成CMAKE_ANDROID_NDK,将使用NDK编译。
8.假如设置了环境变量ANDROID_STANDALONE_TOOLCHAIN,这将被当成CMAKE_ANDROID_STANDALONE_TOOLCHAIN,将使用Standalone Toolchain编译。
9.通过错误可以判断工程是否设置了NDK或Standalone Toolchain。
Cross Compiling for Android with the NDK
编译文件可以配置Android交叉编译生成的Makefile或Ninja文件。
通过如下变量配置来说明如何使用Android NDK:
1.CMAKE_SYSTEM_NAME
设置成Android。必须的,用于开启Android交叉编译。
2.CMAKE_SYSTEM_VERSION
用于设置Android API级别,假如未特别指定,该值将通过如下方式决定:
a.假如设置了CMAKE_ANDROID_API,该值将被作为Android API的级别。
b.假如设置了CMKAE_SYSROOT变量,Android API级别由NDK目录下的sysroot决定。
c.否则将在NDK中使用最新的API级别。
CMAKE_ANDROID_ARCH_ABI
用于设置Android ABI(构架),假如未具体指定,该变量将被设置为默认的armabi。CMAKE_ANDROID_ARCH将会由CMAKE_ANDROID_ARCH_ABI自动推断出来。
CMAKE_ANDROID_NDK
以Android NDK根目录绝对路径的方式赋值${CMKAE_ANDROID_NDK}/platforms目录必须存在。假如未指定将赋默认值。
CMKAE_ANDROID_NDK_DEPRECATED_HEADERS
设置为true将不会使用标准头文件而使用弃用的per-api-level头文件。未指定将默认为false,除非使用的NDK不存在标准头文件。
CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION
设置编译器的NDK工具链版本,未指定则使用最新的GCC工具链。
CMKAE_ANDDROID_STL_TYPE
设置使用的C++标准库。
对于官网的基本介绍就这些了,下面讲解一以下自己的简单使用,主要是用c++编写处理程序,然后用交叉编译编译出Android平台的动态库.so,接着使用JNI调用。
1.新建mymath.h、mymath.cpp
#ifndef _MYMATH_H
#define _MYMATH_H
int myadd(int a, int b);
int mysub(int a, int b);
int mymul(int a, int b);
#endif
mymath.cpp
#include
#include"mymath.h"
int myadd(int a, int b)
{
return a + b;
}
int mysub(int a, int b)
{
return a - b;
}
int mymul(int a, int b)
{
return a * b;
}
2.编写编译文件CMakeLists.txt,编译生成mymath.so:
# this is required
set(CMAKE_SYSTEM_NAME Android)
# API level
set(CMAKE_SYSTEM_VERSION 21)
# specify the cross compiler
set(CMAKE_ANDROID_ARCH_ABI armeabi-v7a)
set(CMAKE_ANDROID_ARM_NEON ON)
set(CMAKE_ANDROID_NDK android-ndk-r16b)
set(CMAKE_ANDROID_STL_TYPE gnustl_static)
cmake_minimum_required(VERSION 3.10)
#project must after android set
project(mymath)
add_library(mymath SHARED mymath.h mymath.cpp)
3.新建一个AS工程,选中app/src/main右键New->Folder->JNI Folder,会在app/src/main下生成1个jni文件夹,选中app/src/main/jni 右键New->Directory,文件名为prebuilt,在选中app/src/main/jni/prebuilt右键New->Directory,文件名为include,将步骤(1)的mymath.h拷贝到app/src/main/jni/prebuilt/include,将mymath.so拷贝到app/src/main/jni/prebuilt下,选中app/src/main/jni/prebuilt右键New->File,文件名为Android.mk,内容如下:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := mymath
LOCAL_SRC_FILES := libmymath.so
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
include $(PREBUILT_SHARED_LIBRARY)
4.选中app/src/main/java/xinyi61.ndkbuild 新建一个mymath的类,
package xinyi61.ndkbuild;
public class mymath {
// Used to load the 'native-lib' library on application startup.
static {
System.loadLibrary("mymath");
}
public native int jnimathAdd(int a, int b);
public native int jnimathSub(int a, int b);
public native int jnimathMul(int a, int b);
}
根据OpenCV移动端之android JNI第6步生成对应的头文件xinyi61_ndkbuild_mymath.h,并新建xinyi61_ndkbuild_mymath.cpp完成其实现:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include
/* Header for class xinyi61_ndkbuild_mymath */
#ifndef _Included_xinyi61_ndkbuild_mymath
#define _Included_xinyi61_ndkbuild_mymath
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: xinyi61_ndkbuild_mymath
* Method: jnimathAdd
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_xinyi61_ndkbuild_mymath_jnimathAdd
(JNIEnv *, jobject, jint, jint);
/*
* Class: xinyi61_ndkbuild_mymath
* Method: jnimathSub
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_xinyi61_ndkbuild_mymath_jnimathSub
(JNIEnv *, jobject, jint, jint);
/*
* Class: xinyi61_ndkbuild_mymath
* Method: jnimathMul
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_xinyi61_ndkbuild_mymath_jnimathMul
(JNIEnv *, jobject, jint, jint);
#ifdef __cplusplus
}
#endif
#endif
//
// Created by xinyi61 on 18-7-6.
//
#include
#include "xinyi61_ndkbuild_mymath.h"
#include "prebuilt/include/mymath.h"
#define LOG_TAG "MYJNI"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT jint JNICALL Java_xinyi61_ndkbuild_mymath_jnimathAdd
(JNIEnv *env, jobject jobj, jint a, jint b)
{
LOGI("11111111111111111111111111111111");
return myadd(a, b);
}
JNIEXPORT jint JNICALL Java_xinyi61_ndkbuild_mymath_jnimathSub
(JNIEnv *env, jobject jobj, jint a, jint b)
{
LOGI("22222222222222222222222222222222");
return mysub(a, b);
}
JNIEXPORT jint JNICALL Java_xinyi61_ndkbuild_mymath_jnimathMul
(JNIEnv *env, jobject jobj, jint a, jint b)
{
LOGI("33333333333333333333333333333333");
return mymul(a, b);
}
#ifdef __cplusplus
}
#endif
并在app/src/main/jni下新建Android.mk和Application.mk
#Android.mk
LOCAL_PATH:=$(call my-dir)
#include $(CLEAR_VARS)
#LOCAL_MODULE := mymath
#LOCAL_SRC_FILES := $(LOCAL_PATH)/prebuilt/libmymath.so
#LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/prebuilt/include
#include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := jniMymath
LOCAL_SRC_FILES:=xinyi61_ndkbuild_mymath.cpp
LOCAL_LDLIBS += -llog
LOCAL_SHARED_LIBRARIES := mymath
include $(BUILD_SHARED_LIBRARY)
include $(LOCAL_PATH)/prebuilt/Android.mk
#Application.mk
APP_ABI := armeabi-v7a
APP_STL := stlport_static
APP_CPPFLAGS:=-frtti -fexceptions
5.打开AS Terminal进入app/src/main/jni,运行ndk-build生成最终的.so.
6.修改app/src/main/java/xinyi61.ndkbuid下的MainActivity
package xinyi61.ndkbuild;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import android.util.Log;
public class MainActivity extends AppCompatActivity {
// Used to load the 'native-lib' library on application startup.
static {
System.loadLibrary("native-lib");
System.loadLibrary("jniMymath");
}
@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);
Log.e("mmm", "11111111111111111");
mymath a = new mymath();
Log.e("mmm", "22222222222222222");
tv.setText(stringFromJNI() + a.jnimathSub(100, 10));
Log.e("mmm", "33333333333333333");
}
/**
* A native method that is implemented by the 'native-lib' native library,
* which is packaged with this application.
*/
public native String stringFromJNI();
}
修改app/src下的build.gradle,红色部分增加部分:
apply plugin: 'com.android.application'
android {
compileSdkVersion 27
defaultConfig {
applicationId "xinyi61.ndkbuild"
minSdkVersion 21
targetSdkVersion 27
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
cppFlags ""
}
}
ndk {
moduleName "jniMymath"
ldLibs "log"
abiFilters "armeabi-v7a"
}
}
sourceSets{
main{
jni.srcDirs = []
jniLibs.srcDirs 'src/main/libs'
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:27.1.1'
implementation 'com.android.support.constraint:constraint-layout:1.1.2'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
最终效果如下:
参考:
android.mk