Android JNI3--JNI基础

这篇具有很好参考价值的文章主要介绍了Android JNI3--JNI基础。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1,C预处理器

C 预处理器不是编译器的组成部分,但是它是编译过程中一个单独的步骤。简言之,C 预处理器只不过是一个文本替换工具而已,它们会指示编译器在实际编译之前完成所需的预处理。我们将把 C 预处理器(C Preprocessor)简写为 CPP。

所有的预处理器命令都是以井号(#)开头。它必须是第一个非空字符,为了增强可读性,预处理器指令应从第一列开始。

#include  导入头文件
#if       if判断操作  【if的范畴 必须endif】
#elif     else if
#else     else
#endif    结束if
#define   定义一个宏
#ifdef    如果定义了这个宏 【if的范畴 必须endif】
#ifndef   如果没有定义这个宏 【if的范畴 必须endif】
#undef    取消宏定义
#pragma   设定编译器的状态

#if  举例:

#if 1 // if
    cout <<  "真" << endl;

#elif 0 // else if
    cout <<  "假" << endl;

#else
    cout << "都不满足" << endl;

#endif // 结束if
    cout << "结束if" << endl;
#ifdef举例:
#ifndef isRelease // 如果没有isRelease这个宏
#define isRelease 1 // 是否是正式环境下 【我就定义isRelease这个宏】

#if isRelease == true
#define RELEASE // 正式环境下 定义RELEASE宏

#elif isRelease == false
#define DEBUG // 测试环境下  定义DEBUG宏

#endif 
#endif 

#ifdef DEBUG // 是否定义了DEBUG这个宏
    cout << "在测试环境" << endl;
#else RELEASE
    cout << "在正式环境" << endl;
#endif // 结束IF

宏的取消#undef: 

#ifdef TEST// 是否定义了这个宏
   cout << "TEST" << endl;
#undef TEST// 取消宏的定义,下面的代码,就没法用这个宏了,相当于:没有定义过TEST宏
#endif

宏变量:

#define VALUE_I 99
#define VALUE_S "字符串"

int main() {
    int i = VALUE_I; // 预处理阶段 宏会直接完成文本替换工作,替换后的样子:int i = 99;
    string s = VALUE_S; // 预处理阶段 宏会直接完成文本替换工作,替换后的样子:string s = "字符串";

    return 0;
}

宏函数,宏函数都是大写

优点:文本替换,不会造成函数调用开销

缺点:会导致代码体积增大

#define ADD(n1, n2) n1 + n2

int main() {
    int r = ADD(1, 2);
    cout << r << endl;//返回3

    return 0;
}

2,JNI  java与native代码互调

在MainActivity中调用native方法:

public class MainActivity extends AppCompatActivity {

    public String name = "测试"; // 签名:Ljava/lang/String;

    static {
        System.loadLibrary("native-lib"); //静态代码块中加载native-lib文件
    }   
  
    public native void changeName();//调用native方法


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TextView tv = findViewById(R.id.txt_name);
        changeName();
        tv.setText(name);
    }

}

使用命令javah -classpath . -jni com.test.MainActivity 生成头文件com_test_MainActivity.h

#include <jni.h>
#include <string>

// 解决循环Copy的问题 第二次就进不来了
#ifndef _Included_com_test_MainActivity // 如果没有定义这个宏
#define _Included_com_test_MainActivity // 我就定义这个宏

#ifdef __cplusplus // 如果是C++环境
extern "C" { // 全部采用C的方式 不准你函数重载,函数名一样的问题
#endif

// 函数的声明
JNIEXPORT jstring JNICALL Java_com_test_MainActivity_changeName
  (JNIEnv *, jobject);


#ifdef __cplusplus // 省略  如果是C++,啥事不干
}

#endif

在native-lib.cpp里面实现changeName()方法

#include "com_test_MainActivity.h"

// NDK工具链里面的 log 库 引入过来
#include <android/log.h>

#define TAG "MainAcitvity"
//自动填充
#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": 必须采用C的编译方式
// // 无论是C还是C++ 最终是调用到 C的JNINativeInterface,所以必须采用C的方式 extern "C"
// 函数的实现
extern "C"

JNIEXPORT  // 标记该方法可以被外部调用

jstring // Java <---> native 转换用的

JNICALL // 代表是 JNI标记

// Java_包名_类名_方法名 

// JNIEnv * env  JNI:的桥梁环境   所有的JNI操作,必须靠他

// jobject jobj  谁调用,就是谁的实例  MainActivity this
// jclass clazz 谁调用,就是谁的class MainActivity.class



extern "C"
JNIEXPORT void JNICALL
Java_com_test_MainActivity_changeName(JNIEnv *env, jobject thiz) {
   // 获取class
   jclass j_cls = env->GetObjectClass(thiz);

   // 获取属性  L对象类型 都需要L
   // jfieldID GetFieldID(MainActivity.class, 属性名, 属性的签名)
   jfieldID j_fid = env->GetFieldID(j_cls, "name", "Ljava/lang/String;");

   // 转换工作 jstring 为JNI类型
   jstring j_str = static_cast<jstring>(env->GetObjectField(thiz ,j_fid));

   // 打印字符串  目标
   char * c_str = const_cast<char *>(env->GetStringUTFChars(j_str, NULL));
   LOGD("native : %s\n", c_str);
   LOGE("native : %s\n", c_str);
   LOGI("native : %s\n", c_str);

    // 修改名字 返回给java层
    jstring jName = env->NewStringUTF("张三");
    env->SetObjectField(thiz, j_fid, jName);
}

签名规则:

    Java的boolean  --- Z 
    Java的byte  --- B
    Java的char  --- C
    Java的short  --- S
    Java的int  --- I
    Java的long  --- J   
    Java的float  --- F
    Java的double  --- D
    Java的void  --- V
    Java的引用类型  --- Lxxx/xxx/xx/类名;
    Java的String  --- Ljava/lang/String;
    Java的array  int[]  --- [I         double[][][][]  --- [[[D
    int add(char c1, char c2) ---- (CC)I
    void a()     ===  ()V

Java调用native方法,native方法调用java方法,互调

在MainActivity中:

public class MainActivity extends AppCompatActivity {

    static {
        System.loadLibrary("native-lib");
    }

    public native void callSum();

    public int sum(int number1, int number2) {
        return number1 + number2;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        callSum();
    }

}

在native-lib.cpp中,实现callSum()代码:

extern "C"
JNIEXPORT void JNICALL
Java_com_test_MainActivity_callSum(JNIEnv *env, jobject job) {
   
    jclass  mainActivityClass = env->GetObjectClass(job);

    // GetMethodID(MainActivity.class, 方法名, 方法的签名)
   jmethodID j_mid = env->GetMethodID(mainActivityClass, "sum", "(II)I");

   // 调用 Java的方法
   jint sum = env->CallIntMethod(job, j_mid, 8, 8);
   LOGE("sum result:%d", sum);

}

3,JNI 数组操作

MainActivity:

public class MainActivity extends AppCompatActivity {

    private final static String TAG = MainActivity.class.getSimpleName();

    static {
        // System.load(D:/xxx/xxxx/xxx/native-lib); 这种是可以绝对路径的加载动态链接库文件
        System.loadLibrary("native-lib"); 
        // 这种是从库目录遍历层级目录,去自动的寻找   apk里面的lib/libnative-lib.so
    }

    public native void testArrayAction(int count, String textInfo, int[] ints, String[] strs);

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        arrTest();
    }

    public void arrTest() {
        int[] ints = new int[]{1,2,3,4,5,6}; // 基本类型的数组

        String[] strs = new String[]{"张三","周五","王五"}; // 对象类型的数组

        testArrayAction(99, "你好", ints, strs);

    }
}

在native-lib.cpp中:

extern "C"
JNIEXPORT void JNICALL
Java_com_test_MainActivity_testArrayAction(JNIEnv *env, jobject thiz,
                                                             jint count,
                                                             jstring text_info,
                                                             jintArray ints,
                                                             jobjectArray strs) {
    // ① 基本数据类型  jint count, jstring text_info, 最简单的
    int countInt = count; // jint本质是int,所以可以用int接收
    LOGI("参数一 countInt:%d\n", countInt);

    // const char* GetStringUTFChars(jstring string, jboolean* isCopy)
    const char *textInfo = env->GetStringUTFChars(text_info, NULL);
    LOGI("参数二 textInfo:%s\n", textInfo);

    // ② 把int[] 转成 int*
    // jint* GetIntArrayElements(jintArray array, jboolean* isCopy)
    int *jintArray = env->GetIntArrayElements(ints, NULL);

    // Java层数组的长度
    // jsize GetArrayLength(jarray array) // jintArray ints 可以放入到 jarray的参数中去
    jsize size = env->GetArrayLength(ints);

    for (int i = 0; i < size; ++i) {
        *(jintArray + i) += 100; // C++的修改,影响不了Java层
        LOGI("参数三 int[]:%d\n", *jintArray + i);
    }
    // 目前无法控制Java的数组 变化 +100
    // 操作杆 ----> JMV
    // env->

    /**
     * 0:           刷新Java数组,并 释放C++层数组
     * JNI_COMMIT:  只提交 只刷新Java数组,不释放C++层数组
     * JNI_ABORT:   只释放C++层数组
     */
    env->ReleaseIntArrayElements(ints, jintArray, 0);

    // ③:jobjectArray 代表是Java的引用类型数组,不一样
    jsize strssize = env->GetArrayLength(strs);
    for (int i = 0; i < strssize; ++i) {

        jstring jobj = static_cast<jstring>(env->GetObjectArrayElement(strs, i));

        // 模糊:isCopy内部启动的机制
        // const char* GetStringUTFChars(jstring string, jboolean* isCopy)
        const char *jobjCharp = env->GetStringUTFChars(jobj, NULL);

        LOGI("参数四 引用类型String 具体的:%s\n", jobjCharp);

        // 释放jstring
        env->ReleaseStringUTFChars(jobj, jobjCharp);
    }
}

4,JNI对象操作

创建Student类

public class Student {

    private final static String TAG = Student.class.getSimpleName();

    public String name;
    public int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        Log.d(TAG, "Java setName name:" + name);
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        Log.d(TAG, "Java setAge age:" + age);
        this.age = age;
    }

    public static void showInfo(String info) {
        Log.d(TAG, "showInfo info:" + info);
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

MainActivity:

public class MainActivity extends AppCompatActivity {

  

    static {
        System.loadLibrary("native-lib");
    }

    public native void putObject(Student student, String str); // 传递引用类型,传递对象
    public native void insertObject(); // 凭空创建Java对象
    public native void testQuote(); // 测试引用
    public native void delQuote(); // 释放全局引用

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
    }

    public void test02(View view) {
        Student student = new Student(); // Java new
        student.name = "史泰龙";
        student.age = 88;
        putObject(student, "九阳神功");

    }

    public void test03(View view) {
        insertObject();
    }

    public void test04(View view) {
        testQuote();
    }

    public void test05(View view) {
        delQuote(); // 必须释放全局引用
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        delQuote(); // Activity销毁时: 必须释放全局引用
    }
}

在native-lib.cpp中:

extern "C"
JNIEXPORT void JNICALL
Java_com_test_MainActivity_putObject(JNIEnv *env,
                                                       jobject thiz,
                                                       jobject student,
                                                       jstring str) {
    const char *strChar = env->GetStringUTFChars(str, NULL);
    LOGI("strChar:%s\n", strChar);
    env->ReleaseStringUTFChars(str, strChar);

    // --------------
    // 1.寻找类 Student
    // jclass studentClass = env->FindClass("com/test/Student"); // 第一种
    jclass studentClass = env->GetObjectClass(student); // 第二种

    // 2.Student类里面的函数规则  签名
    jmethodID setName = env->GetMethodID(studentClass, "setName", "(Ljava/lang/String;)V");
    jmethodID getName = env->GetMethodID(studentClass, "getName", "()Ljava/lang/String;");
    jmethodID showInfo = env->GetStaticMethodID(studentClass, "showInfo", "(Ljava/lang/String;)V");

    // 3.调用 setName
    jstring value = env->NewStringUTF("Zhangsan");
    env->CallVoidMethod(student, setName, value);

    // 4.调用 getName
    jstring getNameResult = static_cast<jstring>(env->CallObjectMethod(student, getName));
    const char *getNameValue = env->GetStringUTFChars(getNameResult, NULL);
    LOGE("调用到getName方法,值是:%s\n", getNameValue);

    // 5.调用静态showInfo
    jstring jstringValue = env->NewStringUTF("静态方法你好,我是C++");
    env->CallStaticVoidMethod(studentClass, showInfo, jstringValue);
}
// C++ 堆 栈 ...
// JNI函数  局部引用,全局引用,...
extern "C"
JNIEXPORT void JNICALL
Java_com_test_MainActivity_insertObject(JNIEnv *env, jobject thiz) {
    /*jstring str = env->GetStringUTFChars();
    jstring str = env->GetStringUTFChars();
    jstring str = env->GetStringUTFChars();
    jstring str = env->GetStringUTFChars();
    jstring str = env->GetStringUTFChars();
    jstring str = env->GetStringUTFChars();
    jstring str = env->GetStringUTFChars();

    // 好习惯:
    // 我用完了,我记释放,在我函数执行过程中,不会导致 内存占用多
    env->ReleaseStringUTFChars()*/


    // 1.通过包名+类名的方式 拿到 Student class  凭空拿class
    const char *studentstr = "com/test/Student";
    jclass studentClass = env->FindClass(studentstr);

    // 2.通过student的class  实例化此Student对象   C++ new Student
    jobject studentObj = env->AllocObject(studentClass); // AllocObject 只实例化对象,不会调用对象的构造函数

    // 方法签名的规则
    jmethodID setName = env->GetMethodID(studentClass, "setName", "(Ljava/lang/String;)V");
    jmethodID setAge = env->GetMethodID(studentClass, "setAge", "(I)V");

    // 调用方法
    jstring strValue = env->NewStringUTF("zhansan");
    env->CallVoidMethod(studentObj, setName, strValue);
    env->CallVoidMethod(studentObj, setAge, 99);


    // env->NewObject() // NewObject 实例化对象,会调用对象的构造函数


    // ====================  下面是 Person对象  调用person对象的  setStudent 函数等

    // 4.通过包名+类名的方式 拿到 Student class  凭空拿class
    const char *personstr = "com/test/Person";
    jclass personClass = env->FindClass(personstr);

    jobject personObj = env->AllocObject(personClass); // AllocObject 只实例化对象,不会调用对象的构造函数

    // setStudent 此函数的 签名 规则
    jmethodID setStudent = env->GetMethodID(personClass, "setStudent",
            "(Lcom/test/Student;)V");

    env->CallVoidMethod(personObj, setStudent, studentObj);

    // 规范:一定记得释放【好习惯】
    // 第一类
    env->DeleteLocalRef(studentClass);
    env->DeleteLocalRef(personClass);
    env->DeleteLocalRef(studentObj);
    env->DeleteLocalRef(personObj);

    // 第二类
    // env->ReleaseStringUTFChars()

    // TODO 局部引用: jobject jclass jstring ...  【函数结束后,会自动释放】
}


jclass dogClass; // 你以为这个是全局引用,实际上他还是局部引用

extern "C"
JNIEXPORT void JNICALL
Java_com_test_MainActivity_testQuote(JNIEnv *env, jobject thiz) {
    if (NULL == dogClass) {
        /*const char * dogStr = "com/derry/as_jni_project/Dog";
        dogClass = env->FindClass(dogStr);*/

        // 升级全局引用: JNI函数结束也不释放,反正就是不释放,必须手动释放   ----- 相当于: C++ 对象 new、手动delete
        const char * dogStr = "com/test/Dog";
        jclass temp = env->FindClass(dogStr);
        dogClass = static_cast<jclass>(env->NewGlobalRef(temp)); // 提升全局引用
        // 记住:用完了,如果不用了,马上释放
        env->DeleteLocalRef(temp);
    }

    // <init> V  是不会变的

    // 构造函数一
    jmethodID init = env->GetMethodID(dogClass, "<init>", "()V");
    jobject dog = env->NewObject(dogClass, init);

    // 构造函数2
    init = env->GetMethodID(dogClass, "<init>", "(I)V");
    dog = env->NewObject(dogClass, init, 100);


    // 构造函数3
    init = env->GetMethodID(dogClass, "<init>", "(II)V");
    dog = env->NewObject(dogClass, init, 200, 300);

    // 构造函数4
    init = env->GetMethodID(dogClass, "<init>", "(III)V");
    dog = env->NewObject(dogClass, init, 400, 500, 600);

    env->DeleteLocalRef(dog); // 释放
    // dogClass = NULL; // 是不是问题解决了,不能这样干(JNI函数结束后,还怎么给你释放呢)

    // 这样就解决了
    /*env->DeleteGlobalRef(studentClass);
    studentClass = NULL;*/
}

// JNI函数结束,会释放局部引用   dogClass虽然被释放,但是还不等于NULL,只是一个悬空指针而已,所以第二次进不来IF,会崩溃

// 非常方便,可以使用了
extern int age; // 声明age
extern void show(); // 声明show函数  5000行代码

// 手动释放全局引用
extern "C"
JNIEXPORT void JNICALL
Java_com_test_MainActivity_delQuote(JNIEnv *env, jobject thiz) {
   if (dogClass != NULL) {
       LOGE("全局引用释放完毕,上面的按钮已经失去全局引用,再次点击会报错");
       env->DeleteGlobalRef(dogClass);
       dogClass = NULL; // 最好给一个NULL,指向NULL的地址,不要去成为悬空指针,为了好判断悬空指针的出现
   }

   // 测试下
   show();
}

5,静态注册动态注册

静态注册:

前面例子所用的都是静态注册

静态注册的优点:开发简单

静态注册的缺点:

1,JNI函数名非常长

2,捆绑上层 需要java层的包名和类名

3,在运行期才会去匹配JNI函数,性能上低于动态注册

动态注册:

MainActivity:

public class MainActivity extends AppCompatActivity {


    static {
        System.loadLibrary("native-lib"); // 默认调用:JNI_OnLoad
    }
    public native void dynamicJavaMethod(); // 动态注册

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        dynamicJavaMethod(); 
    }

}

native-lib.cpp:

JavaVM *jVm = nullptr; //系统乱值,C++11后,取代NULL,作用是可以初始化指针赋值
const char *mainActivityClassName = "com/test/MainActivity";

// native 真正的函数
// void dynamicMethod(JNIEnv *env, jobject thiz) { // OK的
void dynamicMethod() { // 也OK  如果你用不到  JNIEnv jobject ,可以不用写
    LOGD("我是动态注册的函数 dynamicMethod...");
}

static const JNINativeMethod jniNativeMethod[] = {
        {"dynamicJavaMethod", "()V",                   (void *) (dynamicMethod01)},
};

// JNI JNI_OnLoad函数,如果你不写JNI_OnLoad,默认就有JNI_OnLoad,如果你写JNI_OnLoad函数 覆写默认的JNI_OnLoad函数
extern "C"
JNIEXPORT jint JNI_OnLoad(JavaVM *javaVm, void *) {
    // this.javaVm = javaVm;
    ::jVm = javaVm;

    // 做动态注册 全部做完

    JNIEnv *jniEnv = nullptr;
    int result = javaVm->GetEnv(reinterpret_cast<void **>(&jniEnv), JNI_VERSION_1_6);

    // result 等于0  成功 
    if (result != JNI_OK) {
        return -1; // 会崩溃
    }
    jclass mainActivityClass = jniEnv->FindClass(mainActivityClassName);

    // jint RegisterNatives(Class, 我们的数组==jniNativeMethod, 注册的数量 = 1)
    jniEnv->RegisterNatives(mainActivityClass,
                            jniNativeMethod,
                            sizeof(jniNativeMethod) / sizeof(JNINativeMethod));

    LOGE("动态注册成功");

    return JNI_VERSION_1_6; //  // AS的JDK在JNI默认最高1.6
}

6,JNI线程

MainActivity:

public class MainActivity extends AppCompatActivity {

    static {
        System.loadLibrary("native-lib"); // 默认调用:JNI_OnLoad
    }

    public native void naitveThread(); // Java层 调用 Native层 的函数,完成JNI线程

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        naitveThread();
    }

  
    public void updateActivityUI() {
        if (Looper.getMainLooper() == Looper.myLooper()) { // TODO C++ 用主线程调用到此函数 ---->  主线程
            new AlertDialog.Builder(MainActivity.this)
                    .setTitle("UI")
                    .setMessage("updateActivityUI Activity UI ...")
                    .setPositiveButton("ok", null)
                    .show();
        } else {  // TODO  C++ 用异步线程调用到此函数 ---->  异步线程
            Log.d(TAG, "updateActivityUI 所属于子线程,只能打印日志了..");

            runOnUiThread(new Runnable() { // 哪怕是异步线程  UI操作 正常下去 runOnUiThread
                @Override
                public void run() {

                    // 可以在子线程里面 操作UI
                    new AlertDialog.Builder(MainActivity.this)
                            .setTitle("updateActivityUI")
                            .setMessage("所属于子线程,只能打印日志了..")
                            .setPositiveButton("ok", null)
                            .show();
                }
            });
        }
    }
}

native-lib.cpp:

class TestContext {
public:
    JNIEnv *jniEnv = nullptr;  // 不能跨线程 ,会崩溃
    jobject instance = nullptr; // 不能跨线程 ,会崩溃
};

void *myThreadTaskAction(void *pVoid) { // 当前是异步线程
    LOGE("myThreadTaskAction run");

    // 需求:有这样的场景,例如:下载完成 ,下载失败,等等,必须告诉Activity UI端,所以需要在子线程调用UI端

    // 这两个是必须要的
    // JNIEnv *env
    // jobject thiz 

    TestContext * testContext = static_cast<TestContext *>(pVoid);

    // jclass mainActivityClass = testContext ->jniEnv->FindClass(mainActivityClassName); // 不能跨线程 ,会崩溃
    // mainActivityClass = myContext->jniEnv->GetObjectClass(myContext->instance); // 不能跨线程 ,会崩溃

    // 解决方式 (安卓进程只有一个 JavaVM,是全局的,是可以跨越线程的)
    JNIEnv * jniEnv = nullptr; // 全新的JNIEnv  异步线程里面操作
    jint attachResult = ::jVm->AttachCurrentThread(&jniEnv, nullptr); // 附加当前异步线程后,会得到一个全新的 env,此env相当于是子线程专用env
    if (attachResult != JNI_OK) {
        return 0; // 附加失败,返回了
    }

    // 1.拿到class
    jclass mainActivityClass = jniEnv->GetObjectClass(testContext->instance);

    // 2.拿到方法
    jmethodID updateActivityUI = jniEnv->GetMethodID(mainActivityClass, "updateActivityUI", "()V");

    // 3.调用
    jniEnv->CallVoidMethod(testContext->instance, updateActivityUI);

    ::jVm->DetachCurrentThread(); // 必须解除附加,否则报错

    LOGE("C++ 异步线程OK")

    return nullptr;
}
extern "C"
JNIEXPORT void JNICALL
Java_com_test_MainActivity_naitveThread(JNIEnv *env, jobject job) { // 当前是主线程
  
    TestContext * testContext = new TestContext;
    testContext->jniEnv = env;
    // testContext->instance = job; // 默认是局部引用,会崩溃
    testContext->instance = env->NewGlobalRef(job); // 提升全局引用

    pthread_t pid;
    pthread_create(&pid, nullptr, myThreadTaskAction, testContext);
    pthread_join(pid, nullptr);
}

小结:

1,JavaVM是全局引用,绑定当前进程,只有一个地址

2,JNIEnv线程绑定,绑定主线程,绑定子线程

3,jobject  谁调用JNI函数,谁的实例会给jobject

4,JNIEnv *env 不能跨线程,否在崩溃,可以跨函数,可以使用全局的JavaVM附加当前异步线程,得到权限env操作

5,jobject thiz不能跨线程,不能跨函数,否在崩溃,默认是局部引用,提升全局引用,可以解决此问题

6,JavaVM 能够跨线程,跨函数

7,静态缓存

1,先看下非静态缓存:

MainActivity:

public class MainActivity extends AppCompatActivity {

    static {
        System.loadLibrary("native-lib");
    }

    // 假设这里定义了一堆变量
    static String name1  ="T1";
    static String name2  ="T2";
    static String name3  ="T3";
    static String name4  ="T4";
    static String name5  ="T5";
    static String name6  ="T6";   

    public static native void localCache(String name); // 普通的局部缓存

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        localCache("修改后");
    }
}

native-lib.cpp:

extern "C"
JNIEXPORT void JNICALL
Java_com_test_MainActivity_localCache(JNIEnv *env, jclass clazz, jstring name) {

    // 非静态缓存

    jfieldID f_id = nullptr;

    if (f_id == nullptr) {
        f_id = env->GetStaticFieldID(clazz, "name1", "Ljava/lang/String;"); // 有点耗费性能
    } else  {
        LOGE("空的");
    }

    env->SetStaticObjectField(clazz, f_id, name); // 修改后

    f_id = nullptr;

2,静态缓存:

MainActivity:

public class MainActivity extends AppCompatActivity {

    static {
        System.loadLibrary("native-lib");
    }

    static String name1  ="T1";
    static String name2  ="T2";
    static String name3  ="T3";
    static String name4  ="T4";
    static String name5  ="T5";
    static String name6  ="T6";

    public static native void initStaticCache(); // 初始化静态缓存
    public static native void staticCache(String name);
    public static native void clearStaticCache(); // 清除化静态缓存

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    // 事件
    public void staticCacheAction(View view) {
        // 初始化静态缓存
        initStaticCache(); // 如果是在类里面, Person Student ,必须在类的构造函数初始化

        staticCache("修改后");
        staticCache("修改后");
        staticCache("修改后");
        staticCache("修改后");
        staticCache("修改后");
        staticCache("修改后");
        staticCache("修改后");

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        clearStaticCache(); // 清除化静态缓存
    }
}

native-lib.cpp:

static jfieldID f_name1_id = nullptr;
static jfieldID f_name2_id = nullptr;
static jfieldID f_name3_id = nullptr;
static jfieldID f_name4_id = nullptr;
static jfieldID f_name5_id = nullptr;
static jfieldID f_name6_id = nullptr;

// 先缓存
extern "C"
JNIEXPORT void JNICALL
Java_com_test_MainActivity_initStaticCache(JNIEnv *env, jclass clazz) {
    f_name1_id = env->GetStaticFieldID(clazz, "name1", "Ljava/lang/String;");
    f_name2_id = env->GetStaticFieldID(clazz, "name2", "Ljava/lang/String;");
    f_name3_id = env->GetStaticFieldID(clazz, "name3", "Ljava/lang/String;");
    f_name4_id = env->GetStaticFieldID(clazz, "name4", "Ljava/lang/String;");
    f_name5_id = env->GetStaticFieldID(clazz, "name5", "Ljava/lang/String;");
    f_name6_id = env->GetStaticFieldID(clazz, "name6", "Ljava/lang/String;");
}
// 使用
extern "C"
JNIEXPORT void JNICALL
Java_com_test_MainActivity_staticCache(JNIEnv *env, jclass clazz, jstring name) {
    // 不会反复 GetStaticFieldID 提供性能
    env->SetStaticObjectField(clazz, f_name1_id, name);
    env->SetStaticObjectField(clazz, f_name2_id, name);
    env->SetStaticObjectField(clazz, f_name3_id, name);
    env->SetStaticObjectField(clazz, f_name4_id, name);
    env->SetStaticObjectField(clazz, f_name5_id, name);
    env->SetStaticObjectField(clazz, f_name6_id, name);
}
// 清除
extern "C"
JNIEXPORT void JNICALL
Java_com_test_MainActivity_clearStaticCache(JNIEnv *env, jclass clazz) {
    f_name1_id = nullptr;
    f_name2_id = nullptr;
    f_name3_id = nullptr;
    f_name4_id = nullptr;
    f_name5_id = nullptr;
    f_name6_id = nullptr;
}

8,异常处理

public class MainActivity3 extends AppCompatActivity {
    static {
        System.loadLibrary("native-lib");
    }

    static String name1 = "T1";

    // 下面是异常处理
    public static native void exception();
    public static native void exception2() throws NoSuchFieldException; // NoSuchFieldException接收C++层抛上来的异常
    public static native void exception3();

    public static native String derryAction();

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void exceptionAction(View view) {
        exception(); // C++层自己做了补救措施了

        // 捕获人家C++层抛上来的异常
        try {
            exception2();
        } catch (NoSuchFieldException exception) {
            exception.printStackTrace();
            Log.d("test", "exceptionAction: 异常被我捕获了");
        }

        exception3();

        String result = derryAction();
    }

    // 专门给 C++(native层) 层调用的 函数
    public static void show() throws Exception {
        Log.d("test", "show: 1111");
        Log.d("test", "show: 1111");
        Log.d("test", "show: 1111");
        Log.d("test", "show: 1111");
        Log.d("test", "show: 1111");
        Log.d("test", "show: 1111");

        throw new NullPointerException("我是Java中抛出的异常,我的show方法里面发送了Java逻辑错误");
    }

}

native-lib.cpp:文章来源地址https://www.toymoban.com/news/detail-655354.html

// 异常1  【native层主动干的异常】
extern "C"
JNIEXPORT void JNICALL
Java_com_test_MainActivity_exception(JNIEnv *env, jclass clazz) {
    // 假设现在想操作 name2 ,没有name2就会在native层崩溃
    jfieldID f_id = env->GetStaticFieldID(clazz, "name2", "Ljava/lang/String;");

    //崩溃后,有两种解决方案

    // 方式1 补救措施

    jthrowable thr =  env->ExceptionOccurred(); // 监测本次执行,到底有没有异常   JNI函数里面代码有问题

    if(thr) { // 非0 进去,监测到有异常
        LOGD("C++层有异常 监测到了");

        env->ExceptionClear(); // 此异常被清除

        // 开始 补救措施
        jfieldID f_id = env->GetStaticFieldID(clazz, "name1", "Ljava/lang/String;");
    }


}

// 异常2 【native层主动干的异常】
extern "C"
JNIEXPORT void JNICALL
Java_com_test_MainActivity_exception2(JNIEnv *env, jclass clazz) {
    // 假设现在想操作 name2 ,没有name2就会在native层崩溃掉
    jfieldID f_id = env->GetStaticFieldID(clazz, "name2", "Ljava/lang/String;");

    //崩溃后,有两种解决方案

    // 方式2 往Java层抛

    jthrowable jthrowable = env->ExceptionOccurred(); // 监测本次执行,到底有没有异常   JNI函数里面代码有问题

    if(jthrowable) { // 非0 进去,监测到有异常
        LOGD("C++层有异常 监测到了");

        env->ExceptionClear(); // 此异常被清除

        // Throw抛一个 Java的对象     java/lang/String    java/xxxxx/xxx/NullExxx
        jclass clz = env->FindClass("java/lang/NoSuchFieldException");
        env->ThrowNew(clz, "NoSuchFieldException 找不到 name2");
    }
}
// 异常3 【native层被动干的异常  被动 我是Java方法坑了】
extern "C"
JNIEXPORT void JNICALL
Java_com_test_MainActivity_exception3(JNIEnv *env, jclass clazz) {
    jmethodID showID = env->GetStaticMethodID(clazz, "show", "()V");
    env->CallStaticVoidMethod(clazz, showID); // 是不是这句话崩溃的   1是   2不是   答:不是,只是他引起的而已

    // ExceptionCheck 《==》 慢慢的奔溃的,相当于给了你空余时间,既然不是马上崩溃,我就可以检测

    // JNI函数里面代码有问题 没有问题,给你空余时间,慢慢的崩溃
    if (env->ExceptionCheck()) {
        env->ExceptionDescribe(); // 输出描述 信息
        env->ExceptionClear(); // 此异常被清除    业务逻辑控制
    }

    // 注意实现:
    /*// 崩溃后,下面的语句,照样打印
    LOGI("C++层>>>>>>>>>>>>>>>>>>>>>>>>>>>>1");
    LOGI("C++层>>>>>>>>>>>>>>>>>>>>>>>>>>>>2");
    LOGI("C++层>>>>>>>>>>>>>>>>>>>>>>>>>>>>3");
    LOGI("C++层>>>>>>>>>>>>>>>>>>>>>>>>>>>>4");
    LOGI("C++层>>>>>>>>>>>>>>>>>>>>>>>>>>>>5");

    env->NewStringUTF("AAAA"); // 局部引用 崩溃给磨平*/
}


到了这里,关于Android JNI3--JNI基础的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Android JNI系列详解之ndk-build工具的使用

    一、Android项目中使用ndk-build工具编译库文件 之前介绍过CMake编译工具的使用,今天介绍一种ndk自带的编译工具ndk-build的使用。 ndk-build目前主要有两种配置使用方式:  如上图所示,第一种方式是Android.mk+Application.mk+gradle的方式生成库文件;第二种方式是Android.mk+Application.mk+命

    2024年02月10日
    浏览(34)
  • Android JNI--C语言基础

    相当于java的导包操作 例如:#include stdio.h 打印需要注意的是,不和java一样随便打印的,需要占位符 在java中,万物皆对象。 在Linux中,万物皆文件 在C 语言中,万物皆指针 指针其实可以理解为地址   代表取出地址 例如: 例如: 只要记住一句话,内存地址就是指针,指针就

    2024年02月14日
    浏览(27)
  • Android JNI系列详解之CMake和ndk-build编译工具介绍

    一、前提 CMake和ndk-build只是编译工具,本次主要介绍ndk-build和CMake的区别,下节课介绍他们的使用。 二、CMake工具介绍 CMake:cross platform make,是跨平台的编译工具 CMake是在AndroidStudio2.2之后引入(目前默认的NDK编译工具),使用时配合CMakeList.txt和gradle。 对C++的配置: 默认配置

    2024年02月12日
    浏览(34)
  • 【Android -- JNI 和 NDK】Java 和 C/C++ 之间传递参数和返回值

    本文主要介绍 JNI 的数据传递上,即 Java 如何传递对象给 C++; 而 C++ 又如何将数据封装成 Java 所需的对象。 1. 基本数据类型 传递 java 的基本类型是非常简单而直接的,一个 jxxx 之类的类型已经定义在本地系统中了,比如: jint, jbyte, jshort, jlong, jfloat, jdouble, jchar 和 jboolean 分别

    2024年02月09日
    浏览(37)
  • Android JNI基础

    JNI(Java Native Interface)是Java提供的一种机制,用于实现Java和本地(Native)代码之间的交互。通过JNI,Java程序可以调用本地代码(如C、C++)中的函数,实现跨语言的互操作性。 JNI主要用于以下几个方面: 调用系统级别的库和函数:可以使用JNI调用操作系统提供的底层功能,

    2024年04月09日
    浏览(41)
  • android使用ndk开发

    ndk sdk要单独下载和android sdk不同 https://developer.android.google.cn/ndk/downloads?hl=zh-cn 解压后添加ndk路径到path即可 gradle下载太慢使用国内镜像 distributionUrl=https://mirrors.cloud.tencent.com/gradle/gradle-6.7.1-all.zip 执行gradlew.bat gradlew //检查环境,下载gradle依赖项 gradlew tasks //查看可执行任务 gr

    2024年01月17日
    浏览(43)
  • Android NDK基础介绍及例子

    目录 null NDK介绍 java调用c的步骤 一、NDK/JNI NDK JNI -那么为什么需要在Android上去使用C/C++代码? NDK开发 新建一个Native项目 设置调试模式 编写编译脚本文件CMakeLists.txt 加载C/C++库 (1)加载APK中的C/C++库文件 (2)加载外部的C/C++ so库 实现Java和C++互相调用 生成so文件 参考 Android开

    2024年02月11日
    浏览(38)
  • Android NDK开发详解之Android.mk探秘

    本页介绍了 ndk-build 所使用的 Android.mk 构建文件的语法。 概览 Android.mk 文件位于项目 jni/ 目录的子目录中,用于向构建系统描述源文件和共享库。它实际上是一个微小的 GNU makefile 片段,构建系统会将其解析一次或多次。Android.mk 文件用于定义 Application.mk、构建系统和环境变量

    2024年02月06日
    浏览(52)
  • [Android]JNI的基础知识

    目录 1.什么是JNI   2.配置JNI开发环境NDK 3.创建Native C++类型的项目 4. 了解CMakeLists.txt 文件 5.了解native-lib.cpp 文件 6.在 Android 的 MainActivity 中调用 native-lib.cpp 中实现的本地方法 1.什么是JNI         JNI(Java Native Interface)是一种允许Java代码与本地代码(如C或C++)进行交互的技

    2024年02月11日
    浏览(48)
  • android studio JNI开发

    一、JNI的作用: 1.使Java与本地其他类型语言(C、C++)交互; 2.在Java代码调用C、C++等语言的代码 或者 C、C++调用Java代码。 由于JAVA具有跨平台的特点,所以JAVA与本地代码的交互能力弱,采用JNI特性可以增强JAVA与本地代码的交互能力。 二、AndroidStudion中JNI的使用方法: 1、在

    2024年02月13日
    浏览(53)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包