美文网首页
JNI-NDK(JNI静态缓存、异常处理、手写简单的Parcel

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

作者: 大虾啊啊啊 | 来源:发表于2022-08-30 11:47 被阅读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);
    
    }
    

    源码

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

    相关文章

      网友评论

          本文标题:JNI-NDK(JNI静态缓存、异常处理、手写简单的Parcel

          本文链接:https://www.haomeiwen.com/subject/ckylnrtx.html