前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >[084]Binder答疑解惑(二)

[084]Binder答疑解惑(二)

作者头像
王小二
发布2023-09-11 14:09:03
1720
发布2023-09-11 14:09:03
举报

问题

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

一、什么是序列化

我们先来百度一下,百度的结果

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

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

代码语言:javascript
复制
class Person {
    String name;
    int age;
}

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

代码语言:javascript
复制
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。

代码语言:javascript
复制
class Person {
    String name;
    int age;
    IBinder action;//Binder对象
    File  file;//sdcard/1.txt
}

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

代码语言:javascript
复制
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

代码语言:javascript
复制
    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。

代码语言:javascript
复制
    //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://cloud.tencent.com/developer/article/1639790

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

这个部分我就不另外将了,可以参考我的另外一篇文章,https://cloud.tencent.com/developer/article/1639804,直接说结论。

经过Binder传递的File指向内核中同一file结构体,所以右边进程读写File和左边进程读写File是一样。

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

三、Binder作出的努力

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

代码语言:javascript
复制
    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,整个步骤分成三步:

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

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

尾巴

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

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2023-09-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 问题
  • 一、什么是序列化
  • 二、序列化后是同一个对象吗?
    • 2.1 IBinder用起来是不是和原始的IBinder一样
      • 2.2 File用起来是不是和原始的File一样
        • 三、Binder作出的努力
        • 总结
        • 尾巴
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档