JNI,即Java Native Interface(java本地接口),为了方便Java调用C、C++等本地代码所封装的一层接口;JNI是属于Java的,与Android无直接关系;用native关键字来标识。
NDK,即Native Development Kit,是Android所提供的一个工具集合,通过NDK可以在Android中更方便地通过JNI来访问本地代码。
使用NDK的好处:
- 1)提高代码的安全性。由于so库反编译比较困难,因此NDK提高了Android程序的安全性。
- 2)可以很方便地使用目前已有的C/C++开源库
- 3)便于平台间移植。通过C/C++实现的动态库可以很方便地在其他平台上使用。
- 4)提高程序在特定情形下的执行效率,但是并不能明显;提升Android程序的性能。
注:
JNIEnv、不能跨线程,使用JavaVM来生成JNIEnv
jobject,不能跨线程,不能跨函数,需要提升为全局引用。
可以看下方线程部分。
JNI的数据类型和类型签名
基本数据类型
JNI类型 | Java类型 | 签名 | 描述 |
---|---|---|---|
jboolean | boolean | Z | 无符号8位整型 |
jbyte | byte | B | 有符号8位整型 |
jchar | char | C | 无符号16位整型 |
jshort | short | S | 无符号16位整型 |
jint | int | I | 32位整型 |
jlong | long | J | 64位整型 |
jfloat | float | F | 32位浮点型 |
jdouble | double | D | 64位浮点型 |
void | void | V | 无类型 |
引用类型
JNI类型 | Java类型 | 签名 | 描述 |
---|---|---|---|
jobject | Object | Object类型 | |
jclass | Class | [L | Class类型 |
jstring | String | [Ljava/lang/String; | 字符串 |
jobjectArray | Object[] | [Ljava/lang/Object; | 对象数组 |
jbooleanArray | boolean[] | [Z | boolean数组 |
jbyteArray | byte[] | [B | byte数组 |
jcharArray | char[] | [C | char数组 |
jshortArray | short[] | [S | short数组 |
jintArray | int[] | [i | int数组 |
jlongArray | long[] | [J | long数组 |
jfloatArray | float[] | [F | float数组 |
jdoubleArray | double[] | [D | double数组 |
jthrowable | Throwable | Throwable |
JNI方法
env方法 | 描述 |
---|---|
jclass GetObjectClass(jobject obj) | 根据Java对象获取Java类的jclass指针 |
jclass FindClass(const char* name) | 通过传入java中完整的类名来查找java的class,如:env->FindClass(“com/example/jni_helloworld/Student”) |
jfieldID GetFieldID(jclass clazz, const char* name, const char* sig) | 获取成员变量的field ID |
jobject GetObjectField(jobject obj, jfieldID fieldID) | 返回指定field ID的对象,如:强转为JNI层的jstring |
const char* GetStringUTFChars(jstring string, jboolean* isCopy) | 获得一个UTF-8格式的字符串指针,如:强转为C/C++层的char * |
jfieldID GetStaticFieldID(jclass clazz, const char* name, const char* sig) | 获取静态变量的field ID |
jint GetStaticIntField(jclass clazz, jfieldID fieldID) | 获取静态变量int类型的jint值,等价于int |
void SetObjectField(jobject obj, jfieldID fieldID, jobject value) | 给fieldID赋值 |
void SetStaticIntField(jclass clazz, jfieldID fieldID, jint value) | 给静态int fieldID赋值 |
jmethodID GetMethodID(jclass clazz, const char* name, const char* sig) | 获取Java类中方法的ID |
jmethodID GetStaticMethodID(jclass clazz, const char* name, const char* sig) | 获取Java类中静态的方法的ID |
jint CallIntMethod(jobject obj, jmethodID methodID, …); | 访问int的方法 |
jstring NewStringUTF(const char* bytes) | 在JNI层生成一个jstring |
jobject GetObjectArrayElement(jobjectArray array, jsize index) | 返回object数组元素,如:强转为JNI层的jstring |
void ReleaseStringUTFChars(jstring string, const char* utf) | 释放操作 |
jsize GetArrayLength(jarray array) | 获取数组长度 |
void CallVoidMethod(jobject obj, jmethodID methodID, …) | 调用无类型的方法 |
void CallStaticVoidMethod(jclass clazz, jmethodID methodID, …) | 调用静态无类型的方法 |
jobject AllocObject(jclass clazz) | 返回对象的引用,但不调用对象的构造方法 |
jobject NewGlobalRef(jobject obj) | 创建全局引用 |
void DeleteGlobalRef(jobject globalRef) | 释放全局引用 |
void DeleteLocalRef(jobject localRef) | 释放局部引用 |
jobject CallObjectMethod(jobject obj, jmethodID methodID) | 调用类的方法,返回jobject,如:static_cast< jstring>(env->CallObjectMethod(student, getName)); |
jint* GetIntArrayElements(jintArray array, jboolean* isCopy) | 获取int数组中的所有元素 |
void ReleaseIntArrayElements(jintArray array, jint* elems, jint mode) | 释放int数组 |
jthrowable ExceptionOccurred() | 确定是否抛出异常。在本地代码调用ExceptionClear()或Java代码处理异常之前,异常会一直抛出 |
jint ThrowNew(jclass clazz, const char* message) | 使用message指定的消息从指定的类构造一个异常对象,并导致抛出该异常。 |
jboolean ExceptionCheck() | 检测是否发生了异常 |
void ExceptionDescribe() | 打印异常描述 |
void ExceptionClear() | 清除抛出的异常 |
加载so库
从库目录遍历层级目录,去主动查找;apk里面的 lib/libnative-lib.so
System.load(); 可以用决定路径的方式加载动态链接库文件。
static {
System.loadLibrary("native-lib");
}
日志输出打印
// 日志输出
#include <android/log.h>
#define TAG "AAAAAAAAAAAAA"
// __VA_ARGS__ 代表 ...的可变参数
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__);
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__);
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__);
helloworld示例
1) 创建一个新项目 (选择 Native C++)
2) 生成头文件
在
jdk 执行:javah com.example.jni_helloworld.MainActivity
即:javah 包名.类名
生成的头文件放到cpp下:
3) 配置ndk(我这里选择20.0.5)
配置完,进行刷新下:
4) 实现文件
基本数据类型
MainActivity
package com.example.jni_helloworld;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("native-lib");
}
public native void test2(String str, int number);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
test2("zhangSan", 23);
}
}
native-lib.cpp 实现文件
#include "com_example_jni_helloworld_MainActivity.h"
// 日志输出
#include <android/log.h>
#define TAG "AAAAAAAAAAAAA"
// __VA_ARGS__ 代表 ...的可变参数
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__);
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__);
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__);
extern "C"
JNIEXPORT void JNICALL
Java_com_example_jni_1helloworld_MainActivity_test2(JNIEnv *env, jobject thiz, jstring str, jint number) {
int n1 = number;
const char * s1 = env->GetStringUTFChars(str, NULL);
LOGD("int: %d, string: %s ", n1, s1);
}
引用类型 - 数组操作
MainActivity
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("native-lib");
}
// 传递引用类型 数组
public native void test1(String[] strs, int[] intArray);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String[] strs = new String[]{"zhangSan", "liSi"};
int[] intArray = new int[]{100, 200};
test1(strs, intArray);
}
}
native-lib.cpp 实现文件
#include "com_example_jni_helloworld_MainActivity.h"
// 日志输出
#include <android/log.h>
#define TAG "AAAAAAAAAAAAA"
// __VA_ARGS__ 代表 ...的可变参数
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__);
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__);
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__);
extern "C"
JNIEXPORT void JNICALL
Java_com_example_jni_1helloworld_MainActivity_test1(JNIEnv *env, jobject jobj, jobjectArray jobjArray, jintArray int_array) {
// int[]
// 获取int数组长度
jsize int_array_size = env->GetArrayLength(int_array);
// int[] 转成int *
int * intArray = env->GetIntArrayElements(int_array, NULL);
for (int i = 0; i < int_array_size; ++i) {
LOGD("int[]: %d ", *intArray + i);
}
// string[]
jsize size = env->GetArrayLength(jobjArray);
for (int i = 0; i < size; ++i) {
// 返回object数组元素
jstring str = static_cast<jstring>(env->GetObjectArrayElement(jobjArray, i));
// 获得一个UTF-8格式的字符串指针
const char * c_str = env->GetStringUTFChars(str, NULL);
LOGD("数组: %s ",c_str);
// 释放
env->ReleaseStringUTFChars(str, c_str);
}
}
对象操作
Student.java
package com.example.jni_helloworld;
import android.util.Log;
public class Student {
public String name;
public void Student(){
Log.d("AAAAAAAAAAAAAAAAAAA", "Student无参构造方法");
}
public void Student(String str){
Log.d("AAAAAAAAAAAAAAAAAAA", "Student 一个参数构造方法 "+str);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public static void show(String info){
Log.d("AAAAAAAAAAAAAAAAAAA", "student show-------- " + info);
}
@Override
public String toString() {
return "Student{" +
"name='" + name +
'}';
}
}
MainActivity.java
package com.example.jni_helloworld;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("native-lib");
}
public native void putObject(Student student);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Student student = new Student();
student.name = "zhangSan";
putObject(student);
}
}
native-lib.cpp
#include "com_example_jni_helloworld_MainActivity.h"
// 日志输出
#include <android/log.h>
#define TAG "AAAAAAAAAAAAA"
// __VA_ARGS__ 代表 ...的可变参数
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__);
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__);
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__);
extern "C"
JNIEXPORT void JNICALL
Java_com_example_jni_1helloworld_MainActivity_putObject(JNIEnv *env, jobject thiz, jobject student) {
// 寻找类 方式1 通过传入java中完整的类名来查找java的class
jclass studentClass = env->FindClass("com/example/jni_helloworld/Student");
// 寻找类 方式2 根据Java对象获取Java类的jclass指针
// jclass studentClass = env->GetObjectClass(student);
// 获取Java类中方法的ID
jmethodID setName = env->GetMethodID(studentClass, "setName", "(Ljava/lang/String;)V");
jmethodID getName = env->GetMethodID(studentClass, "getName", "()Ljava/lang/String;");
// 获取Java类中静态的方法的ID
jmethodID show = env->GetStaticMethodID(studentClass, "show", "(Ljava/lang/String;)V");
// 调用方法 setName
jstring newName = env->NewStringUTF("LiSi");
env->CallVoidMethod(student, setName, newName);
// 调用方法 getName
jstring getNameValue = static_cast<jstring>(env->CallObjectMethod(student, getName));
const char * c_name = env->GetStringUTFChars(getNameValue, NULL);
LOGD("getName %s ", c_name);
// 调用静态show方法
jstring info = env->NewStringUTF("hahahaha");
env->CallStaticVoidMethod(studentClass, show, info);
// 返回对象的引用,但不调用对象的构造方法
jobject studentObj = env->AllocObject(studentClass);
newName = env->NewStringUTF("LiSi2");
env->CallVoidMethod(studentObj, setName, newName);
// 获取值
getNameValue = static_cast<jstring>(env->CallObjectMethod(studentObj, getName));
const char * c_name2 = env->GetStringUTFChars(getNameValue, NULL);
LOGD("getName %s ", c_name2);
}
局部引用
局部引用,在JNI函数结束后,会释放局部引用,j_str不等于NULL,j_str会变成悬空指针,当再次调用test123()时,就会崩溃了
MainActivity.java
package com.example.jni_helloworld;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("native-lib");
}
public native void test123();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button test_bt = findViewById(R.id.test_bt);
test_bt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
test123();
}
});
}
}
native-lib.cpp
#include "com_example_jni_helloworld_MainActivity.h"
// 日志输出
#include <android/log.h>
#define TAG "AAAAAAAAAAAAA"
// __VA_ARGS__ 代表 ...的可变参数
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__);
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__);
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__);
// 局部 在JNI函数结束后,会释放局部引用,j_str不等于NULL,j_str会变成悬空指针,当再次调用test123()时,就会崩溃了
jstring j_str;
extern "C"
JNIEXPORT void JNICALL
Java_com_example_jni_1helloworld_MainActivity_test123(JNIEnv *env, jobject thiz) {
if (j_str == NULL) {
j_str = env->NewStringUTF("hello");
}
const char * c_str = env->GetStringUTFChars(j_str, NULL);
LOGD("局部引用 %s ", c_str);
}
全局引用
MainActivity.java
package com.example.jni_helloworld;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("native-lib");
}
public native void test123();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button test_bt = findViewById(R.id.test_bt);
test_bt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
test123();
}
});
}
}
native-lib.cpp
#include "com_example_jni_helloworld_MainActivity.h"
// 日志输出
#include <android/log.h>
#define TAG "AAAAAAAAAAAAA"
// __VA_ARGS__ 代表 ...的可变参数
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__);
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__);
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__);
// 局部 在JNI函数结束后,会释放局部引用,j_str不等于NULL,j_str会变成悬空指针,当再次调用test123()时,就会崩溃了
jstring j_str;
extern "C"
JNIEXPORT void JNICALL
Java_com_example_jni_1helloworld_MainActivity_test123(JNIEnv *env, jobject thiz) {
if (j_str == NULL) {
// 旧的 局部引用
// j_str = env->NewStringUTF("hello");
// 升级为全局引用
jstring temp_jstr = env->NewStringUTF("new_hello");
j_str = static_cast<jstring>(env->NewGlobalRef(temp_jstr));
// 释放 temp_jstr
env->DeleteLocalRef(temp_jstr);
}
const char * c_str = env->GetStringUTFChars(j_str, NULL);
LOGD("全局引用 %s ", c_str);
}
调用其它cpp中的变量和方法
MainActivity.java
package com.example.jni_helloworld;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("native-lib");
}
public native void test123();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button test_bt = findViewById(R.id.test_bt);
test_bt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
test123();
}
});
}
}
test.cpp
#include <iostream>
#include <android/log.h>
#define TAG "AAAAAAAAAAAAA"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__);
int age =100;
void show() {
LOGD("show.................");
}
修改CMakeLists.txt
加入test.cpp
native-lib.cpp
#include "com_example_jni_helloworld_MainActivity.h"
// 日志输出
#include <android/log.h>
#define TAG "AAAAAAAAAAAAA"
// __VA_ARGS__ 代表 ...的可变参数
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__);
// 声明
extern int age;
extern void show();
extern "C"
JNIEXPORT void JNICALL
Java_com_example_jni_1helloworld_MainActivity_test123(JNIEnv *env, jobject thiz) {
LOGD("age: %d ", age);
show();
}
动态注册
默认生成的是静态注册,静态注册开发简单,但JNI函数名非常长,捆绑上层的包名+类名,运行期才会去匹配JNI函数,性能上低于动态注册。(该博文上方的都是静态注册的方式!)
MainActivity
public native void test5();
public native int test6(String text);
native-lib.cpp
#include "com_example_jni_helloworld_MainActivity.h"
#include <android/log.h>
#define TAG "AAAAAAAAAAAAA"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__);
// native函数
void nativeTest5(JNIEnv * env, jobject jobj) { // 如果用不到JNIEnv、jobject, 可以写成void nativeTest5()
LOGD("this is test5().");
}
int nativeTest6(JNIEnv * env, jobject jobj, jstring jstr) {
const char * text = env->GetStringUTFChars(jstr, nullptr);
LOGD("this is test6(). text: %s",text);
env->ReleaseStringUTFChars(jstr, text);
return 200;
}
static const JNINativeMethod jniNativeMethod[] = {
// JNINativeMethod为结构体,依次为函数名、函数签名、函数指针
{"test5", "()V", (void *)(nativeTest5)},
{"test6", "(Ljava/lang/String;)I", (int *)(nativeTest6)}
};
// 动态注册
JavaVM * jVm = nullptr;
const char * mainActivityClassName = "com/example/jni_helloworld/MainActivity";
// 这里会覆写默认的JNI_OnLoad函数
extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM * javaVm, void *) {
LOGD("动态注册 start");
::jVm = javaVm;
JNIEnv * jniEnv = nullptr;
int result = javaVm->GetEnv(reinterpret_cast<void **>(&jniEnv), JNI_VERSION_1_6);
if (result != JNI_OK) { // JNI_OK == 0
return -1;
}
jclass mainActivityClass = jniEnv->FindClass(mainActivityClassName);
jniEnv->RegisterNatives(
mainActivityClass,
jniNativeMethod,
sizeof(jniNativeMethod)/ sizeof(JNINativeMethod)
);
LOGD("动态注册 end");
return JNI_VERSION_1_6;
}
JNI线程
MainActivity
public native void testThread();
public void updateUI() {
// 主线程
if (Looper.getMainLooper() == Looper.myLooper()) {
Toast.makeText(MainActivity.this, "主线程updateUI...", Toast.LENGTH_SHORT).show();
} else { // 异步线程
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, "子线程updateUI...", Toast.LENGTH_SHORT).show();
}
});
}
}
native-lib.cpp
#include "com_example_jni_helloworld_MainActivity.h"
#include <pthread.h>
#include <android/log.h>
#define TAG "AAAAAAAAAAAAA"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__);
JavaVM * jVm = nullptr;
extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM * javaVm, void *) {
::jVm = javaVm;
return JNI_VERSION_1_6;
}
class MyContext {
public:
JNIEnv * jniEnv = nullptr; // 不能跨线程,会崩溃
jobject job = nullptr; // 不能跨线程,会崩溃
};
void * myThreadAction(void * pVoid) {
MyContext * myContext = static_cast<MyContext *>(pVoid);
const char * mainActivityClassName = "com/example/jni_helloworld/MainActivity";
// 不能跨线程,会崩溃
// jclass mainActivityClass = myContext->jniEnv->FindClass(mainActivityClassName);
// jclass mainActivityClass = myContext->jniEnv->GetObjectClass(myContext->job);
// 安卓进程只有一个JavaVM是全局的,可以跨线程
JNIEnv * jniEnv = nullptr;
// 附加当前异步线程后,会得到一个全新的 env,此env相当于是子线程专用env
jint attachResult = ::jVm->AttachCurrentThread(&jniEnv, nullptr);
if (attachResult != JNI_OK) {
return 0;
}
// 获取到class
jclass mainActivityClass = jniEnv->GetObjectClass(myContext->job);
// 获取方法
jmethodID jmethodId = jniEnv->GetMethodID(mainActivityClass, "updateUI", "()V");
// 调用方法
jniEnv->CallVoidMethod(myContext->job, jmethodId);
// 解除
::jVm->DetachCurrentThread();
return nullptr; // 必须 return 返回
}
extern "C"
JNIEXPORT void JNICALL
Java_com_example_jni_1helloworld_MainActivity_testThread(JNIEnv *env, jobject thiz) {
MyContext * myContext = new MyContext;
myContext->jniEnv = env;
myContext->job = env->NewGlobalRef(thiz); // 提升为全局引用
pthread_t pid;
pthread_create(&pid, nullptr, myThreadAction, myContext);
pthread_join(pid, nullptr);
}
排序
MainActivity
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("native-lib");
}
public native void test123();
public native void sort(int[] array);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button test_bt = findViewById(R.id.test_bt);
test_bt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int[] array = {1,25, 16, -5, 10, 100, 22};
sort(array);
for (int i = 0; i < array.length; i++) {
Log.d("AAAAAAAAAAAAA", "array数组 第" + i + "个元素:" + array[i]);
}
}
});
}
}
native-lib.cpp
#include "com_example_jni_helloworld_MainActivity.h"
#include <android/log.h>
#define TAG "AAAAAAAAAAAAA"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__);
int compare(const jint * n1, const jint * n2) {
return *n1 - *n2;
}
extern "C"
JNIEXPORT void JNICALL
Java_com_example_jni_1helloworld_MainActivity_sort(JNIEnv *env, jobject thiz, jintArray array) {
// 获取int数组中的所有元素
jint * intArray = env->GetIntArrayElements(array, nullptr);
// 获取数组长度
int length = env->GetArrayLength(array);
// 排序
qsort(intArray, length, sizeof(int), reinterpret_cast<int (*)(const void *, const void *)>(compare));
// 释放int数组
// 第三个参数 :jint mode
// 0 : 既要同步数据给 arr ,又要释放 intArray,会排序
// JNI_COMMIT: 会同步数据给 arr ,但是不会释放 intArray,会排序
// JNI_ABORT: 不同步数据给 arr ,但是会释放 intArray,所以上层看到就并不会排序
env->ReleaseIntArrayElements(array, intArray, JNI_COMMIT);
}
异常捕获
C++层处理异常 1
MainActivity:
static String name1 = "zhangSan";
public static native void testException1();
native-lib.cpp :
#include "com_example_jni_helloworld_MainActivity.h"
#include <android/log.h>
#define TAG "AAAAAAAAAAAAA"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__);
extern "C"
JNIEXPORT void JNICALL
Java_com_example_jni_1helloworld_MainActivity_testException1(JNIEnv *env, jclass clazz) {
// 正常的
// jfieldID name1 = env->GetStaticFieldID(clazz, "name1", "Ljava/lang/String;");
// 异常的,会崩溃
jfieldID name1 = env->GetStaticFieldID(clazz, "name1-22222", "Ljava/lang/String;");
// 捕获异常
jthrowable jthrowable1 = env->ExceptionOccurred();
if (jthrowable1) {
LOGD("native层,检测到异常情况...");
// 清除异常
env->ExceptionClear();
LOGD("native层,异常已被清除...");
}
}
C++层处理异常 2 - 抛给java层
MainActivity:
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("native-lib");
}
public native void test123();
static String name1 = "zhangSan";
public static native void testException2() throws NoSuchFieldException;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button test_bt = findViewById(R.id.test_bt);
test_bt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
testException2();
} catch (NoSuchFieldException e) {
e.printStackTrace();
Log.d("AAAAAAAAAAAAAAA", "Java层捕获到异常...");
}
}
});
}
}
native-lib.cpp :
#include "com_example_jni_helloworld_MainActivity.h"
#include <android/log.h>
#define TAG "AAAAAAAAAAAAA"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__);
extern "C"
JNIEXPORT void JNICALL
Java_com_example_jni_1helloworld_MainActivity_testException2(JNIEnv *env, jclass clazz) {
// 正常的
// jfieldID name1 = env->GetStaticFieldID(clazz, "name1", "Ljava/lang/String;");
// 异常的,会崩溃
jfieldID name1 = env->GetStaticFieldID(clazz, "name1-22222", "Ljava/lang/String;");
// 捕获异常
jthrowable jthrowable1 = env->ExceptionOccurred();
if (jthrowable1) {
LOGD("native层,检测到异常情况...");
// 清除异常
env->ExceptionClear();
LOGD("native层,异常已被清除...");
// Throw抛一个java的Throwable对象
jclass no_clzz = env->FindClass("java/lang/NoSuchFieldException");
env->ThrowNew(no_clzz, "NoSuchFieldException异常了哦...");
}
}
C++层处理异常 2 - java层出现问题时
MainActivity:
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("native-lib");
}
public static native void testException3();
public static void show() throws Exception {
Log.d("AAAAAAAAAAAAAAA", "show() 抛的异常...");
throw new NullPointerException("show() 抛的异常 NullPointerException ...");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button test_bt = findViewById(R.id.test_bt);
test_bt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
testException3();
}
});
}
}
native-lib.cpp :
#include "com_example_jni_helloworld_MainActivity.h"
#include <android/log.h>
#define TAG "AAAAAAAAAAAAA"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__);
extern "C"
JNIEXPORT void JNICALL
Java_com_example_jni_1helloworld_MainActivity_testException3(JNIEnv *env, jclass clazz) {
jmethodID showID = env->GetStaticMethodID(clazz, "show", "()V");
env->CallStaticVoidMethod(clazz, showID);
// 这里是需要紧跟着可能出现异常的代码处
if (env->ExceptionCheck()) {
LOGD("native层,检测到异常情况...");
// 输出描述
env->ExceptionDescribe();
// 清除异常
env->ExceptionClear();
LOGD("native层,异常已被清除...");
}
}