Android JNI

本文详细介绍了JNI(Java Native Interface)和Android NDK的使用,包括NDK的优势、JNI的数据类型、方法调用、对象操作、异常处理、线程管理和动态库加载。此外,还展示了如何在C++中调用Java方法、处理数组和对象,以及如何处理异常和线程同步。同时,讨论了静态与动态注册的区别,并提供了异常捕获的示例。
摘要由CSDN通过智能技术生成

    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类型签名描述
jbooleanbooleanZ无符号8位整型
jbytebyteB有符号8位整型
jcharcharC无符号16位整型
jshortshortS无符号16位整型
jintintI32位整型
jlonglongJ64位整型
jfloatfloatF32位浮点型
jdoubledoubleD64位浮点型
voidvoidV无类型

引用类型

JNI类型Java类型签名描述
jobjectObjectObject类型
jclassClass[LClass类型
jstringString[Ljava/lang/String;字符串
jobjectArrayObject[][Ljava/lang/Object;对象数组
jbooleanArrayboolean[][Zboolean数组
jbyteArraybyte[][Bbyte数组
jcharArraychar[][Cchar数组
jshortArrayshort[][Sshort数组
jintArrayint[][iint数组
jlongArraylong[][Jlong数组
jfloatArrayfloat[][Ffloat数组
jdoubleArraydouble[][Ddouble数组
jthrowableThrowableThrowable

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层,异常已被清除...");
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值