本文使用最新的JNI构建工具CMake完成
通过这篇文章,你讲学习到:
camke构建自己的三方so库
学会使用cmake管理自己jni文件
学会使用cmake调用三方的so库
最后分析自己开发过程中遇到的坑
1.CMakeLists.txt 构建so库
创建jni的工具类JNI :
这个类的原本用途是,在工程里用来管理jni的方法,和加载so库用的
但是在这里仅仅只是为了加载so库
package com.bendeng.jnindk;
/**
* @author: dwj<br>
* @date: 2019/4/10 15:39<br>
* @desc: <br>
*/
public class JNI {
// Used to load the 'test-lib' library on application startup.
static {
// 一定要加这一句,否不会生成so库
System.loadLibrary("test-lib");
}
}
创建自己的cpp文件(test.cpp)和头文件(test.h)
test.cpp
#include "../header/test.h"
int return_value(void)
{
return 5;
}
void print_value()
{
cout<<"successful call this method"<<endl;
}
int add(int x, int y)
{
return x+y;
}
int mul (int x, int y)
{
return x*y;
}
test.h
#include <iostream>
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平台,市面上的机型基本都设置了
Make Project,就会在libs目录下生成so库
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<br>
* @date: 2019/4/10 15:39<br>
* @desc: <br>
*/
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 <jni.h>
#include <string>
#include <iostream>
#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"。如下图所示:
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
————————————————
版权声明:本文为CSDN博主「嗨摔得漂亮」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/dengweijunkedafu/article/details/89218863