cmake以及jni环境配置

1. JNI文件编译后生成头文件

a)首先,需要定义好头文件内容,例如

public class ImageUtil {
    public static native String hello(String s);
    public static native int[] disjoint(float[] pm, float[] lm, int w, int h, int neighbour);
}

b)然后通过javah命令,生成头文件(*.h)命令:

javah -classpath D:\\project\\app\\src\\main\\java  -jni com.example.util.ImageUtil

而对于java对象生成jni头文件时可能会报错,比如Bitmap有可能找不到签名的错误,需要加上依赖包
javah -jni -classpath  C:\\Users\\Administrator-Local1\\AppData\\Local\\Android\\Sdk\\platforms\\android-28\\android.jar;. com.example.util.ImageUtil

注意:需要进入main/java所在的目录,这样javah才能找到包名对应的路径,否则有可能遇到下面的错误

Exception in thread "main" java.lang.IllegalArgumentException: Not a valid class
 name: java/com/example/util/ImageUtil

这种错误解决办法可参考:https://blog.csdn.net/github_37847975/article/details/79939895

生成完后就会有如下的头文件定义,格式为java开头,后面跟上包名,然后再加上类名,最后加上定义的方法名:

JNIEXPORT jstring JNICALL Java_com_example_util_ImageUtil_hello
  (JNIEnv *, jclass, jstring);

接下来需要在C++实现Java_com_example_util_ImageUtil_hello方法,关于如何从C++中返回java对象,如C++返回HashMap<A ,B>是需要注意的一个问题,A,B可以是任意对象,需要熟悉JNI的反射机制,返回值的签名可参考http://gityuan.com/2016/05/28/android-jni/

例如,HashMap<A ,B>的签名为

"(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"

例如,C++返回int[][],可通过返回jObjectArray对象时,其签名为

env->FindClass("[I")

通过

env->SetObjectArrayElement中设置每个元素对象是一个jintArray对象。

c)然后,当然java调用C++,实现方法内容

d)编译整个工程,目前cmake是主流,NDK慢慢被抛弃,下面会相信说明cmake的详细过程,编译工程的过程中也会按照编写好的cmake文件编译C++文件,编译完后在project->app->build->intermediates->cmake->debug->obj->arm64-v8a目录下生成so文件

2. cmake 编译

a)NEW->Folder->JNI Folder建立JNI文件夹,可以在该文件夹下放C++文件

 

b)CMakeLists.txt文件内容编写,主要是环境的相关配置内容,主要有以下内容

set(ANDROID_ABI armeabi-v7a)
# 设置opencv的路径
set(pathToOpenCv D:/OpenCV-android-sdk/sdk)
# 设置opencv头文件路径
include_directories(${pathToOpenCv}/sdk/native/jni/include)
#动态方式加载库文件
add_library(lib_opencv STATIC IMPORTED )
#引入libopencv_java4.so文件
#set_target_properties(lib_opencv PROPERTIES IMPORTED_LOCATION #${PROJECT_SOURCE_DIR}/src/main/jni/${ANDROID_ABI}/libopencv_java4.so )


# 将当前 "./src/main/cpp" 目录下的所有源文件保存到 "NATIVE_SRC" 中,然后在 add_library 方法调用。
aux_source_directory(src/main/cpp NATIVE_SRC )

# 配置opencv,将opencv的库文件拷贝到jniLibs目录下,并配置头文件目录
#set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../jniLibs)
#set(OpenCV_DIR D:/OpenCV-android-sdk/sdk/native/jni)
#find_package(OpenCV REQUIRED)
include_directories(${CMAKE_SOURCE_DIR} D:/OpenCV-android-sdk/sdk/native/jni/include)

# ========================= import opencv lib start =============================
add_library(libopencv_highgui STATIC IMPORTED )
set_target_properties(libopencv_highgui PROPERTIES
        IMPORTED_LOCATION "${lib}/${ANDROID_ABI}/libopencv_highgui.a")

add_library(libopencv_imgcodecs STATIC IMPORTED )
set_target_properties(libopencv_imgcodecs PROPERTIES
        IMPORTED_LOCATION "${lib}/${ANDROID_ABI}/libopencv_imgcodecs.a")

add_library(libopencv_imgproc STATIC IMPORTED )
set_target_properties(libopencv_imgproc PROPERTIES
        IMPORTED_LOCATION "${lib}/${ANDROID_ABI}/libopencv_imgproc.a")

add_library(libopencv_core STATIC IMPORTED )
set_target_properties(libopencv_core PROPERTIES
        IMPORTED_LOCATION "${lib}/${ANDROID_ABI}/libopencv_core.a")
# ======================================================================
file(GLOB native_srcs "src/main/cpp/*.cpp")
# 指定生成的库名
add_library(hello_lib SHARED
            hello.cpp)
find_library( # Defines the name of the path variable that stores the
              # location of the NDK library.
              log-lib
              # Specifies the name of the NDK library that
              # CMake needs to locate.
              log )
target_link_libraries(hello_lib2 ${log-lib}
        # ${OpenCV_LIBS}
        ${jnigraphics-lib} ${z-lib}
        libopencv_imgproc libopencv_core
        libopencv_highgui libopencv_imgcodecs)

gradle配置:

externalNativeBuild {
    cmake {
        arguments "--graphviz=./target_deps_graphviz"
        cppFlags "-std=c++11 -s  -lz -fvisibility=hidden -Os -fvisibility-inlines-hidden"
        abiFilters 'armeabi-v7a', 'arm64-v8a'
    }
}
sourceSets {
    main {
        jniLibs.srcDirs = ['libs']  # 打包so到apk配置
    }
}

c)加载so库,需要在程序的最开始初始化so库,可以放在static代码中,加载方式如下

static {
    try {
        OpenCVLoader.initDebug();
        System.loadLibrary("hello_lib");
    } catch (UnsatisfiedLinkError e) {
        Log.e(TAG,"Native library not found, so library is unavailable.");
    }
}

d) 对生成的so库进行压缩:

编译时使用的是ANDROID_NDK中的android.toolchain.cmake配置进行编译的,进行so压缩可以在该文件中修改相关配置参数进行编译,也可以在gradle配置文件中控制编译的相关参数,经过压缩后so从10.3M缩小到了4.09M,缩小了2.5倍,所以,在移动端有限的资源下,对so压缩有多重要就可想而知了,下面就是压缩的方法的一些总结。

1. 找到android_ndk->build->cmake->android.toolchain.cmake文件去掉-g -funwind-tables编译选项,同时去掉-Wl,--build-id链接      器选项,可以压缩1倍以上

2.  在gradle中设置cmake的cppFlags "-std=c++11 -s  -lz"中加上-s,有可能导致so库不可用

3. 不要启用 Exceptions 和 RTTI

4. 不要使用 iostream(没有测试过,但是代码没有输出iostream)

5. 使用 -fvisibility=hidden(亲测,有效)

6. 使用 gc-sections 丢弃未使用的函数(nkd默认带有该参数)

7. 使用 –icf=safe 移除重复代码(测试发现,不起作用)

8. 修改交叉编译工具链的默认标记位

9. 限制编译的ABI

JNI可参考:https://www.jianshu.com/p/08dcc910b088

so压缩参考文档: Android NDK: How to Reduce Binaries Size

so文件的压缩可参考:https://zhuanlan.zhihu.com/p/72475595,尤其是-g选项

注意问题:1. ndk的版本非常非常重要,我是用ndkr16, 使用更高的版本不知道为什么没有编译过去

                  2. cmake的门槛有点高,对cmake不熟悉的不要随便修改编译的参数,除非你确定这个参数是正确的,有可能就是这                        些参数设置的不对才编译不过去

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值