OpenCV移动端之CMake Android交叉编译

        目前基于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.假如以<ndk>/platforms/android-<api>/arch-<arch>的格式赋值给CMAKE_SYSROOT的变量,则<ndk>部分将会用CMAKE_ANDROID_NDK替换并且将使用NDK方式编译。     

4.假如以<standalone-toolchain>/sysroot的格式赋值给CMAKE_SYSROOT的变量,则<standalone-toolchain>部分将会用CMAKE_ANDROID_STANDALONE_TOOLCHAIN替换并且将使用Standalone Toolchain将被使用。 5.假如cmake中设置了ANDROID_NDK的变量,这将被当成CMAKE_ANDROID_NDK使用,工程将使用NDK编译。

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<iostream>
#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 <jni.h>
/* 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 <android/log.h>
#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

猜你喜欢

转载自blog.csdn.net/xxboy61/article/details/80953288