JNI 基礎 - Android 共享記憶體的序列化過程

weixin_34208283發表於2018-09-18

Parcel原始碼分析

parcel的簡單使用

        Parcel parcel=Parcel.obtain();
        parcel.writeInt(12);
        parcel.writeInt(24);
        parcel.setDataPosition(0);
        int number1 = parcel.readInt();
        int number2 = parcel.readInt();
        Log.e(TAG, "number1: "+number1+",number2:"+number2 );

Parcel.obtain()原始碼分析

 public static Parcel obtain() {
       //從緩衝池中取,沒有就直接new
        final Parcel[] pool = sOwnedPool;
        synchronized (pool) {
            Parcel p;
            for (int i=0; i<POOL_SIZE; i++) {
                p = pool[i];
                if (p != null) {
                    pool[i] = null;
                    if (DEBUG_RECYCLE) {
                        p.mStack = new RuntimeException();
                    }
                    p.mReadWriteHelper = ReadWriteHelper.DEFAULT;
                    return p;
                }
            }
        }
        return new Parcel(0);
    }

最終會走到

    private void init(long nativePtr) {
        if (nativePtr != 0) {
            mNativePtr = nativePtr;
            mOwnsNativeParcelObject = false;
        } else {
            mNativePtr = nativeCreate();
            mOwnsNativeParcelObject = true;
        }
    }

nativeCreate原始碼分析

private static native long nativeCreate();

這時候我們去下載好的ndk原始碼.,找到,比如我的是放在D盤
D:\ndk\android-6.0.1_r1\android-6.0.1_r1\frameworks\base\core\jni下面的android_os_Parcel.cpp

  {"nativeCreate",              "()J", (void*)android_os_Parcel_create},

找到android_os_Parcel_create

static jlong android_os_Parcel_create(JNIEnv* env, jclass clazz)
{
     //實際就是new個Parcel物件,並轉換物件為long型別
    Parcel* parcel = new Parcel();
    return reinterpret_cast<jlong>(parcel);
}

parcel.writeInt()原始碼分析

  private static native void nativeWriteInt(long nativePtr, int val);

繼續看android_os_Parcel.cpp原始碼對nativeWriteInt分析

{"nativeWriteInt",            "(JI)V", (void*)android_os_Parcel_writeInt},
static void android_os_Parcel_writeInt(JNIEnv* env, jclass clazz, jlong nativePtr, jint val) {
     //long轉換成Parcel物件
    Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
    if (parcel != NULL) {
        const status_t err = parcel->writeInt32(val);
        if (err != NO_ERROR) {
            signalExceptionForError(env, clazz, err);
        }
    }
}

writeInt32這個原始碼需要到D:\ndk\android-6.0.1_r1\android-6.0.1_r1\frameworks\native\libs\binder下面的Parcel.cpp

status_t Parcel::writeInt32(int32_t val)
{
    return writeAligned(val);
}
template<class T>//相當於java中的泛型
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:
        *reinterpret_cast<T*>(mData+mDataPos) = val;//mData代表首地址,mDataPos代表指標偏移量
        return finishWrite(sizeof(val));
    }

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

finishWrite原始碼分析

status_t Parcel::finishWrite(size_t len)
{
    if (len > INT32_MAX) {
        // don't accept size_t values which may have come from an
        // inadvertent conversion from a negative int.
        return BAD_VALUE;
    }

    //printf("Finish write of %d\n", len);
    mDataPos += len;//指標向後+偏移量
    ALOGV("finishWrite Setting data pos of %p to %zu", this, mDataPos);
    if (mDataPos > mDataSize) {
        mDataSize = mDataPos;
        ALOGV("finishWrite Setting data size of %p to %zu", this, mDataSize);
    }
    //printf("New pos=%d, size=%d\n", mDataPos, mDataSize);
    return NO_ERROR;
}

parcel.readInt()原始碼分析

private static native int nativeReadInt(long nativePtr);
 {"nativeReadInt",             "(J)I", (void*)android_os_Parcel_readInt},
static jint android_os_Parcel_readInt(JNIEnv* env, jclass clazz, jlong nativePtr)
{
    Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
    if (parcel != NULL) {
        return parcel->readInt32();
    }
    return 0;
}

最終會走到

template<class T>
status_t Parcel::readAligned(T *pArg) const {
    COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE_UNSAFE(sizeof(T)) == sizeof(T));

    if ((mDataPos+sizeof(T)) <= mDataSize) {
        const void* data = mData+mDataPos;
         //指標向後移動
        mDataPos += sizeof(T);
       //取值
        *pArg =  *reinterpret_cast<const T*>(data);
        return NO_ERROR;
    } else {
        return NOT_ENOUGH_DATA;
    }
}

android自己動手實現記憶體共享

首先還是使用

        Parcel parcel=new Parcel();
        parcel.writeInt(12);
        parcel.writeInt(24);
        parcel.setDataPosition(0);
        int number1 = parcel.readInt();
        int number2 = parcel.readInt();
        Log.e(TAG, "number1: "+number1+",number2:"+number2 );

Parcel類的建立

public class Parcel {
    private static long mNativePtr;

    static {
        System.loadLibrary("native-lib");
        mNativePtr = nativeCreate();
    }
    public void writeInt(int value) {
        nativeWriteInt(mNativePtr, value);
    }

    public void setDataPosition(int pos) {
        nativeSetDataPosition(mNativePtr, pos);
    }
    public final int readInt() {
        return nativeReadInt(mNativePtr);
    }
    //c層構建一個Parcel.cpp物件,然後指標地址
    private static native long nativeCreate();
    //寫int
    private static native void nativeWriteInt(long mNativePtr, int value);
    //讀int
    private static native int nativeReadInt(long nativePtr);
    //寫完之後重新設定偏移位置
    private static native void nativeSetDataPosition(long nativePtr, int pos);
}

生成標頭檔案,這裡我就不闡述了,之前寫過了。生成標頭檔案後,我們就可以寫c程式碼了

#include <string>
#include <malloc.h>
#include <android/log.h>

#define TAG "JNI_TAG"
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG,__VA_ARGS__)
#include "com_peakmain_ndk_Parcel.h"
extern "C"
//c/c++  更加靈活,更加接近底層,操作的是一塊記憶體
class Parcel {
    char *mData;//char共享記憶體的首地址
    int mDataPos = 0;//=0,預設沒有賦值的話
public:
    Parcel() {
        mData = (char *) (malloc(1024));
    }

    void writeInt(jint value) {
        //賦值
        *reinterpret_cast<int *>(mData + mDataPos) = value;
        mDataPos += sizeof(int);
    }
    //設定當前指標的位置
    void setDataPosition(jint position) {
        mDataPos = position;
    }
    jint readInt() {
        int result = *reinterpret_cast<int*>(mData+mDataPos);//取當前地址的值
        mDataPos += sizeof(int);//指標偏移4
        return result;
    }
};

JNIEXPORT jlong JNICALL Java_com_peakmain_ndk_Parcel_nativeCreate
        (JNIEnv *env, jclass jclzz) {
    Parcel *parcel = new Parcel();
    return reinterpret_cast<jlong>(parcel);
}


JNIEXPORT void JNICALL Java_com_peakmain_ndk_Parcel_nativeWriteInt
        (JNIEnv *env, jclass clazz, jlong nativePtr, jint value) {
    Parcel *parcel = reinterpret_cast<Parcel *>(nativePtr);
    parcel->writeInt(value);

}

JNIEXPORT jint JNICALL Java_com_peakmain_ndk_Parcel_nativeReadInt
        (JNIEnv *env, jclass clazz, jlong nativePtr) {
    Parcel *parcel = reinterpret_cast<Parcel *>(nativePtr);
    return parcel->readInt();
}

JNIEXPORT void JNICALL Java_com_peakmain_ndk_Parcel_nativeSetDataPosition
        (JNIEnv *env, jclass jclazz, jlong nativePtr, jint value) {
    Parcel *parcel = (Parcel *) (nativePtr);
    parcel->setDataPosition(value);
}

相關文章