JNI-NDK(JNI静态缓存、异常处理、手写简单的Parcel)

1、C++中捕捉异常

抛什么类型的异常,就捕捉什么异常

#include 
using namespace std;
void exceptionTest() {
    throw "我报废了";
}
int main() {
    try {
        exceptionTest();
    }
    catch (const char *&msg) {
        cout << msg << endl;
    }
    return 0;
}
我报废了

2、JNI中异常处理

JNI中异常处理分为主动清除内部异常、将异常抛给Java、调用Java函数的时候,捕捉Java的异常

/**
* 异常测试
*/
extern "C"
JNIEXPORT void JNICALL
Java_com_hvm_vender_myapplication_MainActivity_exceptionTest(JNIEnv *env, jobject thiz) {
   if (j_calss == nullptr) {
       jclass temp = env->GetObjectClass(thiz);
       j_calss = static_cast(env->NewGlobalRef(temp));
   }

   LOGD("异常测试");
   //主动让他异常
//    jfieldID jfieldId = env->GetFieldID(j_calss, "Ddaada", "I");
//    //1、主动清除异常
//    jthrowable jthrowable = env->ExceptionOccurred(); // 监测本次执行,到底有没有异常   JNI函数里面代码有问题
//    if(jthrowable){
//        //有异常
//        //清除异常
//        LOGD("有异常");
//        env->ExceptionClear();
//    }
   //2、抛异常给Java
//    if (jthrowable) {
//        //有异常
//        //清除异常
//        LOGD("有异常");
//        env->ExceptionClear();
//        jclass clz = env->FindClass("java/lang/NoSuchFieldException");
//        //抛异常给Java
//        env->ThrowNew(clz, "NoSuchFieldException 是在是找不到 name8888啊,没有办法,抛给你了");
//    }
   //3、调用Java函数有异常
   jmethodID methodId = env->GetMethodID(j_calss, "callexceptionTest", "()V");
   env->CallVoidMethod(thiz, methodId);
   if(env->ExceptionCheck()){
       LOGD("Java有异常");
       //输出异常描述
       env->ExceptionDescribe();
       env->ExceptionClear();
   }
//清除完继续执行
   jfieldID jfieldId = env->GetFieldID(j_calss, "age", "I");
   jint ageInt = env->GetIntField(thiz, jfieldId);
   LOGD("ageInt = %d", ageInt);

}

3、静态缓存和全局引用

经测试发现jfieldID可以使用静态缓存,jclass需要使用全局引用,才能静态缓存

//jfieldID可以使用静态缓存,
jfieldID name = nullptr;
jfieldID age = nullptr;
//jclass需要使用全局引用,才能静态缓存
jclass j_calss = nullptr;
/**
 * 开启静态缓存
 */
extern "C"
JNIEXPORT void JNICALL
Java_com_hvm_vender_myapplication_MainActivity_staticCancheInit(JNIEnv *env, jobject thiz) {
    jclass temp = env->GetObjectClass(thiz);
    j_calss = static_cast(env->NewGlobalRef(temp));
    name = env->GetFieldID(j_calss, "name", "Ljava/lang/String;");
    age = env->GetFieldID(j_calss, "age", "I");

}
/**
 * 使用获取缓存
 */
extern "C"
JNIEXPORT void JNICALL
Java_com_hvm_vender_myapplication_MainActivity_staticCanche(JNIEnv *env, jobject thiz) {
    jstring jstring = static_cast<_jstring *>(env->GetObjectField(thiz, name));
    jint jint = env->GetIntField(thiz, age);
    const char *str = env->GetStringUTFChars(jstring, NULL);
    LOGD("age = %d", jint);
    LOGD("name = %s", str);

}
/**
 * 清除缓存
 */
extern "C"
JNIEXPORT void JNICALL
Java_com_hvm_vender_myapplication_MainActivity_staticCancheClear(JNIEnv *env, jobject thiz) {
    name = nullptr;
    age = nullptr;
    j_calss = nullptr;
}
/**
 * 全局引用测试
 */
extern "C"
JNIEXPORT void JNICALL
Java_com_hvm_vender_myapplication_MainActivity_global(JNIEnv *env, jobject thiz) {
    //多次调用看是否出现问题
    if (j_calss == nullptr) {
        jclass temp = env->GetObjectClass(thiz);
        j_calss = static_cast(env->NewGlobalRef(temp));
    }
    jfieldID jfieldId = env->GetFieldID(j_calss, "age", "I");
    jint ageInt = env->GetIntField(thiz, jfieldId);
    LOGD("ageInt = %d", ageInt);
}

4、手写简单的Parcel

Android中Parcel的原理分享

Parcel在Android中用于序列化和反序列化,也就是讲数据写进内存中和从内存中读取。对于Serializable来说使用比较麻烦,但是性能更高,因为Serializable需要经过IO操作,而Parcel则是写入到内存中。而Parcel的实现其实是在Native中的Parcel.cpp中,通过一个mData指针来处理读写数据。每写一次数据之后都要将指针位移对应数据类型的长度。写完之后将指针指向起始位置。而读数据也是一样,每次读数据也是通过指针位移的方式读数据。因此我们在Parcel读写数据的时候,读写的顺序一定是要一样的,否则可能会读出错误的数据。因为不同数据类型的长度不一样,指针的位移长度不一样。如下图所示:


image.png

Parcel的简单实现

  • Java代码
package com.hvm.vender.myapplication;

/**
 * author:weishixiong
 * date:2022/8/30 09:36
 * emial:[email protected]
 * desc:自定义Parcel
 */
public class MyParcel {
    //存储Native MyParcel对象
    private long mNativePtr = 0; // used by native code
    private static MyParcel instance = new MyParcel();

    public MyParcel() {
        mNativePtr = nativeCreate();
    }


    public static MyParcel getInstance() {
        return instance;
    }

    public void writeInt(int value) {
        nativeWriteInt(mNativePtr, value);

    }

    public int readInt() {
       return nativeReadInt(mNativePtr);
    }

    public void setQosPosition(int position) {
        nativeSetQosPosition(mNativePtr, position);

    }

    native long nativeCreate();

    /**
     * 写入数据
     *
     * @param value
     */
    native void nativeWriteInt(long mNativePtr, int value);

    /**
     * 读数据
     */
    native int nativeReadInt(long mNativePtr);

    /**
     * 设置指针位置
     *
     * @param position
     */
    native void nativeSetQosPosition(long mNativePtr, int position);
}

package com.hvm.vender.myapplication;

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

import com.hvm.vender.myapplication.databinding.ActivityMainBinding;

import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";

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

    private ActivityMainBinding binding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        MyParcel myParcel = new MyParcel();

        binding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());
        binding.btnWrite1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                myParcel.writeInt(100);
                myParcel.writeInt(200);
                myParcel.writeInt(600);
                myParcel.writeInt(700);
                //恢复指针
                myParcel.setQosPosition(0);

            }
        });
        binding.btnRead1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.d(TAG, "read: " + myParcel.readInt());
                Log.d(TAG, "read: " + myParcel.readInt());
                Log.d(TAG, "read: " + myParcel.readInt());
                Log.d(TAG, "read: " + myParcel.readInt());
                //恢复指针
                myParcel.setQosPosition(0);


            }
        });
     

  • c++代码
    头文件
//
// Created by DELL on 2022/8/30.
//

#ifndef MY_APPLICATION_MYPARCEL_H
#define MY_APPLICATION_MYPARCEL_H

#include 
#include 


class MyParcel {

public:
    //存储写入的数据,指向首地址
    char *mData = NULL;
    //指针的位置
    int qos = NULL;

    MyParcel();

    virtual ~MyParcel();

    void writeInt(int value);

    int readInt();

    void changePosition(int position);

    void setPosition(int position);
};

#endif //MY_APPLICATION_MYPARCEL_H

实现文件

//
// Created by DELL on 2022/8/30.
//

#include "MyParcel.h"

MyParcel::MyParcel() {
    this->mData = static_cast(malloc(1024));

}

MyParcel::~MyParcel() {
    if (this->mData) {
        free(this->mData);
        this->mData = NULL;
    }
    if (this->qos) {
        this->qos = 0;
    }
}

/**
 * 写入数据
 * @param value
 */
void MyParcel::writeInt(int value) {
    //转换成int*指针 再取值
    *reinterpret_cast(this->mData + this->qos) = value;
    //每次写一个数据,就移动一次指针
    changePosition(sizeof(int));

}

/**
 * 读数据
 * @return
 */
int MyParcel::readInt() {
    int value = *reinterpret_cast(this->mData + this->qos);
    //每次读一个数据,就移动一次指针
    changePosition(sizeof(int));
    return value;

}

/**
 * 移动指针
 * @param position
 */
void MyParcel::changePosition(int position) {
    this->qos = this->qos + position;
}

/**
 * 设置当前的指针位置
 * @param position
 */
void MyParcel::setPosition(int position) {
    this->qos = position;

}
  • JNI代码
/**
 * 初始化、讲Native对象强转成long  返回给java
 */
extern "C"
JNIEXPORT jlong JNICALL
Java_com_hvm_vender_myapplication_MyParcel_nativeCreate(JNIEnv *env, jobject thiz) {
    MyParcel *myParcel = new MyParcel;
    return reinterpret_cast(myParcel);
}
/**
 * 写数据
 */
extern "C"
JNIEXPORT void JNICALL
Java_com_hvm_vender_myapplication_MyParcel_nativeWriteInt(JNIEnv *env, jobject thiz,
                                                          jlong m_native_ptr, jint value) {
    MyParcel *myParcel = reinterpret_cast(m_native_ptr);
    myParcel->writeInt(value);

}
/**
 * 读数据
 */
extern "C"
JNIEXPORT jint JNICALL
Java_com_hvm_vender_myapplication_MyParcel_nativeReadInt(JNIEnv *env, jobject thiz,
                                                         jlong m_native_ptr) {
    MyParcel *myParcel = reinterpret_cast(m_native_ptr);
    return myParcel->readInt();

}
/**
 * 设置指针的位置
 */
extern "C"
JNIEXPORT void JNICALL
Java_com_hvm_vender_myapplication_MyParcel_nativeSetQosPosition(JNIEnv *env, jobject thiz,
                                                                jlong m_native_ptr, jint position) {
    MyParcel *myParcel = reinterpret_cast(m_native_ptr);
    myParcel->setPosition(position);

}

源码

https://gitee.com/daxiaa/jni-ndk.git

你可能感兴趣的:(JNI-NDK(JNI静态缓存、异常处理、手写简单的Parcel))