JNI-NDK(JNI静态缓存、异常处理、手写简单的Parcel
2022-08-30 本文已影响0人
大虾啊啊啊
1、C++中捕捉异常
抛什么类型的异常,就捕捉什么异常
#include <iostream>
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<jclass>(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<jclass>(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<jclass>(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:weishixiong220328@credithc.com
* 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 <jni.h>
#include <malloc.h>
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<char *>(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<int *>(this->mData + this->qos) = value;
//每次写一个数据,就移动一次指针
changePosition(sizeof(int));
}
/**
* 读数据
* @return
*/
int MyParcel::readInt() {
int value = *reinterpret_cast<int *>(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<jlong>(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<MyParcel *>(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<MyParcel *>(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<MyParcel *>(m_native_ptr);
myParcel->setPosition(position);
}