美文网首页
NDK开发之路1-共享内存

NDK开发之路1-共享内存

作者: HardMan | 来源:发表于2021-08-13 20:01 被阅读0次

序列化方式

在Android开发过程中,需要对数据进行序列化操作时候 通常有两种方式
第一种 实现Serializable接口,第二种实现Parcelable接口,在writeToParcel方法中将数据写入,在createFromParcel读取数据,重新构造对象。

//第一种方式
public class Student implements Serializable 
//第二种方式
public class Student implements Parcelable {
    protected Student(Parcel in) {
    }

    public static final Creator<Student> CREATOR = new Creator<Student>() {
        @Override
        public Student createFromParcel(Parcel in) {
            return new Student(in);
        }

        @Override
        public Student[] newArray(int size) {
            return new Student[size];
        }
    };

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
    }
}

Serializabale接口方式 开发简单,只要实现接口就行。但通常为了性能考虑,会选择parcelable接口进行序列化处理。那么为什么Parcelable接口的性能要比Serializabale接口优秀呢,简单来说,Serializable接口是基于IO流的操作,而Parcelable是基于内存的操作。可想而知,设想一下 一个是从磁盘中读取数据,一个是从内存中读取数据,哪个更快就一目了然了。

接下来就通过源码分析Parcel共享内存的实现原理

先看Parcel的代码,有一个变量mNativePtr,它是由底层创建并返回给java层的一个指针,代表共享内存的首地址

public final class Parcel {
    private static final boolean DEBUG_RECYCLE = false;
    private static final boolean DEBUG_ARRAY_MAP = false;
    private static final String TAG = "Parcel";

    @SuppressWarnings({"UnusedDeclaration"})
    private long mNativePtr; // used by native code

创建过程 调了nativeCreate方法,我们跟进去看

    private void init(long nativePtr) {
        if (nativePtr != 0) {
            mNativePtr = nativePtr;
            mOwnsNativeParcelObject = false;
        } else {
            mNativePtr = nativeCreate();
            mOwnsNativeParcelObject = true;
        }
    }
  //进到了jni函数内部
    private static native long nativeCreate();

我们找到Android源码,在android_os_Parcel.cpp种可以找到方法的具体实现。这段代码也很简单,构造了一个Parcel指针,并将它返回出去

static jlong android_os_Parcel_create(JNIEnv* env, jclass clazz)
{
  Parcel* parcel = new Parcel();
  return reinterpret_cast<jlong>(parcel);
}


在java层中 我们调用writeInt方法 其实是将int值写入到共享内存中,那么看看具体是怎么操作

· image.png

我们可以看到 是将java传的指针对象强转成C层的指针对象,并通过该对象 去执行写入的操作。具体看一下Parcel.cpp的writeInt32的方法。在该方法中 将内存的指针 根据所传值的内存大小,将指针进行偏移,在偏移到指定位置后,再将数据写入该内存地址中。

status_t Parcel::writeInt32(int32_t val)
{
    return writeAligned(val);
}

status_t Parcel::writeAligned(T val) {
      COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE_UNSAFE(sizeof(T)) == sizeof(T));
    //如果内存大小还没超出阈值
      if ((mDataPos+sizeof(val)) <= mDataCapacity) {
           restart_write:
      //内存指针偏移 mdataPos位 并将偏移后的内存进行赋值
          *reinterpret_cast<T*>(mData+mDataPos) = val;
            return finishWrite(sizeof(val));
        }

    status_t err = growData(sizeof(val));
    if (err == NO_ERROR) goto restart_write;
    return err;
  }

在存完之后,我们其实很容易也难猜到,怎么取数据,只要从指定内存位置中去取出来就可以了。接下来看一下readInt32方法是怎么读取数据。
但在这之前,经过一系列的写入数据,指针的偏移量已经不再是0了,所以为了按顺序读取数据,需要将偏移量置为0,让指针从首地址开始,按照写入数据的顺序,依次读出。


image.png image.png image.png image.png

将指针对应的值返回出来。

通过对内存的操作,实现了数据存取 即序列化的过程
下一篇 手动实现共享内存NDK开发之路2-手动实现Parcel

相关文章

网友评论

      本文标题:NDK开发之路1-共享内存

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