Android 中 IPC 机制 (二)

731 阅读6分钟
原文链接: blog.csdn.net

1.IPC基础概念介绍

Serializable和Parcelable接口可以完成对象的序列化过程,当我们需要通过Intent和Binder传输数据时就需要使用Serializable或者Parcelable。还有的时候我们需要把对象持久化到存储设备上或者通过网络传输给其他客户端也需要使用Serializable或者Parcelable。

序列化:把对象转换为字节序列的过程称为对象的序列化

反序列化:把字节序列恢复为对象的过程称为对象的反序列化

(1)Serializable接口

Serializable是Java提供的一个序列化接口,它是一个空接口,为对象提供标准的序列化和反序列化操作

package com.zhoujian.ipc.bean;

import java.io.Serializable;

/**
 * Created by zhoujian on 2017/2/24.
 */

public class User implements Serializable{

    private static final long serialVersionUID = 1L;

    private int userId;

    private String userName;

    private boolean isMale;


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

    public boolean isMale() {
        return isMale;
    }

    public void setMale(boolean male) {
        isMale = male;
    }

    public int getUserId() {
        return userId;
    }

    public void setUserId(int userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    @Override
    public String toString() {
        return "User{" +
                "isMale=" + isMale +
                ", userId=" + userId +
                ", userName='" + userName + '\'' +
                '}';
    }
}

通过Serializable方式来实现对象的序列化,非常简单,所有的工作都被系统自动完成了。

序列化过程

  private void serilize()
    {
       new Thread(){
           @Override
           public void run() {
               try
               {
                   //对象序列化过程
                   User user= new User(true,1,"zhoujian");
                   if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
                       File sdCardDir = Environment.getExternalStorageDirectory();//获取SDCard目录
                       File saveFile = new File(sdCardDir, "my.txt");
                       FileOutputStream outStream = new FileOutputStream(saveFile);
                       ObjectOutputStream out = new ObjectOutputStream(outStream);
                       out.writeObject(user);
                       Log.d(TAG, "对象序列化成功!");
                       out.close();
                   }
               }
               catch (IOException e)
               {
                   e.printStackTrace();
               }
           }
       }.start();
    }

反序列化过程


    private void nonserilize()
    {

        new Thread(){
            @Override
            public void run()
            {
                try
                {
                    if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
                        File sdCardDir = Environment.getExternalStorageDirectory();//获取SDCard目录
                        File saveFile = new File(sdCardDir, "my.txt");
                        FileInputStream  inStream = new FileInputStream(saveFile);
                        ObjectInputStream in = new ObjectInputStream(inStream);
                        User user = (User)in.readObject();
                        in.close();
                        Log.d(TAG, "对象反序列化成功!");
                        Log.d(TAG, "user=="+user.toString());
                    }
                }
                catch (Exception e)
                {
                    e.printStackTrace();
                }
            }
        }.start();
    }

运行的结果为:

这里写图片描述

注意需要加权限


 <!-- 在SDCard中创建与删除文件权限 -->                                                               
 <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>           
 <!-- 往SDCard写入数据权限 -->                                                                   
 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>              

serialVersionUID有什么作用?

serialVersionUID是用来辅助序列化和反序列化的,序列化后的数据中的serialVersionUID只有和当前类的serialVersionUID相同才能够正常的被反序列化。

serialVersionUID工作机制:序列化的时候系统会把当前类的serialVersionUID写入序列化文件中,当反序列化的时候系统会去检测文件中的serialVersionUID和当前类的serialVersionUID是否一致,如果一致就说明序列化类的版本和当前类的版本是相同的,这个时候可以成功反序列化。否则不可以。

给serialVersionUID指定为1L或者当前类结构生成的hash值,这两者没有本质区别

注意:静态成员变量属于类不属于对象,所以不参与序列化过程;用transient关键字标记的成员变量不参与序列化过程

(2)Parcelable接口

package com.zhoujian.android.bean;

import android.os.Parcel;
import android.os.Parcelable;

/**
 * Created by zhoujian on 2016/11/28.
 */

public class User implements Parcelable
{

    public int userId;

    public String userName;

    public boolean isMalel;


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

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(this.userId);
        dest.writeString(this.userName);
        dest.writeByte(this.isMalel ? (byte) 1 : (byte) 0);
    }

    public User() {
    }

    protected User(Parcel in) {
        this.userId = in.readInt();
        this.userName = in.readString();
        this.isMalel = in.readByte() != 0;
    }

    public static final Creator<User> CREATOR = new Creator<User>() {
        @Override
        public User createFromParcel(Parcel source) {
            return new User(source);
        }

        @Override
        public User[] newArray(int size) {
            return new User[size];
        }
    };
}

Parcelable接口与Serializable接口都能实现序列化并且都可用于Intent间传递数据。

Serializable是Java中的序列化接口,使用简单但是开销大,学序列化和反序列化过程需要大量I/O操作。
Parcelable是Android中的序列化方式,效率高,但是使用比较麻烦。

3.Binder

Binder是Android中的一个类,它实现了IBinder接口。

从IPC角度来说,Binder是Android中的一种跨进程的通讯方式。

从Android Framwork角度来说,Binder是ServiceManager连接各种Manager和ManagerService的桥梁

从Android应用层来说,Binder是客户端和服务端进行通信的媒介。

Android开发中,Binder主要用于Service中,包括AIDL和Messenger,其中普通Service中的Binder不涉及进程间通信,所以较为简单,无法触及Binder的核心,而Messenger的底层是AIDL,所以选择AIDL来分析Binder的工作机制

这里写图片描述

Book.java

package com.zhoujian.ipc.aidl;

import android.os.Parcel;
import android.os.Parcelable;

/**
 * Created by zhoujian on 2017/2/24.
 */

public class Book implements Parcelable
{
    public int bookId;
    public String bookName;

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

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(this.bookId);
        dest.writeString(this.bookName);
    }

    public Book(int bookId,String bookName)
    {
        this.bookId = bookId;

        this.bookName = bookName;

    }

    protected Book(Parcel in) {
        this.bookId = in.readInt();
        this.bookName = in.readString();
    }

    public static final Creator<Book> CREATOR = new Creator<Book>() {
        @Override
        public Book createFromParcel(Parcel source) {
            return new Book(source);
        }

        @Override
        public Book[] newArray(int size) {
            return new Book[size];
        }
    };
}

Book.aidl

// Book.aidl
package com.zhoujian.ipc.aidl;

// Declare any non-default types here with import statements

parcelable Book;

IBookManager.aidl

// IBookManager.aidl
package com.zhoujian.ipc.aidl;

import com.zhoujian.ipc.aidl.Book;

// Declare any non-default types here with import statements

interface IBookManager {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */

     //从远处服务端获取图书列表
     List<Book> getBookList();

     //往图书列表中添加一本书
     void addBook(in Book book);
}

Book.java是一个表示图书信息的类,实现了Parcelable接口。

Book.aidl是Book类在AIDL中的声明。

IBookManager.aidl是我们定义的一个接口,里面有两个方法:getBookList和addBook

注意:尽管Book类已经和IBookManager位于相同的包中,但是在IBookManager中仍然要导入Book类

在As中系统为我们生成一个IBookManager.java类

这里写图片描述

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: /Users/zhoujian/AndroidStudioProjects/IPCDemo/app/src/main/aidl/com/zhoujian/ipc/aidl/IBookManager.aidl
 */
package com.zhoujian.ipc.aidl;
// Declare any non-default types here with import statements

public interface IBookManager extends android.os.IInterface {
    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements com.zhoujian.ipc.aidl.IBookManager {

        //Binder的唯一标识
        private static final java.lang.String DESCRIPTOR = "com.zhoujian.ipc.aidl.IBookManager";

        /**
         * Construct the stub at attach it to the interface.
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an com.zhoujian.ipc.aidl.IBookManager interface,
         * generating a proxy if needed.
         */

        //asInterface:用于将服务端Binder对象转化成客户端的AIDL接口类型的对象,这种转换是区分进程的,如果客户端和服务端位于同一个进程,那么此方法返回的就是

        //服务端的Stub对象本身,否则返回的是系统封装的Stub.Proxy对象
        public static com.zhoujian.ipc.aidl.IBookManager asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.zhoujian.ipc.aidl.IBookManager))) {
                return ((com.zhoujian.ipc.aidl.IBookManager) iin);
            }
            return new com.zhoujian.ipc.aidl.IBookManager.Stub.Proxy(obj);
        }

        /**
         * 返回当前Binder对象
         * @return
         */
        @Override
        public android.os.IBinder asBinder() {
            return this;
        }

        /**
         * onTransact方法运行在服务端中的Binder线程池中,当客户端发起跨进程请求时,远程请求会通过系统底层封装后交由此方法来处理
         *
         * 服务端通过code可以确定客户端请求的目标方法是什么,接着从data中取出目标方法需要的参数,然后执行目标方法。目标方法执行完毕后
         *
         * 就向reply中写入返回值,如果此方法返回false,客户端请求就会失败
         * @param code
         * @param data
         * @param reply
         * @param flags
         * @return
         * @throws android.os.RemoteException
         */

        @Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_getBookList: {
                    data.enforceInterface(DESCRIPTOR);
                    java.util.List<com.zhoujian.ipc.aidl.Book> _result = this.getBookList();
                    reply.writeNoException();
                    reply.writeTypedList(_result);
                    return true;
                }
                case TRANSACTION_addBook: {
                    data.enforceInterface(DESCRIPTOR);
                    com.zhoujian.ipc.aidl.Book _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.zhoujian.ipc.aidl.Book.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    this.addBook(_arg0);
                    reply.writeNoException();
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }




        /**
         *  Proxy#getBookList
         *
         *  这个方法运行在客户端
         *
         *
         */

        private static class Proxy implements com.zhoujian.ipc.aidl.IBookManager {
            private android.os.IBinder mRemote;

            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }

            @Override
            public android.os.IBinder asBinder() {
                return mRemote;
            }

            public java.lang.String getInterfaceDescriptor() {
                return DESCRIPTOR;
            }

            /**
             * Demonstrates some basic types that you can use as parameters
             * and return values in AIDL.
             */
            @Override
            public java.util.List<com.zhoujian.ipc.aidl.Book> getBookList() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.util.List<com.zhoujian.ipc.aidl.Book> _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.createTypedArrayList(com.zhoujian.ipc.aidl.Book.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            @Override
            public void addBook(com.zhoujian.ipc.aidl.Book book) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    if ((book != null)) {
                        _data.writeInt(1);
                        book.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }

        static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }

    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    public java.util.List<com.zhoujian.ipc.aidl.Book> getBookList() throws android.os.RemoteException;

    public void addBook(com.zhoujian.ipc.aidl.Book book) throws android.os.RemoteException;
}

根据代码可以看出IBookManager是一个接口,它继承了IInterface这个接口,所有可以在Binder中传输的接口都要继承IInterface接口。