Android开发,JNI,NDK,C++层操作java的对象实践

Android开发,JNI,NDK,C++层操作java的对象实践

1.数组

在jni中调用数组

extern "C"
JNIEXPORT void JNICALL
Java_com_example_myapplication_MainActivity_testArr(JNIEnv *env, jobject thiz, jint a, jstring s,
                                                    jintArray ints, jobjectArray strings) {
    int cnt = a;
    LOGD("cnt == %d\n", cnt);
    const char *test = env->GetStringUTFChars(s, NULL);
    LOGD("------ %s\n", test);
    //回收
    env->ReleaseStringUTFChars(s, test);
}

在这里插入图片描述

运行下

在这里插入图片描述

遍历int数组在jni中

在这里插入图片描述

jni修改数组底层,JNI_OK标记

在这里插入图片描述

在这里插入图片描述

java层随之改变

在这里插入图片描述

2.引用数据类型数组

字符串类型数组在jni中

在这里插入图片描述

修改java层

在这里插入图片描述

jni代码

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

#include <android/log.h>

#define TAG "testLog"

#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__);

extern "C" JNIEXPORT jstring JNICALL
Java_com_example_myapplication_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}
//代码采用C语言编译
extern "C"
//关键字jni,
JNIEXPORT
//java的native方法的返回值
void
//关键字
JNICALL
//java包名, 类名,方法名
//jobject1是MainActivity的this实例
//一个是类调用,一个是对象调用,
Java_com_example_myapplication_MainActivity_test(JNIEnv *env, jobject jobject1) {


}extern "C"
JNIEXPORT void JNICALL
Java_com_example_myapplication_MainActivity_doubleFromJNI(JNIEnv *env, jobject thiz) {
    jclass test = env->GetObjectClass(thiz);
//    jfieldID num = env->GetFieldID(test, "num", "D");
//
//    env->SetDoubleField(thiz,num, 444.5);


    jfieldID num2 = env->GetFieldID(test, "num2", "D");
    env->SetDoubleField(thiz, num2, 5555.5);

    LOGD("SSSSSSSSSSSSSS %d\n", 1312);
}extern "C"
JNIEXPORT void JNICALL
Java_com_example_myapplication_MainActivity_testAddNum(JNIEnv *env, jobject thiz) {
    /*jclass jclass1 = env->GetObjectClass(thiz);
    jmethodID add = env->GetMethodID(jclass1, "addNum", "(II)I");
    int jint = env->CallIntMethod(thiz, add, 2, 3);
    LOGD("============%d\n", jint);*/
    jclass jclass1 = env->GetObjectClass(thiz);
    jmethodID jmethodId = env->GetMethodID(jclass1, "getTestString",
                                           "(Ljava/lang/String;I)Ljava/lang/String;");
    jstring va = env->NewStringUTF("22222");
    jstring rrr = (jstring) env->CallObjectMethod(thiz, jmethodId, va, 222);
    const char *string = env->GetStringUTFChars(rrr, NULL);
    LOGD("==============%s\n", string);
}extern "C"
JNIEXPORT void JNICALL
Java_com_example_myapplication_MainActivity_testArr(JNIEnv *env, jobject thiz, jint a, jstring s,
                                                    jintArray ints, jobjectArray strings) {
    int cnt = a;
    LOGD("cnt == %d\n", cnt);
    const char *test = env->GetStringUTFChars(s, NULL);
    LOGD("------ %s\n", test);
    //回收
    env->ReleaseStringUTFChars(s, test);
    //获取java传下来的数组
    jint *jint1 = env->GetIntArrayElements(ints, NULL);

    //计算长度
    int len = env->GetArrayLength(ints);
    for (int i = 0; i < len; ++i) {
        LOGD("C++ %d\n", *(jint1 + i));
        *(jint1 + i) = (i + 10111);
    }
    //释放素组内存,jni下面的修改传递给java层
    env->ReleaseIntArrayElements(ints, jint1, JNI_OK);

    //
    int strslen = env->GetArrayLength(strings);
    for (int i = 0; i < strslen; ++i) {
        jstring jstring1 = (jstring) env->GetObjectArrayElement(strings, i);
        const char *string = env->GetStringUTFChars(jstring1, NULL);
        LOGD("C++============ %s\n", string);
        //释放内存
        env->ReleaseStringUTFChars(jstring1,string);
        //修改java层
        jstring updateValue = env->NewStringUTF("qiuqiuqiuqiuq");
        env->SetObjectArrayElement(strings, i, updateValue);

        jstring jstring12 = (jstring) env->GetObjectArrayElement(strings, i);
        const char *string2 = env->GetStringUTFChars(jstring12, NULL);
        LOGD("C++============2222 %s\n", string2);


        //释放内存,jni函数结束会释放变量的内存
        env->ReleaseStringUTFChars(jstring12,string2);
    }

}

java代码

package com.example.myapplication;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    // Used to load the 'native-lib' library on application startup.
    static {
//        System.loadLibrary("native-lib");
        System.loadLibrary("native-lib");
    }

    private static final String TAG = "MainActivity";
    private String test = "test";

    public native String stringFromJNI();

    public native void test();

    private double num = 1;
    private double num2 = 2;

    public native void doubleFromJNI();

    //jni调用它
    public int addNum(int a, int b) {
        Log.e(TAG, "addNum: ");
        return a + b;
    }

    //
    public native void testAddNum();

    public String getTestString(String s, int a) {
        Log.e(TAG, "getTestString: ");
        return s;
    }

    public native void testArr(int a, String s, int[] ints, String[] strings);

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

        // Example of a call to a native method
        TextView tv = findViewById(R.id.sample_text);
        tv.setText(stringFromJNI());
        Log.e(TAG, "onCreate: ");
        test();
        Log.e(TAG, "onCreate: 修改前 " + num2);
        doubleFromJNI();
        Log.e(TAG, "onCreate: 修改后 " + num2);
        testAddNum();
    }

    public void test1(View view) {
        int[] ints = new int[]{1, 2, 3};
        String[] strings = new String[]{"123", "1"};
        testArr(111, "222", ints, strings);
        for (int anInt : ints) {
            Log.e(TAG, "test1: anInt" + anInt);
        }
        for (String string : strings) {
            Log.e(TAG, "test1: string" + string);
        }
    }

    /**
     * A native method that is implemented by the 'native-lib' native library,
     * which is packaged with this application.
     */
}

3.java对象,jni层创建java对象

java

package com.example.myapplication;

import androidx.annotation.NonNull;

public class Test {
    private static final String TAG = "Test";
    public String name;
    public int age;
    public Test() {
    }

    public Test(String a, int b) {
        this.name = a;
        this.age = b;
    }

    public static String getTAG() {
        return TAG;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

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

jni代码

extern "C"
JNIEXPORT void JNICALL
Java_com_example_myapplication_MainActivity_testObject(JNIEnv *env, jobject thiz,
                                                       jobject test,
                                                       jstring cstring) {
    const char *string = env->GetStringUTFChars(cstring, NULL);
    LOGD("string ============== %s\n", string);
    //回收ziy
    env->ReleaseStringUTFChars(cstring, string);

    jclass jclass1 = env->FindClass("com/example/myapplication/Test");
    //调用对象的toString
    jmethodID toStringId = env->GetMethodID(jclass1, "toString", "()Ljava/lang/String;");

    jstring jstring1 = (jstring) env->CallObjectMethod(test, toStringId);

    const char *toStringJstring1 = env->GetStringUTFChars(jstring1, NULL);
    LOGD("toStringJstring1 ==== %s\n", toStringJstring1)
}

在这里插入图片描述

测试代码

  public void test2(View view) {
        Test test = new Test();
        test.name = "adasd";
        test.age = 111;
        testObject(test, "hello");
    }

jni中调用对象的get,set方法

extern "C"
JNIEXPORT void JNICALL
Java_com_example_myapplication_MainActivity_testObject(JNIEnv *env, jobject thiz,
                                                       jobject test,
                                                       jstring cstring) {
    const char *string = env->GetStringUTFChars(cstring, NULL);
    LOGD("string ============== %s\n", string);
    //回收ziy
    env->ReleaseStringUTFChars(cstring, string);

    jclass jclass1 = env->FindClass("com/example/myapplication/Test");
    //调用对象的toString
    jmethodID toStringId = env->GetMethodID(jclass1, "toString", "()Ljava/lang/String;");

    jstring jstring1 = (jstring) env->CallObjectMethod(test, toStringId);

    const char *toStringJstring1 = env->GetStringUTFChars(jstring1, NULL);
    LOGD("toStringJstring1 ==== %s\n", toStringJstring1)
    //释放内存
    env->ReleaseStringUTFChars(jstring1, toStringJstring1);
    env->DeleteLocalRef(jclass1);

    jclass tesObj = env->FindClass("com/example/myapplication/Test");

    jmethodID setName = env->GetMethodID(tesObj, "setName", "(Ljava/lang/String;)V");
    jstring jstring2 = env->NewStringUTF("1123131");
    //调用对象的set方法
    env->CallVoidMethod(test, setName, jstring2);


    //调用get方法
    jclass tesObj1 = env->FindClass("com/example/myapplication/Test");

    jmethodID getName = env->GetMethodID(tesObj, "getName", "()Ljava/lang/String;");
    //调用对象的get方法
    jstring jstring3 = (jstring) env->CallObjectMethod(test, getName);
    const char *name = env->GetStringUTFChars(jstring3, NULL);
    LOGD("get name ==== %s\n", name)

}

运行

在这里插入图片描述

可以看出get,set方法都被调用,并且get到我们设置的值

jni创建对象

java

 public native void testCreateObj();

    public void test3(View view) {
        testCreateObj();
    }

jni

extern "C"
JNIEXPORT void JNICALL
Java_com_example_myapplication_MainActivity_testCreateObj(JNIEnv *env, jobject thiz) {

    jclass jclass1 = env->FindClass("com/example/myapplication/Test2");
    jclass jclass2 = env->FindClass("com/example/myapplication/Test");
    //分配对象,不会调用构造方法
    jobject jobject1 = env->AllocObject(jclass1);
    jobject jobject2 = env->AllocObject(jclass2);

    //实例化对象
//    env->NewObject(jclass1);
        jmethodID jmethodId = env->GetMethodID(jclass1,"setTest",
                                               "(Lcom/example/myapplication/Test;)V");


    env->CallVoidMethod(jobject1,jmethodId,jobject2);


    jmethodID setName = env->GetMethodID(jclass2, "setName", "(Ljava/lang/String;)V");
    jstring jstring2 = env->NewStringUTF("2222222");
    //调用对象的set方法
    env->CallVoidMethod(jobject2, setName, jstring2);
}

运行在jni创建的对象,

在这里插入图片描述

4.全局对象引用

测试代码Test

package com.example.myapplication;

import android.util.Log;

import androidx.annotation.NonNull;

public class Test {
    private static final String TAG = "Test";
    public String name;
    public int age;

    public Test() {
        Log.e(TAG, "Test: ");
    }

    public Test(int num) {
        Log.e(TAG, "Test: " + num);
    }

    public Test(int num1, int num2) {
        Log.e(TAG, "Test: " + num1 + num2);

    }

    public static void testStatic(String info) {

    }

    public Test(String a, int b) {
        this.name = a;
        this.age = b;
    }

    public static String getTAG() {
        return TAG;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

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

MainActivity

public native void testGlobalReferences();

    public native void deleteGlobalReferences();

    @Override
    protected void onDestroy() {
        super.onDestroy();
        deleteGlobalReferences();
    }

    public void test4(View view) {
        testGlobalReferences();
    }

    public void test5(View view) {
        deleteGlobalReferences();
    }

jni调用java层的构造方法

extern "C"
JNIEXPORT void JNICALL
Java_com_example_myapplication_MainActivity_testGlobalReferences(JNIEnv *env, jobject thiz) {

    if (!test) {
        test = env->FindClass("com/example/myapplication/Test");

    }
    //init 构造方法。()调用无参构造方法
    jmethodID jmethodId = env->GetMethodID(test, "<init>", "()V");
    jobject jobject1 = env->NewObject(test, jmethodId);
    //调用一个参数构造方法
    jmethodID jmethodId2 = env->GetMethodID(test, "<init>", "(I)V");
    jobject jobject2 = env->NewObject(test, jmethodId2, 1);
    //调用两个参数构造方法
    jmethodID jmethodId3 = env->GetMethodID(test, "<init>", "(II)V");
    jobject jobject3 = env->NewObject(test, jmethodId3, 1, 2);
}

测试

在这里插入图片描述

局部引用和全局引用

//是局部变量,自动释放内存
jclass test = nullptr;
extern "C"
JNIEXPORT void JNICALL
Java_com_example_myapplication_MainActivity_deleteGlobalReferences(JNIEnv *env, jobject thiz) {
    if (!test) {
        env->DeleteGlobalRef(test);
        test = nullptr;
    }
}
extern "C"
JNIEXPORT void JNICALL
Java_com_example_myapplication_MainActivity_testGlobalReferences(JNIEnv *env, jobject thiz) {

    if (!test) {
//        test = env->FindClass("com/example/myapplication/Test");

        jclass jclass1 = env->FindClass("com/example/myapplication/Test");
        //全局变量
        test = (jclass) env->NewGlobalRef((jobject) jclass1);
        env->DeleteLocalRef(jclass1);
    }
    //init 构造方法。()调用无参构造方法
    jmethodID jmethodId = env->GetMethodID(test, "<init>", "()V");
    jobject jobject1 = env->NewObject(test, jmethodId);
    //调用一个参数构造方法
    jmethodID jmethodId2 = env->GetMethodID(test, "<init>", "(I)V");
    jobject jobject2 = env->NewObject(test, jmethodId2, 1);
    //调用两个参数构造方法
    jmethodID jmethodId3 = env->GetMethodID(test, "<init>", "(II)V");
    jobject jobject3 = env->NewObject(test, jmethodId3, 1, 2);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android NDK中,我们可以使用JNIJava Native Interface)来实现C/C++代码和Java代码的相互调用。 下面是一个简单的例子,展示了如何在NDK中调用Java的方法。 首先,在Java中创建一个类,并在其中声明一个需要被C/C++回调的方法: ```java public class MyCallback { public void processData(byte[] data) { // 处理数据的逻辑 } } ``` 然后,在C/C++代码中,我们需要使用JNI来获取Java的MyCallback对象,并调用其processData方法。具体步骤如下: 1. 首先,需要在C/C++代码中引入JNI头文件: ```c++ #include <jni.h> ``` 2. 获取Java的MyCallback对象: ```c++ JNIEnv* env; JavaVM* jvm; // 获取当前线程的JNIEnv指针 jvm->AttachCurrentThread(&env, NULL); // 获取MyCallback类 jclass myCallbackClass = env->FindClass("com/example/MyCallback"); // 获取MyCallback对象 jmethodID constructor = env->GetMethodID(myCallbackClass, "<init>", "()V"); jobject myCallbackObj = env->NewObject(myCallbackClass, constructor); ``` 3. 调用MyCallback对象的processData方法: ```c++ // 获取processData方法的ID jmethodID processDataMethod = env->GetMethodID(myCallbackClass, "processData", "([B)V"); // 构造byte[]对象 jbyteArray data = env->NewByteArray(size); env->SetByteArrayRegion(data, 0, size, (jbyte*)buf); // 调用processData方法 env->CallVoidMethod(myCallbackObj, processDataMethod, data); ``` 最后,记得在C/C++代码中释放JNI相关资源: ```c++ jvm->DetachCurrentThread(); env->DeleteLocalRef(myCallbackClass); env->DeleteLocalRef(myCallbackObj); env->DeleteLocalRef(data); ``` 以上就是在NDK中实现线程回调Java方法的基本步骤。需要注意的是,在调用Java方法时,需要使用JNIEnv指针。此外,如果在多线程环境下操作JNI,需要使用jvm->AttachCurrentThread()方法获取当前线程的JNIEnv指针。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值