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

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

1.数组

在jni中调用数组

c 复制代码
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代码

c 复制代码
#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代码

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

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代码

c 复制代码
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)
}

测试代码

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

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

c 复制代码
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

java 复制代码
 public native void testCreateObj();

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

jni

c 复制代码
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

java 复制代码
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

java 复制代码
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层的构造方法

c 复制代码
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);
}

测试

局部引用和全局引用

c 复制代码
//是局部变量,自动释放内存
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);
}
相关推荐
南东山人3 小时前
一文说清:C和C++混合编程
c语言·c++
吾日三省吾码3 小时前
JVM 性能调优
java
Estar.Lee4 小时前
查手机号归属地免费API接口教程
android·网络·后端·网络协议·tcp/ip·oneapi
温辉_xh4 小时前
uiautomator案例
android
弗拉唐4 小时前
springBoot,mp,ssm整合案例
java·spring boot·mybatis
oi775 小时前
使用itextpdf进行pdf模版填充中文文本时部分字不显示问题
java·服务器
工业甲酰苯胺5 小时前
MySQL 主从复制之多线程复制
android·mysql·adb
少说多做3435 小时前
Android 不同情况下使用 runOnUiThread
android·java