Serializable接口

Serializable接口的实现

1
2
3
4
5
6
public class User implements Serializable {
private static final long serialVersionUID=519067123721295773L;//辅助序列化和反序列化过程
public int userId;
public String userName;
public boolean isMale;
}

序列化和反序列化的实现

1
2
3
4
5
6
7
8
9
10
11
12
//序列化过程
User user=new User(0,"jack",true);// 1. 创建一个User对象
ObjectOutputStream out=new ObjectOutputStream(// 2. 创建一个对象输出流
new FileOutputStream("cache.txt")//它包裹了一个文件输入流,指向 "cache.txt"
);
out.writeObject(user);// 3. 将user对象写入文件
out.close();// 4. 关闭流
//反序列化
ObjectInputStream in=new ObjectInputStream( // 1. 创建一个对象输入流
new FileOutputStream("cache.txt"));//它包裹了一个文件输出流,读取 "cache.txt"
User user=(User) in.readObject();// 2. 从文件中读取对象,并强制类型转换为User
in.close();// 3. 关闭流

serialVersionUID作用

  1. 序列化时:当一个 User 对象被序列化时(比如写入文件),Java运行时环境会把这个 serialVersionUID 的值也一并写入到字节流中。
  2. 反序列化时:当程序试图从文件中读取字节流并将其反序列化成一个 User 对象时,Java运行时环境会进行一个关键的检查:
  • 它会比较文件中的 serialVersionUID 和当前JVM中加载的 User.class 文件计算出的 serialVersionUID 是否一致。
  • 如果两者一致,就认为版本是兼容的,反序列化过程继续,对象被成功创建。
  • 如果两者不一致,JVM会认为“你拿旧版本的说明书来组装新版本的零件,可能会出问题”,于是直接抛出 InvalidClassException 异常,反序列化失败。

显示设定这个值就相当于你给这个类贴上了一个永久性的、写死的“版本标签”,减少反序列化失败的可能

Parcelable接口

用法实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
public class User implements Parcelable {
//基本字段
public int userId;
public String userName;
public boolean isMale;
//另一个实现了Parcelable接口的对象
public Book book;

public User(boolean isMale, int userId, String userName) {
this.isMale = isMale;
this.userId = userId;
this.userName = userName;
}

//这个方法在绝大多数情况下直接返回 0 即可,不需要关心它。它仅在对象中包含特殊类型的数据(如文件描述符)时才返回1。
@Override
public int describeContents() {
return 0;
}
//序列化过程
@Override
public void writeToParcel(Parcel out, int flags){
out.writeInt(userId);
out.writeString(userName);
//没有写入布尔值的方法所以将true转化为1存入,false转为0
out.writeInt(isMale?1:0);
//写入另一个 Parcelable 对象
out.writeParcelable(book, 0);
}
//反序列化
//反序列化入口
public static final Parcelable.Creator<User> CREATOR=new Parcelable.Creator<User>(){
@Override
public User createFromParcel(Parcel in) {
return new User(in);
}

@Override
public User[] newArray(int size) {
return new User[size];
}
};
//反序列化执行的地方
//读取顺序: 这里最关键的一点是,读取数据的顺序必须和 writeToParcel 方法中写入的顺序完全一致! 这是 Parcelable 最容易出错的地方。
private User(Parcel in){
userId=in.readInt();
userName=in.readString();
isMale=in.readInt()==1;
book=in.readParcelable(Thread.currentThread().getContextClassLoader());
}
}

Parcelable方法说明

Serializable和Parcelable的选取

Serializable是Java中的序列化接口,其使用起来简单但开销很大,序列化和反序列化过程需要大量的I/O操作。而Parcelable是Android中的序列化方式,因此更适合Android平台,他的缺点就是使用起来麻烦一些,但效率很高,这是Android推荐的序列化方式。Parcelable主要用在内存序列化上,将对象序列化到存储设备中或者将对象序列化后通过网络传输这两种情况推荐使用Serializable。

Binder

方法一

使用AIDL文件,SDK自动生成Binder类

  • 首先一定要在build.gradle.kts中声明

    1
    2
    3
    buildFeatures {
    aidl=true
    }
  • 接下来正常操作

方法二

自己创建Binder类

  • 声明一个aidl性质的接口,继承IInterface接口

    1
    2
    3
    4
    5
    6
    7
    8
    public interface IBookManager extends IInterface {
    static final String DESCRIPTOR="com.example.ipctest.IBookManager";
    static final int TRANSACTION_getBookList= IBinder.FIRST_CALL_TRANSACTION+0;
    static final int TRANSACTION_addBook= IBinder.FIRST_CALL_TRANSACTION+1;
    public List<Book> getBookList() throws RemoteException;
    public void addBook(Book book) throws RemoteException;

    }
  • 实现Stub类和Stub类中的Proxy代理类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    public class BookManagerImpl extends Binder implements IBookManager {
    public BookManagerImpl(){
    this.attachInterface(this,DESCRIPTOR);
    }
    public static IBookManager asInterface(IBinder obj){
    if((obj==null)){
    return null;
    }
    IInterface iin=obj.queryLocalInterface(DESCRIPTOR);
    if(((iin!=null)&&(iin instanceof IBookManager))){
    return ((IBookManager) iin);
    }
    return new BookManagerImpl.Proxy(obj);
    }
    @Override
    public IBinder asBinder() {
    return this;
    }

    @Override
    protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {
    switch (code){
    case INTERFACE_TRANSACTION:{
    reply.writeString(DESCRIPTOR);
    return true;
    }
    case TRANSACTION_getBookList:{
    data.enforceInterface(DESCRIPTOR);
    List<Book> result=this.getBookList();
    reply.writeNoException();
    reply.writeTypedList(result);
    return true;
    }
    case TRANSACTION_addBook:{
    data.enforceInterface(DESCRIPTOR);
    Book arg0;
    if((0!=data.readInt())){
    arg0=Book.CREATOR.createFromParcel(data);
    }else {
    arg0=null;
    }
    this.addBook(arg0);
    reply.writeNoException();
    return true;
    }
    }
    return super.onTransact(code, data, reply, flags);
    }

    @Override
    public List<Book> getBookList() throws RemoteException {
    //TODO待实现
    return null;
    }

    @Override
    public void addBook(Book book) throws RemoteException {
    //TODO待实现
    }
    private static class Proxy implements IBookManager{
    private IBinder mRemote;
    Proxy (IBinder remote){
    mRemote=remote;
    }
    @Override
    public IBinder asBinder() {
    return mRemote;
    }
    public java.lang.String getInterfaceDescriptor(){
    return DESCRIPTOR;
    }
    @Override
    public List<Book> getBookList() throws RemoteException {
    Parcel data =Parcel.obtain();
    Parcel reply = Parcel.obtain();
    List<Book> result;
    try{
    data.writeInterfaceToken(DESCRIPTOR);
    mRemote.transact(TRANSACTION_getBookList,data,reply,0);
    reply.readException();
    result=reply.createTypedArrayList(Book.CREATOR);
    }finally {
    reply.recycle();
    data.recycle();
    }
    return result;
    }

    @Override
    public void addBook(Book book) throws RemoteException {
    Parcel data =Parcel.obtain();
    Parcel reply = Parcel.obtain();
    try{
    data.writeInterfaceToken(DESCRIPTOR);
    if((book!=null)){
    data.writeInt(1);
    book.writeToParcel(data,0);
    }else {
    data.writeInt(0);
    }
    mRemote.transact(TRANSACTION_addBook,data,reply,0);
    reply.readException();
    }finally {
    reply.recycle();
    data.recycle();
    }
    }
    }
    }

    名词解释

    • DESCRIPTOR

      Binder的唯一标识,一般用Binder的类名表示

    • asInterface(android.os,IBinder obj)

      用于将服务端的Binder对象转换成客户端所需要的AIDL接口类型对象,这种转换过程区分进程,如果客户端和服务端位于同一进程,那么此方法返回的就是服务端的Stub对象本身,否返回的就是系统封装后的Stub.proxy对象

    • asBinder

      此方法用于返回当前的Binder对象

    • onTransact

    • 这个方法运行在服务端的Binder线池中

    • Proxy#getBookList

      这个方法运行在客户端,内部实现:首先创建该方法所需要的输入型Parcel对象_data,输出型Parcel对象 _reply和返回值对象List,然后把该方法的参数写入 _data中,接着调用transact方法来发起RPC(远程过程调用)请求,同时当前线程挂起;然后服务端的onTransact方法会被调用,直到RPC过程返回后,当前线程继续执行,并从reply中取出RPC过程的返回结果;最后返回 _reply中的数据

    • Proxy#addBook

      过程和上面的一样,没有返回值不需要从 _reply中取出返回值

linkToDeath和unlinkToDeath

为了解决Binder死亡对功能的影响提供了这俩方法(为了监听远程进程的死亡事件)

给Binder设置死亡代理:

  • 首先声明一个DeathRecipient对象,移除之前的binder代理并重新绑定远程服务

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    private IBinder.DeathRecipient mDeathRecipient=new  IBinder.DeathRecipient(){
    /**
    * 当远程服务的 Binder 死亡时(即其所在进程终止),这个方法会被回调。
    */
    @Override
    public void binderDied() {
    // 1. 安全检查,虽然理论上此时 mBookManager 不会为 null,但加上更健壮
    if(mBookManager==null) return;
    // 2. 解除死亡监听
    // 既然已经收到了死亡通知,就不再需要监听了,所以要解除注册,防止内存泄漏。
    mBookManager.asBinder.unlinkToDeath(mDeathRecipient,0);
    // 3. 清理无效的代理对象
    // 远程服务已经死亡,这个 mBookManager 代理对象也失效了,立即设为 null,
    // 防止应用的其他地方误用这个“已死亡”的对象,导致 DeadObjectException。
    mBookManager=null;
    // 4. 实现重连机制(关键步骤)
    // 这是最重要的一步,你可以在这里编写逻辑来重新绑定远程服务。
    // 比如可以启动一个新的 bindService 请求,这样当远程服务恢复后,你的客户端就能自动重新连接上。
    //TODO:这里重新绑定远程Service
    }
    };
  • 设置死亡代理

    1
    2
    3
    4
    //将底层的 IBinder 对象转换为客户端可直接调用的 AIDL 接口代理对象 (mService)。
    mService=IMessageBoxManager.Stub.asInterface(binder);
    //给这个 binder 连接注册一个“死亡讣告”接收者 (mDeathRecipient)。
    binder.linkToDeath(mDeathRecipient,0);

**注:**通过Binder的方法isBinderAlive也可以判断Binder是否死亡