1. 环境
android studio
2. JNI开发基本流程
2.1 创建 HelloWorld.java,并声明 native 方法 add();
package com.example.myapplication.utis;
public class HelloWorld {
static {
System.loadLibrary("mynativeso");
}
public native int add(int a,int b);
}
2.2 使用 javac 命令编译源文件,生成 HelloWorld.h 头文件
- 进入HelloWorld.java所在的目录
- javac -encoding utf-8 -h . HelloWorld.java
- 将生成的HelloWorld.h头文件放到app/src/main/jni目录中
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_myapplication_utis_HelloWorld */
#ifndef _Included_com_example_myapplication_utis_HelloWorld
#define _Included_com_example_myapplication_utis_HelloWorld
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_example_myapplication_utis_HelloWorld
* Method: add
* Signature: (II)V
*/
JNIEXPORT jint JNICALL Java_com_example_myapplication_utis_HelloWorld_add
(JNIEnv *, jobject, jint, jint);
#ifdef __cplusplus
}
#endif
#endif
2.3 在源文件 HelloWorld.cpp 中实现函数原型
2.3.1 自定义头文件的方法实现
#include <jni.h>
#include <string>
extern "C"
jint Java_com_example_myapplication_utis_HelloWorld_add
(JNIEnv *env, jobject jj, jint a, jint b){
return a + b;
};
2.3.2 调用非标准so中的方法
- 将非标准的libtest.so放到app/src/main/jniLibs对应的文件夹中
- 将libtest.so对应的头文件test.h放到app/src/main/jni中
- 在jni中创建文件HelloWorld.cpp文件
#ifndef _TEST_JNI_ADD_H_
#define _TEST_JNI_ADD_H_
int add(int x, int y);
#endif
#include <jni.h>
#include <string>
#include <test.h>//导入需要的.h文件,这个是必须的,如果依赖的第三方库没有.h,需要自己编写
extern "C"
jint Java_com_example_myapplication_utis_HelloWorld_add
(JNIEnv *env, jobject jj, jint a, jint b){
int result = add(a,b);//引入test.so中的方法
return result;
};
2.4 编译本地代码,生成 Hello-World.so 动态原生库文件
2.4.1 ndk构建
2.4.2 cmake构建
2.4.2.1 创建文件app/CMakeLists.txt文件
#指定cmake最小版本(影响下面api的使用)
cmake_minimum_required(VERSION 3.4.1)
#设置生成的so动态库最后输出的路径
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI})
#1.指定库的名字、类型(STATIC,SHARED)、以及关联源码的相对路径
#2.可以多次调用
add_library(
# 设置生成库的名字(这里最终会生成libhelloworld.so)
helloworld
# 生成动态库
SHARED
# 指定源码文件,HelloWorld.cpp文件
src/main/jni/HelloWorld.cpp )
#指定头文件所在的目录
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/src/main/jni/include/)
#添加已经构建好的库,并配置相关参数,指定其所在路径
#${PROJECT_SOURCE_DIR} 项目根目录
#${CMAKE_CURRENT_SOURCE_DIR} 当前文件所在地址
add_library(test SHARED IMPORTED)
set_target_properties(test
PROPERTIES IMPORTED_LOCATION
${CMAKE_CURRENT_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libtest.so)
#将ndk中的源码构建至静态库
add_library( app-glue
STATIC
${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c )
#从ndk库从找到内置的库(liblog)并将其所在路径存储为一个变量(log-lib)
find_library(
# log库的别名
log-lib
#log库
log )
#关联cmake编译时需要使用的库
target_link_libraries(
#即将要生成的libhelloworld.so
helloworld
#对应libtest.so
test
#对应上面定义的静态库
app-glue
#对应上面定义的ndk内置liblog库
${log-lib} )
2.4.2.2 配置app下的build.gradle
android {
...
defaultConfig {
...
//可选配置项
externalNativeBuild {
// For ndk-build, instead use the ndkBuild block.
cmake {
// Passes optional arguments to CMake.
arguments "-DANDROID_ARM_NEON=TRUE", "-DANDROID_TOOLCHAIN=clang"
// Sets a flag to enable format macro constants for the C compiler.
cFlags "-D__STDC_FORMAT_MACROS"
// Sets optional flags for the C++ compiler.
cppFlags "-fexceptions", "-frtti"
}
}
...
//添加
ndk {
// Specifies the ABI configurations of your native
// libraries Gradle should build and package with your app.
abiFilters 'x86', 'x86_64', 'armeabi', 'armeabi-v7a',
'arm64-v8a'
}
}
buildTypes {...}
//模块配置
externalNativeBuild {
//添加
cmake {
// CMakeLists.txt相对路径
path "CMakeLists.txt"
}
}
...
//添加
packagingOptions {//加上这些代码(可选)
pickFirst 'lib/armeabi-v7a/libhelloworld.so'
}
}
packagingOptions选项说明:
1)pickFirst:匹配到多个相同文件,只提取第一个。只作用于APK,不能过滤aar和jar中的文件
2)exclude:过滤掉某些文件或者目录不添加到APK中,作用于APK,不能过滤aar和jar中的内容。
3)doNotStrip:可以设置某些动态库不被优化压缩。
4)merge:将匹配的文件都添加到APK中,和pickFirst有些相反,会合并所有文件。
2.4.2.3 点击make project就可以在jniLibs下生成指定的libhelloworld.so文件
2.4.2.4 在其他项目中使用时需要创建同包名下的HelloWorld.java文件;同时将依赖的so文件放到项目的jniLibs下的响应目录中
3. 用so生成aar供三方使用
3.1 新建C++项目
默认会生成下面的文件
android {
...
defaultConfig {
...
}
buildTypes {}
...
externalNativeBuild {
cmake {
path file('src/main/cpp/CMakeLists.txt')
version '3.18.1'
}
}
...
}
cmake_minimum_required(VERSION 3.18.1)
project("nativeapplication")
add_library(
nativeapplication
SHARED
native-lib.cpp)
find_library(
log-lib
log)
target_link_libraries(
nativeapplication
${log-lib})
#include <jni.h>
#include <string>
extern "C" JNIEXPORT jstring JNICALL
Java_com_kemai_nativeapplication_MainActivity_stringFromJNI(
JNIEnv* env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("nativeapplication");
}
private ActivityMainBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
TextView tv = binding.sampleText;
tv.setText(stringFromJNI());
}
public native String stringFromJNI();
}
make project生成so文件,如下图
3.2 新建android-lib
创建帮助类DemoHelper.java
package com.kemai.videostacker;
public class DemoHelper {
static {
System.loadLibrary("nativeapplication");
}
public native int add(int a,int b);
}
将上面生成的so拷贝到jniLibs相应的文件夹中
修改上一步中的native-lib.cpp文件,增加add方法的实现
#include <jni.h>
#include <string>
extern "C" JNIEXPORT jstring JNICALL
Java_com_kemai_nativeapplication_MainActivity_stringFromJNI(
JNIEnv* env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
//新增内容
extern "C"
JNIEXPORT jint JNICALL
Java_com_kemai_videostacker_DemoHelper_add(JNIEnv *env, jobject thiz, jint a, jint b) {
return a + b;
}
添加完毕后,重新make project项目,在app下重新生成so文件(否则新增的c方法无法被找到),然后重新拷贝到mylibrary中。接着assebleRelease命令执行mylibrary,生成aar文件
将mylibrary-release.aar放到其他项目中进行测试
若有收获,就点个赞吧