博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JNI 基础 - Android 共享内存的序列化过程
阅读量:5906 次
发布时间:2019-06-19

本文共 6371 字,大约阅读时间需要 21 分钟。

1. 进程间的通信方式有哪些

2. binder 和 socket 通信的区别有哪些

3. Android 为什么在大部分场景下用 Binder 进行进程间通信

4. Serializable 和 Parcelable 之间的区别

5. Parcelable 序列化和反序列化的具体过程

不知道大家在面试中的过程中,有没有碰到上面类似的问题,我在腾讯和 oppo 面试的时候就碰到了,这些问题可能都比较简单。比如:Serializable 开销大,Parcelable 更加高效,但是若要问为什么是这样?恐怕就会有一小部分人答不上来了。今天我们就带大家来了解这些知识,既然学了一些 JNI 基础,那么我们自己从 native 层来实现这些。

之前讲 opencv 有说到,可以用纯 java 代码去写,在 opencv 中 native 层操作的是 Mat 数据矩阵,对应 java 层也有 Mat 这个类,我们看下 java 和 native 是怎么对应起来的。

// C++: class Mat//javadoc: Matpublic class Mat {    public final long nativeObj;    public Mat(long addr)    {        if (addr == 0)            throw new java.lang.UnsupportedOperationException("Native object address is NULL");        nativeObj = addr;    }    //    // C++: Mat::Mat()    //    // javadoc: Mat::Mat()    public Mat()    {        nativeObj = n_Mat();        return;    }    //    // C++: Mat::Mat(int rows, int cols, int type)    //    // javadoc: Mat::Mat(rows, cols, type)    public Mat(int rows, int cols, int type)    {        // 在 c++ 层创建一个对象,然后把指针地址 jlong 返回给 java 层        nativeObj = n_Mat(rows, cols, type);         return;    }    // C++: Mat::Mat()    private static native long n_Mat();    // C++: Mat::Mat(int rows, int cols, int type)    private static native long n_Mat(int rows, int cols, int type);}复制代码
JNIEXPORT jlong JNICALL Java_org_opencv_core_Mat_n_1Mat__DDI  (JNIEnv* env, jclass, jdouble size_width, jdouble size_height, jint type){    static const char method_name[] = "Mat::n_1Mat__DDI()";    try {        LOGD("%s", method_name);        Size size((int)size_width, (int)size_height);        return (jlong) new Mat( size, type );    } catch(const std::exception &e) {        throwJavaException(env, &e, method_name);    } catch (...) {        throwJavaException(env, 0, method_name);    }    return 0;}复制代码

上面的代码比较简单,当我们调用 Mat mat = new Mat(720,1280,CvType.CV_8UC4) ,这个时候会调用 native 层去创建 c++ 的 Mat 对象,然后将指针地址回调给 java 层,为什么要这么做?其实我们也能猜到,c++ 操作的是 native 层的 Mat 的对象,我们把创建好的对象指针给 java 层,是为了再次在 native 层操作的时候,可以根据指针地址找到对应 c++ 的 Mat 对象,然后就可以进行一些列的操作。

为什么讲到 opencv 去了?其实这次的重点就是,在 JNI 的开发过程中,native 层的对象怎么传给 java 层的对象进行保存和操作。接下来我们再来看一个例子 Android 共享内存的序列化过程,相信只要看过 Parcel 的源码,上面的所有问题都迎刃而解了。如果没有 frameworker 层的源码,请先去下载,千万不要下载阉割版的,里面要有 native 层的源码。

public final class Parcel {    // 保存的是 c++ 层的 Parcel.cpp 对象的指针地址    private long mNativePtr; // used by native code    private Parcel(long nativePtr) {        if (DEBUG_RECYCLE) {            mStack = new RuntimeException();        }        //Log.i(TAG, "Initializing obj=0x" + Integer.toHexString(obj), mStack);        init(nativePtr);    }    private void init(long nativePtr) {        if (nativePtr != 0) {            mNativePtr = nativePtr;            mOwnsNativeParcelObject = false;        } else {            mNativePtr = nativeCreate();            mOwnsNativeParcelObject = true;        }    }    /**     * Write an integer value into the parcel at the current dataPosition(),     * growing dataCapacity() if needed.     */    public final void writeInt(int val) {        nativeWriteInt(mNativePtr, val);    }    /**     * Read an integer value from the parcel at the current dataPosition().     */    public final int readInt() {        return nativeReadInt(mNativePtr);    }    // 创建 Parcel.cpp 返回 jlong    private static native long nativeCreate();    // 写入 int 数据    private static native void nativeWriteInt(long nativePtr, int val);    // 读 int 数据    private static native int nativeReadInt(long nativePtr);}复制代码
// 创建 Parcel ,返回指针地址给 java 层static jlong android_os_Parcel_create(JNIEnv* env, jclass clazz){    Parcel* parcel = new Parcel();    return reinterpret_cast
(parcel);}// 通过指针地址获取 c++ 层的对象,然后进行操作static void android_os_Parcel_writeInt(JNIEnv* env, jclass clazz, jlong nativePtr, jint val) { Parcel* parcel = reinterpret_cast
(nativePtr); if (parcel != NULL) { const status_t err = parcel->writeInt32(val); if (err != NO_ERROR) { signalExceptionForError(env, clazz, err); } }}复制代码
status_t Parcel::writeInt32(int32_t val){    return writeAligned(val);}template
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
(mData+mDataPos) = val; // 返回写入成功 return finishWrite(sizeof(val)); } // 返回错误 status_t err = growData(sizeof(val)); if (err == NO_ERROR) goto restart_write; return err;}int32_t Parcel::readInt32() const{ return readAligned
();}template
T Parcel::readAligned() const { T result; if (readAligned(&result) != NO_ERROR) { result = 0; } return result;}template
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
(data); return NO_ERROR; } else { return NOT_ENOUGH_DATA; }}复制代码

看到这里再来说共享内存,或者说 binder 驱动就会变得简单起来了,Parcel 其实就是在 native 层开辟了一块内存,然后按照一定的顺序往这块内存里面写数据。当我需要取数据的时候,我们按照原来写的顺序取出来就可以了。写的顺序和取的顺序必须保持一致,不然肯定会出错。那为什么不在 java 层做呢?请问在 java 层能做到这些效果吗?这也证实了其实 c 和 c++ 更加灵活,因为操作的是一块内存地址上的数据,想干嘛就可以干嘛。怎么回答上面的问题应该不用我说了,了解了原理那么在回答的时候可以自己做一些扩展。

既然是 JNI 基础部分,那么我们最好还是自己动手来敲一下。好比要了解 Retrofit 、OkHttp 和 RxJava 这些常用开源库,我们最好自己动手敲一下核心部分,就能更加加深印象。

var parcel = Parcel.obtain()  parcel.writeInt(12)  parcel.writeInt(24)  var number1 = parcel.readInt()  var number2 = parcel.readInt()  Log.e("TAG","number1 = $number1 , number2 = $number2")复制代码
class Parcel{    private var mNativePtr: Long = 0 // used by native code    init {        System.loadLibrary("native-lib")        mNativePtr = nativeCreate();    }    private external fun nativeCreate(): Long;    // 两个读写 int 的方法    fun writeInt(value: Int) {        nativeWriteInt(mNativePtr, value);    }    fun readInt(): Any {        return nativeReadInt(mNativePtr)    }    // 两个读写 native 方法    private external fun nativeWriteInt(mNativePtr: Long, value: Int);    private external fun nativeReadInt(nativePtr: Long): Int}复制代码

视频地址:https://pan.baidu.com/s/1H2d3mxxZJn7brGEYKNrCHw 视频密码:i81c

转载地址:http://mecpx.baihongyu.com/

你可能感兴趣的文章
flink watermark介绍
查看>>
Android Xutils 框架
查看>>
Sysbench 0.5版安装配置
查看>>
书摘—你不可不知的心理策略
查看>>
【博客话题】毕业——开始人生的艰苦历程
查看>>
Linux安装telnet
查看>>
sap scriptfom 多语言翻译
查看>>
黄聪:3分钟学会sessionStorage用法
查看>>
Entity Framework 全面教程详解(转)
查看>>
Windows上Python2.7安装Scrapy过程
查看>>
挖掘数据金矿 领军协同创新 曙光荣膺“2016大数据创新应用领袖企业”称号
查看>>
Fast通道获得Win10 Mobile Build 14977更新
查看>>
Firefox 跟踪保护技术将页面加载时间减少 44%
查看>>
java解析虾米音乐
查看>>
mysql 多行合并函数
查看>>
艾级计算机的发展与挑战
查看>>
RocketMQ事务消息实战
查看>>
手把手教你做出好看的文本输入框
查看>>
zabbix 3.2.7 (源码包)安装部署
查看>>
vsCode 快捷键、插件
查看>>