1、什么是NDK
- 定义:Native Development Kit,NDK是Google开发的一套开发和编译工具集。
- 作用:快速开发C、 C++的动态库,并自动将so和应用一起打包成 APK,
即可通过 NDK在 Android中 使用 JNI与本地代码(如C、C++)交互。
2、什么是JNI
JNI 全称 Java Native Interface,Java 本地化接口,可以通过 JNI 调用系统提供的 API。
JNI是一套编程接口,用来实现Java代码与本地的C/C++代码进行交互。
3、什么是cmake
CMake是一个跨平台的编译(Build)工具,可以用简单的语句来描述所有平台的编译过程。
CMake 允许开发者编写一种平台无关的 CMakeList.txt 文件来定制整个编译流程,然后再根据目标用户的平台进一步生成所需的本地化 Makefile 和工程文件。
4、NDK项目创建
Android studio2.3后就开始支持cmake,个人推荐使用cmake进行NDK开发。
- 首先新建项目选择选择模板时选择"Native C++ ",就会自动创建对应的C++文件,CMakeLists.txt配置文件,build.gradle会加入一些配置信息。
-
如果你的的项目已经创建过了,你可以把cpp文件夹、gradle配置信息、CMakeLists.txt文件拷贝到对应目录。
image01.png
build.gradle配置信息
android {
compileSdkVersion 29
defaultConfig {
applicationId "com.hy.myapp"
minSdkVersion 19
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
ndk {
abiFilters 'armeabi'
}
externalNativeBuild {
cmake {
cppFlags "-std=c++11"
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
externalNativeBuild {
cmake {
path "CMakeLists.txt"
version "3.10.2"
}
}
}
5、cmake语法
# 指定cmake的最小版本
cmake_minimum_required(VERSION 3.4.1)
# 搜索当前目录下的所有.cpp文件
aux_source_directory(./src/main/cpp DIR_CPP)
message(${CMAKE_ANDROID_ARCH_ABI})
message(${CMAKE_SOURCE_DIR})
# 指定头文件目录
include_directories(src/main/cpp/ffmpeg3/include)
include_directories(src/main/cpp/include)
add_library( # 生成库的名字
native-lib
# 生成动态库或共享库
SHARED
# 将以下的C++文件编译到动态库
src/main/cpp/native-lib.cpp
${DIR_CPP}
)
# 生成静态库
# 静态库在程序编译时会被连接到目标代码中,程序运行时将不再需要该静态库。
# add_library(native-static-lib STATIC native-static-lib.cpp)
set(lib_src_dir ${CMAKE_SOURCE_DIR}/src/main/jniLibs/armeabi)
# 添加其他预构建的库
add_library(avcodec-lib SHARED IMPORTED)
set_target_properties(avcodec-lib PROPERTIES IMPORTED_LOCATION ${lib_src_dir}/libavcodec.so)
add_library(avdevice-lib SHARED IMPORTED)
set_target_properties(avdevice-lib PROPERTIES IMPORTED_LOCATION ${lib_src_dir}/libavdevice.so)
add_library(avfilter-lib SHARED IMPORTED)
set_target_properties(avfilter-lib PROPERTIES IMPORTED_LOCATION ${lib_src_dir}/libavfilter.so)
add_library(avformat-lib SHARED IMPORTED)
set_target_properties(avformat-lib PROPERTIES IMPORTED_LOCATION ${lib_src_dir}/libavformat.so)
add_library(avutil-lib SHARED IMPORTED)
set_target_properties(avutil-lib PROPERTIES IMPORTED_LOCATION ${lib_src_dir}/libavutil.so)
add_library(swresample-lib SHARED IMPORTED)
set_target_properties(swresample-lib PROPERTIES IMPORTED_LOCATION ${lib_src_dir}/libswresample.so)
add_library(swscale-lib SHARED IMPORTED)
set_target_properties(swscale-lib PROPERTIES IMPORTED_LOCATION ${lib_src_dir}/libswscale.so)
# 查找到指定的预编译库,并将它的路径存储在变量中
find_library(
log-lib
log)
# 设置 target 需要链接的库
target_link_libraries( # 目标库
native-lib
# 目标库需要链接的库
avcodec-lib
avdevice-lib
avfilter-lib
avformat-lib
avutil-lib
swresample-lib
swscale-lib
# log-lib 是上面 find_library 指定的变量名
${log-lib})
gradle build 详细错误信息显示,可以使用以下命令
gradlew compileDebug --stacktrace
gradlew compileDebug --stacktrace -info
gradlew compileDebug --stacktrace -debug
(推荐) gradlew compileDebugSources --stacktrace -info
6、JNI语法
- java 调用C++方法
public class TestJni {
private static final String TAG =TestJni.class.getSimpleName();
static {
System.loadLibrary("native-lib");
}
public static native String stringFromJNI();
}
extern "C" {
JNIEXPORT jstring JNICALL
Java_com_hy_learn_TestJni_stringFromJNI(JNIEnv *env, jclass clazz) {
std::string hello = "hello world from C++";
return env->NewStringUTF(hello.c_str());
}
}
- C++ 调用Java方法
/** 对照表
* |JAVA |JNI
* |boolean |Z
* |byte |B
* |char |C
* |short |S
* |int |I
* |long |L
* |float |F
* |double |D
*
* |String |Ljava/lang/String;
* |Class |Ljava/lang/Class;
* |Throwable |Ljava/lang/Throwable;
* |int[] |[I
* |byte[] |[B
* |Object[] |[Ljava/lang/Object;
*
*/
- jni访问java类字段
/**
* 实体类
*/
public class UserBean {
public String id;
public String name;
public String pwd;
public String tel;
public int age;
public static int score=100;
public UserBean(String name, int age) {
this.name = name;
this.age = age;
}
public static void plusFun(int x,int y){
int sum=x+y;
Log.d(" static function --",String.valueOf(sum));
}
public void getUser(String name,int age,String tel){
Log.d("userBean ----",name+ " : "+age+ " : "+tel);
}
public static int getUserInfo(byte[]data,int length){
for(byte b:data){
Log.d("data ----",String.valueOf(b));
}
return length;
}
}
//jni访问java类字段
public static native void accessField(UserBean userBean);
//------------ JNI修改类的属性字段 -----------------
extern "C"
JNIEXPORT void JNICALL
Java_com_hy_learn_TestJni_accessField(JNIEnv *env, jclass clazz, jobject user) {
jclass cls = env->GetObjectClass(user);
jfieldID jfId = env->GetFieldID(cls, "name", "Ljava/lang/String;");
jfieldID jfId_age = env->GetFieldID(cls, "age", "I");
jstring str=env->NewStringUTF("my name is C++");
env->SetObjectField(user,jfId,str);
env->SetIntField(user,jfId_age,16);
}
- jni 访问 java类静态字段
//jni访问java类静态字段
public static native void accessStaticField(UserBean userBean);
//------------ JNI修改类静态属性字段 -----------------
extern "C"
JNIEXPORT void JNICALL
Java_com_hy_learn_TestJni_accessStaticField(JNIEnv *env, jclass thiz, jobject user) {
jclass cls = env->GetObjectClass(user);
jfieldID jfId = env->GetStaticFieldID(cls, "score", "I");
int score=env->GetStaticIntField(cls,jfId);
env->SetStaticIntField(cls,jfId,score-1);
}
- jni 调用 java 类方法
//jni 调用 java 类方法
public static native void callMethod(UserBean userBean);
//---------------- JNI调用类方法 -----------------
extern "C"
JNIEXPORT void JNICALL
Java_com_hy_learn_TestJni_callMethod(JNIEnv *env, jclass clazz, jobject user) {
jclass cls = env->GetObjectClass(user);
jmethodID mid=env->GetMethodID(cls,"getUser","(Ljava/lang/String;ILjava/lang/String;)V");
jstring name = env->NewStringUTF("leo");
jstring tel = env->NewStringUTF("10086");
env->CallVoidMethod(user,mid,name,20,tel);
}
- jni 调用 java 类静态方法
// jni调用java类静态方法
public static native void callStaticMethod(UserBean userBean);
//---------------- JNI调用类静态方法 -----------------
extern "C"
JNIEXPORT void JNICALL
Java_com_hy_learn_TestJni_callStaticMethod(JNIEnv *env, jclass clazz, jobject user) {
jclass cls = env->GetObjectClass(user);
jmethodID mid=env->GetStaticMethodID(cls,"getUserInfo","([BI)I");
int size=6;
char *c_str ="011011";
jbyteArray array=env->NewByteArray(size);
for(int i=0;i<size;i++){
env->SetByteArrayRegion(array,0,strlen(c_str), (jbyte *) c_str);
}
env->CallStaticIntMethod(cls,mid,array,size);
}
- jni通过接口参数回调
// 接口作为参数
public interface ICallback {
void onSuccess(String msg);
void onFail(String error);
}
// jni通过接口参数回调
public static native void callbackMethod(ICallback callback);
//---------------- JNI主线程调用 -----------------
extern "C"
JNIEXPORT void JNICALL
Java_com_hy_learn_TestJni_callbackMethod(JNIEnv *env, jclass clazz, jobject callback) {
jclass cls = env->GetObjectClass(callback);
jmethodID mid_success=env->GetMethodID(cls,"onSuccess","(Ljava/lang/String;)V");
jmethodID mid_fail=env->GetMethodID(cls,"onFail","(Ljava/lang/String;)V");
jstring msg=env->NewStringUTF("C++ success");
jstring error=env->NewStringUTF("C++ fail");
env->CallVoidMethod(callback,mid_success,msg);
env->CallVoidMethod(callback,mid_fail,error);
}
// -------- 调用 -------------
TestJni.callbackMethod(object : ICallback {
override fun onSuccess(msg: String?) {
Logger.d("onSuccess -----", msg)
}
override fun onFail(error: String?) {
Logger.d("onFail --------", error)
}
})
- jni 子线程通过接口参数回调java类方法
//jni子线程通过接口参数回调java类方法
public static native void callbackChildThread(ICallback callback);
static jobject threadObject;
static jmethodID threadMethod;
//声明一个线程
pthread_t childThread;
JavaVM *jvm;
//-- 在加载动态库时会去调用 JNI_Onload 方法,可以得到 JavaVM 实例对象 --
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env;
jvm = vm;
if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
return -1;
}
return JNI_VERSION_1_6;
}
//定义一个线程的回调
void *threadCallback(void *) {
JNIEnv *env= nullptr;
if(jvm->AttachCurrentThread(&env, nullptr)==0){
jstring msg=env->NewStringUTF("C++ childThread success");
env->CallVoidMethod(threadObject,threadMethod,msg);
jvm->DetachCurrentThread();
}
//执行线程完毕之后,退出线程
pthread_exit(&childThread);
}
//---------------- JNI子主线程调用 -----------------
extern "C"
JNIEXPORT void JNICALL
Java_com_hy_learn_TestJni_callbackChildThread(JNIEnv *env, jclass clazz, jobject callback) {
threadObject=env->NewGlobalRef(callback);
jclass cls = env->GetObjectClass(callback);
threadMethod=env->GetMethodID(cls,"onSuccess","(Ljava/lang/String;)V");
pthread_create(&childThread, nullptr, threadCallback, nullptr);
}
- jni 访问 java 类构造器返回对象
//jni访问java类构造器返回对象
public static native UserBean callConstructor();
//---------------- JNI访问Java构造器 -----------------
extern "C"
JNIEXPORT jobject JNICALL
Java_com_hy_learn_TestJni_callConstructor(JNIEnv *env, jclass clazz) {
jclass cls = env->FindClass("com/hy/learn/UserBean");
jmethodID mId = env->GetMethodID(cls, "<init>", "(Ljava/lang/String;I)V");
jstring name = env->NewStringUTF("jim from jni");
jint tel = 18;
jobject user = env->NewObject(cls, mId, name, tel);
/** 第二种方法
* jobject users=env->AllocObject(cls);
* env->CallNonvirtualVoidMethod(users,cls,mId,name,tel);
*/
return user;
}
7、总结
使用cmake进行NDK开发还是很方便的,也要有代码提示。源码地址:https://github.com/xulj-tech/LearnAndroid/tree/master/learn-jni-lib
网友评论