美文网首页
[084]Binder答疑解惑(二)

[084]Binder答疑解惑(二)

作者: 王小二的技术栈 | 来源:发表于2023-09-09 13:03 被阅读0次

问题

能否深入讲解一下Binder中的序列化

一、什么是序列化

百度的结果

序列化 (Serialization)是将对象的状态[信息转换]为可以存储或传输的形式的过程。
在序列化期间,对象将其当前状态写入到临时或[持久性]存储区。
以后,可以通过从存储区中读取或反序列化对象的状态,重新创建该对象。

举个例子,看看这个Person对象如何序列化。

class Person {
    String name;
    int age;
}

序列化的时候将String和int写个某个内存区域,反序列化的时候读取这个内存区域,重新构造一个Person对象。

class Person implements Parcelable {
    String name;
    int age;

    public static final Parcelable.Creator CREATOR = new Creator() {
        @Override
        public Object createFromParcel(Parcel parcel) {
            Person person = new Person();
            person.name = parcel.readString();
            person.age = parcel.readInt();
            return person ;
        }

        @Override
        public Object[] newArray(int i) {
            return new Object[0];
        }
    };

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

    @Override
    public void writeToParcel(@NonNull Parcel parcel, int i) {
        parcel.writeString(name);
        parcel.writeInt(age);
    }

}

Parcelable就是按照上述的意思实现的。
这块内存区域就是Parcel对象对应的`内存区域。
writeToParcel就是序列化,代码的内部实现就会将name和age按照规则写到parcel中
createFromParcel就是反序列化,代码的内部实现就会从Parcel中读取name和age,然后重新构造Person类。

难道Binder的序列化就这么简单,当然还没有,Binder考虑的更多。

二、序列化后是同一个对象吗?

当然可以很明显的看到序列化后就不是一个对象。你操作反序列之后对象,其实在操作一个新的对象,和原始对象没有关系。
Binder的目标就是让对象在经过Binder接口传递之后,反序列化后的对象用起来和原始对象一样。
对Person类进行扩展,加入一个IBinder对象和一个File。

class Person {
    String name;
    int age;
    IBinder action;//Binder对象
    File  file;//sdcard/1.txt
}

经过Binder的传递这个对象之后,对端将会拿到一个对象

class Person {
    String name;
    int age;
    IBinder action;//BinderProxy对象
    File  file;//sdcard/1.txt
}

IBinder的对象从Binder对象变成BinderProxy对象,File对象变成了新的File都指向了同一个文件。

我们在来好好理解一下这段话:反序列化后的对象用起来和原始对象一样。

2.1 IBinder用起来是不是和原始的IBinder一样

当左边的进程调用Binder.transact最后调用的是Binder.onTransact

    public final boolean transact(int code, @NonNull Parcel data, @Nullable Parcel reply,
            int flags) throws RemoteException {
        if (false) Log.v("Binder", "Transact: " + code + " to " + this);

        if (data != null) {
            data.setDataPosition(0);
        }
        boolean r = onTransact(code, data, reply, flags);
        if (reply != null) {
            reply.setDataPosition(0);
        }
        return r;
    }

当右边的进程调用BinderProxy的transact接口会通过Binder驱动跨进程调用Binder.onTransact。

    //Entry point from android_util_Binder.cpp's onTransact.
    @UnsupportedAppUsage
    private boolean execTransact(int code, long dataObj, long replyObj,
            int flags) {
            return execTransactInternal(code, dataObj, replyObj, flags, callingUid);
    }

    private boolean execTransactInternal(int code, long dataObj, long replyObj, int flags,
            int callingUid) {
            res = onTransact(code, data, reply, flags);
        return res;
    }

因为BinderProxy和Binder都实现了IBinder接口,都实现了transact接口,所以将BinderProxy.transact和Binder.transact来说两者的效果是一样的。

思考
如果把右边的对象再传递给左边的进程,会发生什么,IBinder对象是Binder还是BinderProxy
答案:https://www.jianshu.com/p/740f1ee32fd1

2.2 File用起来是不是和原始的File一样

这个部分我就不另外将了,可以参考我的另外一篇文章,https://www.jianshu.com/p/0f300d539ff5,直接说结论。
经过Binder传递的File指向内核中同一file结构体,所以右边进程读写File和左边进程读写File是一样。

需要注意的是如果用ParcelFileDescriptor,因为在传递File之前重新打开了一次File,这样子虽然操作的是同一个文件,可能无法共享读写指针了。

三、Binder作出的努力

Binder为以下的对象类型实现了跨进程传递,但是本质上只实现了Binder对象,BinderProxy对象,和FD的传递,配合上IBinder对象的跨进程调用和Linux的一切皆文件的设计理念,基本达成了Parcelable对象在经过Binder接口传递后,用起来和原始的对象一样目标。

    enum {
        BINDER_TYPE_BINDER = B_PACK_CHARS('s', 'b', '*', B_TYPE_LARGE),//Binder
        BINDER_TYPE_WEAK_BINDER = B_PACK_CHARS('w', 'b', '*', B_TYPE_LARGE),//Binder
        BINDER_TYPE_HANDLE = B_PACK_CHARS('s', 'h', '*', B_TYPE_LARGE),//BinderProxy
        BINDER_TYPE_WEAK_HANDLE = B_PACK_CHARS('w', 'h', '*', B_TYPE_LARGE),//BinderProxy
        BINDER_TYPE_FD = B_PACK_CHARS('f', 'd', '*', B_TYPE_LARGE),//文件fd
        BINDER_TYPE_FDA = B_PACK_CHARS('f', 'd', 'a', B_TYPE_LARGE),//文件fd数组,但是似乎没看到用的地方
        BINDER_TYPE_PTR = B_PACK_CHARS('p', 't', '*', B_TYPE_LARGE),//这是什么类型,留一个思考题
      };

思考题
BINDER_TYPE_PTR目前只支持在hwbinder中,看看他的本质是什么,是否实现用起来和原始对象一样。
需要查看的代码
system/libhwbinder/Parcel.cpp中的writeBuffer
common/drivers/android/binder.c中case BINDER_TYPE_PTR的处理

总结

如果你想要真正了解Binder的序列化只需要去研究parcel.cpp和binder.c,整个步骤分成三步:

第一步:客户端使用parcel.cpp提供接口负责将对象A打包成binder驱动可以识别格式,并传递给Binder驱动
第二步:binder驱动按照自己支持的能力,将对象转化成对服务端可以访问的区域以及parcel.cpp可以识别的格式
第三步:服务端使用parcel.cpp提供接口将驱动传递过来的数据重新解析对象A`。

任何序列化和反序列化的机制,让我去研究,我都按照反序列化后的对象用起来和原始对象一样。要求去研究,然后去探究对应的跨进程,跨芯片通信机制是如何实现这个目标的。

尾巴

去了解设计者的设计理念和设计目标,有助于你真正的透过现象看到本质。

相关文章

  • 《答疑解惑二》

    今天听了老师的答疑课,又让自己有了新的收获和认知:人没有优缺点,人只有行为。家长对孩子的教育存在两种内在的看法和标...

  • 2019-01-11-福君老师答疑解惑后感

    福君老师答疑解惑后感 福君老师答疑解惑图1 福君老师答疑解惑图2 我的问题:人力资源学习训练营,我作为四组的小组上...

  • 答疑解惑

    问题1 我是在省会城市有5家餐饮店,每个月会有25万左右的流动资金,这部分资金会在年底的时候支付房租和其他的部分物...

  • 答疑解惑_

    伙伴们,早上没听到禹妃班主任的课,简要记录供没听到伙伴们学习: 困惑执行力不强,早上起不来的情况: 1早晨起来用舒...

  • 答疑解惑

    如果你的医保在医院用不了,可能的原因:1、你的信息未录入医保系统;2、你的医保系统(身份)信息错误;3、提...

  • 答疑解惑

    1,孩子在学校的学习已经很紧张了,没有时间读经典,您说读了经典对识字很好,可我家孩子读了经典并没有提高他的认字水平...

  • 答疑解惑

    农历:七月十二 星期一 天气:晴 气温:27~38 学习内容:1、含德之家YY语音频道学习。 2、含德之家微信群...

  • 答疑解惑

    燕教授有什么好?这是自己问过自己的问题,也是顾客一来会问的问题。 燕教授的好太多,首先我看中的是它的安全,哺乳期小...

  • 答疑解惑

    1.替代法:用鼓励代替惩。比如孩子上课不能认真听讲,爱做小动作,就可以跟孩子一起去买一个笔记本,告诉孩子,如果...

  • 答疑解惑

    今天老首长在群里发了个疑难解图,我冒昧试解一下,倘若有误请指正。 题图如下: 其实並非难解,只需步...

网友评论

      本文标题:[084]Binder答疑解惑(二)

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