JNI/NDK:CMakeLists.txt 构建so库,导入三方so库,以及遇到的坑

本文使用最新的JNI构建工具CMake完成

通过这篇文章,你讲学习到:

  • camke构建自己的三方so库
  • 学会使用cmake管理自己jni文件
  • 学会使用cmake调用三方的so库
  • 最后分析自己开发过程中遇到的坑

1.CMakeLists.txt 构建so库

  • 创建jni的工具类JNI :
    • 这个类的原本用途是,在工程里用来管理jni的方法,和加载so库用的
    • 但是在这里仅仅只是为了加载so库
package com.bendeng.jnindk;

/**
 * @author: dwj
* @date: 2019/4/10 15:39
* @desc:
*/ public class JNI { // Used to load the 'test-lib' library on application startup. static { // 一定要加这一句,否不会生成so库 System.loadLibrary("test-lib"); } }
  • 创建自己的cpp文件(test.cpp)和头文件(test.h)

JNI/NDK:CMakeLists.txt 构建so库,导入三方so库,以及遇到的坑_第1张图片

  • test.cpp
#include "../header/test.h"

int return_value(void)
{
    return 5;
}
void print_value()
{
    cout<<"successful call this method"<
  • test.h
#include 
using namespace std;

int return_value(void);
void print_value();
int add(int x, int y);
int mul (int x, int y);
  • 配置CMakelist.txt文件,构建goodutil库,两步如下:
cmake_minimum_required(VERSION 3.4.1)

#so库的输出路径
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY
	${PROJECT_SOURCE_DIR}/libs/${ANDROID_ABI}) 

#1. 添加自己的so库test-lib
add_library( # Sets the name of the library.
             test-lib

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
             src/main/cpp/include/test.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 )
#2.添加链接
target_link_libraries( # Specifies the target library.
                       test-lib
                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib} )
  • 设置需要生成的CPU平台,市面上的机型基本都设置了

JNI/NDK:CMakeLists.txt 构建so库,导入三方so库,以及遇到的坑_第2张图片

  • Make Project,就会在libs目录下生成so库

JNI/NDK:CMakeLists.txt 构建so库,导入三方so库,以及遇到的坑_第3张图片      JNI/NDK:CMakeLists.txt 构建so库,导入三方so库,以及遇到的坑_第4张图片

2.管理Android工程的jni文件

  • 上面的JNI.java 在正式的工程中使用如下:包含so库的加载,和本地方法的声明;
  • 其中native-lib是由native-lib.cpp构建的so库,native-lib.cpp是按照jni接口的规范写的。
  • native-lib.cpp内可以引用三方的so库,三方的库不用安装jni的规范些,因为三方的库是用 native-lib.cpp封装后提供给java调用的
  • JNI.java可以直接调用,调用是根据(包名_类名_方法)名寻找的
package com.bendeng.jnindk;

/**
 * @author: dwj
* @date: 2019/4/10 15:39
* @desc:
*/ public class JNI { // 此参数被C++赋值 public int number; // Used to load the 'native-lib' library on application startup. static { System.loadLibrary("test-lib"); } /** * 通过在C++里面改变field参数值number */ public native void changeFieldValue(); /** * 通过在C++里面调用java方法 */ public native double callJavaMethod(); // 此方法被C++调用 public double max(double d1, double d2) { return d1 > d2 ? d1 : d2; } /** * 调用C++封装的so方法 */ public native int returnValue(); }
  • native-lib.cpp,按jni接口规范写的,供java层调用,这里也可以调用.cpp封装的so库文件limit
#include 
#include 
#include 
#include "header/test.h"

extern "C" JNIEXPORT void

JNICALL
Java_com_bendeng_jnindk_JNI_changeFieldValue(JNIEnv *env, jobject jobj) {

    // 获取jobj中class对象
    jclass clazz = env->GetObjectClass(jobj);
    // 获取字段number的ID,最后一个参数是签名
    jfieldID id_number = env->GetFieldID(clazz, "number", "I");
    // 获取字段的值
    jint number = env->GetIntField(jobj, id_number);
    // 打印默认的初始值
    std::cout << "number=" + number << std::endl;
    // 给number赋新的值
    env->SetIntField(jobj, id_number, 100);

}

extern "C" JNIEXPORT jdouble

JNICALL
Java_com_bendeng_jnindk_JNI_callJavaMethod(JNIEnv *env, jobject jobj) {

    // 获取jobj中class对象
    jclass clazz = env->GetObjectClass(jobj);
    // 获取方法max的ID,最后一个参数是签名
    jmethodID id_max = env->GetMethodID(clazz, "max", "(DD)D");
    // 获取字段的值
    jdouble max_value = env->CallDoubleMethod(jobj, id_max, 1.2, 3.4);
    return max_value;

}
extern "C" JNIEXPORT jint

JNICALL
Java_com_bendeng_jnindk_JNI_returnValue(JNIEnv *env, jobject jobj) {
    // 此方法是调用test.cpp封装的so里面的方法
    return return_value();
}
  • 配置CMakelist.txt文件,构建goodutil库,两步如下:

cmake_minimum_required(VERSION 3.4.1)

#so库的输出路径
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY
	${PROJECT_SOURCE_DIR}/libs/${ANDROID_ABI}) 

#1. 添加自己的so库native-lib
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).
             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 )
#2.添加链接
target_link_libraries( # Specifies the target library.
                       native-lib
                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib} )

3.导入第三方的so库

  • 把第三方的库文件放到libs目录下面,包含上面生成的7中CPU文件
  • 然后配置 CMakeLists.txt ,一共4步,如下:
cmake_minimum_required(VERSION 3.4.1)
#1.配置第三方so库.h头文件路径
include_directories(src/main/cpp/header)

#指定so库输出路径
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY
	${PROJECT_SOURCE_DIR}/libs/${ANDROID_ABI})

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).
             src/main/cpp/native-lib.cpp )

#2.添加第三方库
add_library(test-lib SHARED IMPORTED)
#3.添加库的路径
set_target_properties(test-lib
        PROPERTIES IMPORTED_LOCATION
        ${PROJECT_SOURCE_DIR}/libs/${ANDROID_ABI}/libtest-lib.so)

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 )

#4.添加链接
target_link_libraries( # Specifies the target library.
                       native-lib
                       test-lib
                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib} )
  • Make Project,会在libs下生成新的libnative-lib.so库文件,图就不贴了,文章上面有贴出过
  • 最后运行的时候,记得把build.gradle文件中的生成cmake相关的代码注释掉,同时需要添加一段代码,否则会报错:couldn't found "libtest-lib.so"。如下图所示:

JNI/NDK:CMakeLists.txt 构建so库,导入三方so库,以及遇到的坑_第5张图片

4.做jni调用过程中遇到的坑

  •  公司一个项目,需要做jni编程,来调用算法模型里面的方法,算法那边给到的是so库文件,里面都是C++方法,不是按照jni接口规范写的,因此java没法直接调用。所以需要在算法so库的基础上,再封装一个so库,采用jni接口规范编写;
  • 可能算法工程师也不懂so库的封装,给到我这边的so库和.h文件,按照上面的方法,一直编译不通过。后来拿到cpp文件后,自己通过上面的方法,先封装算法的so库,然后通过jni接口规范来调用,发现可以编译通过,原来搞了半天,是算法so库封装有问题;
  • 编译运行都ok,但是一启动APP就闪退,还是报错找不到“libtest-lib.so”库,网上说需要加上下面这段代码:
sourceSets {
            main {
                jni.srcDirs = []
                jniLibs.srcDirs = ['libs']
            }
        }
  • 但是加上后,又报出新的错误,大概意思是so库重复,这样一想,肯定是又会生成新的so库文件,导致重复的,最后把build.gradle中cmake相关代码注释掉,跑起来就正常工作了:
apply plugin: 'com.android.application'

android {
    compileSdkVersion 28
    defaultConfig {
        applicationId "com.bendeng.jnindk"
        minSdkVersion 18
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
//        externalNativeBuild {
//            cmake {
//                cppFlags ""
//            }
//        }

//        ndk{
//            abiFilters 'x86', 'x86_64', 'armeabi', 'armeabi-v7a', 'arm64-v8a', 'mips', 'mips64'
//        }

        sourceSets {
            main {
                jni.srcDirs = []
                jniLibs.srcDirs = ['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:28.+'
    implementation 'com.android.support.constraint:constraint-layout:1.0.2'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.1'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
}

导入第三方so库(非jni接口规范封装的so库),并在此基础上做jni开发,总算高一段落了,但是技术宅男不能满足于此,下一阶段将为大家输出jna的编程原理,使用起来将更加简单方便。

 

参考资料:https://blog.csdn.net/hukou6335490/article/details/83687419

https://blog.csdn.net/hjj378315764/article/details/79834352

https://blog.csdn.net/yuanzhihua126/article/details/78992068

https://blog.csdn.net/wzhseu/article/details/79683045

你可能感兴趣的:(Android笔记(难点))