Android 进程间通信(一)

作者: me_touch | 来源:发表于2017-09-12 18:53 被阅读0次

    通常情况下,Android的进程之间的内存并不能共享, 所以Android 的一个进程通常不能访问另一个进程的内存。那么,要实现IPC(跨进程通信)就要使用到一些看似特殊的方式了,总的来说就是Android的四大组件。但对于Activity, ContentProvider, BroadcastReceiver而言, 用作跨进程只是实现了单向的数据操作,而并不能进行方法调用(AIDL的接口调用)。而通过bind service, 使用AIDL可以进行相互之间跨进程的方法调用和数据传递。而通过AIDL的形式传递对象时,必须要让对象对应的类支持Parcelable接口。

    Android进程间通信(二)

    什么是Parcelable

    在了解Parcelable协议之前,先解释一下Parcel。

    官方文档

    按照文档中的解释, Parcel是一种可以通过IBinder传递的消息(数据和对象引用)容器. 一个Parcel可以包含IPC通信时,另一端能够去扁平化的扁平化数据和一个实时的IBinder,这个IBinder将会导致在通信的另一端接收到一个与Parcel中的原始Ibinder相连接的IBinder代理。

    注意:Parcel并不是一个通用的序列化机制。这个类是为高性能的IPC传输而设计的,就其本身而言,并不适合持久化存储, 任意改变其中数据的底层实现都将会导致数据的不可读。

    Parcel的API是为了围绕解决读写不同类型的数据。主要包括6大类 Primitives(基类), Primitive Arrays, Parcelables, Bundles, Active Objects(Binder, ParcelFileDescriptor), Untyped Containers(容器类)。

    • Parcelable

    继承了此接口的类的示例可以从Parcel 写入和存储。实现此接口的类必须定义一个非空的静态变量 CREATOR, 该变量实现了Parcelable.Creator接口。

    //writeToParcel(...)的flag标志位, 被写入的数据是其他函数的运行结果, 会阻止资源的释放
    public static final int PARCELABLE_WRITE_RETURN_VALUE = 0x0001;
    
    //writeToParcel(...)的flag标志位, 标示父对象将会管理通常从内部类复制的重复数据
    public static final int PARCELABLE_ELIDE_DUPLICATES = 0x0002;
    
    //文件描述符标志
    public static final int CONTENTS_FILE_DESCRIPTOR = 0x0001;
    
    //描述Parcelable中特殊对象的种类, 通常为0, 但包含文件描述符时,应返回 CONTENTS_FILE_DESCRIPTOR
    public int describeContents();
    
    //在dest中扁平化这个对象
    public void writeToParcel(Parcel dest, int flags);
    
    //必须实现的静态变量CREATOR, 用于丛Parcel中创建Parcelable实例
    public interface Creator<T> {
        
        //创建实例    
        public T createFromParcel(Parcel source);
            
        //创建一组新的Parcelable类数组
        public T[] newArray(int size);
    }
    
    
    public interface ClassLoaderCreator<T> extends Creator<T> {
         
        //特殊的CREATOR,允许接收ClassLoader   
        public T createFromParcel(Parcel source, ClassLoader loader);
    }
    
    • PaecelableCompat

    Pracelable的向下兼容类,为了更好的支持低版本, 以API13为分界线(support-v4-25.2.0), 创建一个兼容性更好的CREATOR, APi13以下 和之后的两个版本的主要差别就在于ClassLoader。

    • Parcelable和Serializable的区别

    Serializable是JDK提供的一种序列化方式,而Parcelable则是Android为解决IPc而提供的一种序列方式,它相对于Serializable而言,具有更高的性能,但实现更加麻烦, 而且不适合持久化存储(原因上文有提到, Parcel对data实现有严格要求,改变任意的实现都可能导致之前的存储不可读。 可在下例中找到具体解释)。

    NewsEntity.ava

    package th.how.bean;
    
    import org.greenrobot.greendao.annotation.Entity;
    import org.greenrobot.greendao.annotation.Generated;
    import org.greenrobot.greendao.annotation.Id;
    import org.greenrobot.greendao.annotation.Keep;
    
    /**
     * Created by me_touch on 2017/8/16.
     *注解是因为使用了GreenDao, 可以忽略
     */
    
    @Entity
    public class NewsEntity{
    
        @Id
        int id;
        String title;
    
        @Keep
        public NewsEntity(int id, String title){
            this.id = id;
            this.title = title;
        }
    
        @Generated(hash = 2121778047)
        public NewsEntity() {
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public int getId() {
            return id;
        }
    
        public void setTitle(String title) {
            this.title = title;
        }
    
        public String getTitle() {
            return title;
        }
    
        @Override
        public String toString() {
            return "id = " + id + ", title = " + title;
        }
    }
    

    NewsEntityParcel.java

    package th.how.bean;
    
    import android.os.Parcel;
    import android.os.Parcelable;
    import android.support.v4.os.ParcelableCompat;
    import android.support.v4.os.ParcelableCompatCreatorCallbacks;
    
    /**
     * Created by me_touch on 2017/9/7.
     * Parcel 实现类, 实现Parcelable接口, 读取和写入的顺序要保持一致,否则有可能导致不可测的结果
     */
    
    public class NewsEntityParcel extends NewsEntity implements Parcelable{
    
        //如果改变读写顺序,会导致原存储数据变为不可读状态
        public NewsEntityParcel(Parcel in){
            this.id = in.readInt();
            this.title = in.readString();
        }
    
        public NewsEntityParcel(int id, String title){
            this.id = id;
            this.title = title;
        }
    
        @Override
        public int describeContents() {
            return 0;
        }
    
        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeInt(id);
            dest.writeString(title);
        }
        
        public void readFromParcel(Parcel dest){
            this.id = dest.readInt();
            this.title = dest.readString();
        }
    
        //通过ParcelableCompat 创建CREATOR, 基于兼容性考虑
        public static Parcelable.Creator<NewsEntityParcel> CREATOR = ParcelableCompat.newCreator(
                new ParcelableCompatCreatorCallbacks<NewsEntityParcel>() {
            @Override
            public NewsEntityParcel createFromParcel(Parcel in, ClassLoader loader) {
                return new NewsEntityParcel(in);
            }
    
            @Override
            public NewsEntityParcel[] newArray(int size) {
                return new NewsEntityParcel[size];
            }
        });
    }
    

    AIDL的概念及语法

    官方文档

    概念

    根据文档所述,AIDL是用来简化将对象编组成跨越边界的对象这一繁杂过程的。AIDL文件的编写遵循Java简单语法。AIDL的接口通过1个或多个方法声明。其与普通的接口类就语法而言,区别主要在于以下几点

    1. AIDL 只支持方法,也即是说在AIDL中不能定义静态变量
    2. AIDL有自己的关键词。
    3. 在Java1.8中,接口可以包含实现的方法,而AIDL中不能。
    4. 不能使用权限修饰符,以及final, static 等
    5. 必须显式的使用import

    关键词

    • oneway 通常情况下client通过AIDL的方式调用service方法时, client 会进入阻塞状态,等待service对应的方法执行完成。然而通过oneway关键词修饰的方法,就可以避免被调用时阻塞客户端。oneway可以用于修饰interface 和方法, 修饰interface时,相当于在所有声明的方法前加oneway关键词。注意 oneway 不能修饰有返回值的方法。

    • in, out, inout 用于标记非原语参数的数据走向, 注意所有原语参数默认并且只能为in。in表示数据从客户端流向服务端,而out表示数据从服务端流向客户端, inout则是双向的(客户端和服务端是相对的, 并不绝对)。
      如果设置错误,有可能导致获得的对象不为空,但值却是空的,当然也不能在并没有必要的情况下,直接设置成inout,这会导致严重的开销。

    • 什么时候用in, 什么时候用out? 当仅需要把客户端的值传递给服务端时用in, 仅需要在服务端完成赋值,再传递给客户端时用out

    传递对象

    除了对象对应的类需要实现Parcelable方法外,还 需要在aidl文件中声明对应的类为Parcelable, 用以查找并识别该类实现了Parcelable方法。

    一个例子

    具体方式: 在module目录下,与java同级的目录下建立一个aidl目录,然后建立一个包名与需要传递的对象对应的类的包名相同的包,如NewsEntityParcel.aidl所示, 如果不需传递, 则忽略掉这一步。创建新的aidl文件以定义需要调用的方法,需要声明所在的包,如IMyAidlInterface.aidl, IClientInterface.aidl, 然后编译,生成对应的接口文件(build/generated/source/aidl)。

    NewsEntityParcel.aidl

    package th.how.bean;
    parcelable NewsEntityParcel;
    

    IMyAidlInterface.aidl

    package th.how.ipc;
    import th.how.bean.NewsEntityParcel;
    import th.how.ipc.IClientInterface;
    interface IMyAidlInterface {
        NewsEntityParcel getData(int id, String title, in NewsEntityParcel parcel);
        void register(IClientInterface callback);
        void unRegister(IClientInterface callback);
    }
    

    IClientInterface.aidl

    package th.how.ipc;
    import th.how.bean.NewsEntityParcel;
    
    interface IClientInterface {
        void callbackClient(in NewsEntityParcel parcel);
    }
    

    分析一下AIDL文件经在编译后生成的文件, 以IMyAidlInterface.aidl为例

    /*
     * This file is auto-generated.  DO NOT MODIFY.
     * Original file: G:\\2SelfWork\\HowApp\\app\\src\\main\\aidl\\th\\how\\ipc\\IMyAidlInterface.aidl
     */
    package th.how.ipc;
    public interface IMyAidlInterface extends android.os.IInterface
    {
    /** Local-side IPC implementation stub class. */
    public static abstract class Stub extends android.os.Binder implements th.how.ipc.IMyAidlInterface
    {
    private static final java.lang.String DESCRIPTOR = "th.how.ipc.IMyAidlInterface";
    /** Construct the stub at attach it to the interface. */
    public Stub()
    {
    this.attachInterface(this, DESCRIPTOR);
    }
    /**
     * 将IBinder对象转化为对应的IMyAidlInterface
     * 方式:从IBinder中根据DESCRIPTOR查询对应的IMyAidlInterface对象,如果没有,则
     * 通过代理生成对应的对象
     */
    public static th.how.ipc.IMyAidlInterface asInterface(android.os.IBinder obj)
    {
    if ((obj==null)) {
    return null;
    }
    android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
    if (((iin!=null)&&(iin instanceof th.how.ipc.IMyAidlInterface))) {
    return ((th.how.ipc.IMyAidlInterface)iin);
    }
    return new th.how.ipc.IMyAidlInterface.Stub.Proxy(obj);
    }
    @Override public android.os.IBinder asBinder()
    {
    return this;
    }
    @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;
    }
    //对应的getData(int id, String title, in NewsEntityParcel parcel)方法
    case TRANSACTION_getData:
    {
    data.enforceInterface(DESCRIPTOR);
    //获取三个参数的值
    int _arg0;
    _arg0 = data.readInt();
    java.lang.String _arg1;
    _arg1 = data.readString();
    th.how.bean.NewsEntityParcel _arg2;
    if ((0!=data.readInt())) {
    _arg2 = th.how.bean.NewsEntityParcel.CREATOR.createFromParcel(data);
    }
    else {
    _arg2 = null;
    }
    //在Stub对象中调用该方法
    th.how.bean.NewsEntityParcel _result = this.getData(_arg0, _arg1, _arg2);
    reply.writeNoException();
    //在reply中写入返回值
    if ((_result!=null)) {
    reply.writeInt(1);
    _result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
    }
    else {
    reply.writeInt(0);
    }
    return true;
    }
    case TRANSACTION_register:
    {
    data.enforceInterface(DESCRIPTOR);
    th.how.ipc.IClientInterface _arg0;
    _arg0 = th.how.ipc.IClientInterface.Stub.asInterface(data.readStrongBinder());
    this.register(_arg0);
    reply.writeNoException();
    return true;
    }
    case TRANSACTION_unRegister:
    {
    data.enforceInterface(DESCRIPTOR);
    th.how.ipc.IClientInterface _arg0;
    _arg0 = th.how.ipc.IClientInterface.Stub.asInterface(data.readStrongBinder());
    this.unRegister(_arg0);
    reply.writeNoException();
    return true;
    }
    }
    return super.onTransact(code, data, reply, flags);
    }
    private static class Proxy implements th.how.ipc.IMyAidlInterface
    {
    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;
    }
    @Override public th.how.bean.NewsEntityParcel getData(int id, java.lang.String title, th.how.bean.NewsEntityParcel parcel) throws android.os.RemoteException
    {
    android.os.Parcel _data = android.os.Parcel.obtain();
    android.os.Parcel _reply = android.os.Parcel.obtain();
    th.how.bean.NewsEntityParcel _result;
    try {
    _data.writeInterfaceToken(DESCRIPTOR);
    _data.writeInt(id);
    _data.writeString(title);
    if ((parcel!=null)) {
    _data.writeInt(1);
    parcel.writeToParcel(_data, 0);
    }
    else {
    _data.writeInt(0);
    }
    mRemote.transact(Stub.TRANSACTION_getData, _data, _reply, 0);
    _reply.readException();
    if ((0!=_reply.readInt())) {
    _result = th.how.bean.NewsEntityParcel.CREATOR.createFromParcel(_reply);
    }
    else {
    _result = null;
    }
    }
    finally {
    _reply.recycle();
    _data.recycle();
    }
    return _result;
    }
    @Override public void register(th.how.ipc.IClientInterface callback) throws android.os.RemoteException
    {
    android.os.Parcel _data = android.os.Parcel.obtain();
    android.os.Parcel _reply = android.os.Parcel.obtain();
    try {
    _data.writeInterfaceToken(DESCRIPTOR);
    _data.writeStrongBinder((((callback!=null))?(callback.asBinder()):(null)));
    mRemote.transact(Stub.TRANSACTION_register, _data, _reply, 0);
    _reply.readException();
    }
    finally {
    _reply.recycle();
    _data.recycle();
    }
    }
    @Override public void unRegister(th.how.ipc.IClientInterface callback) throws android.os.RemoteException
    {
    android.os.Parcel _data = android.os.Parcel.obtain();
    android.os.Parcel _reply = android.os.Parcel.obtain();
    try {
    _data.writeInterfaceToken(DESCRIPTOR);
    _data.writeStrongBinder((((callback!=null))?(callback.asBinder()):(null)));
    mRemote.transact(Stub.TRANSACTION_unRegister, _data, _reply, 0);
    _reply.readException();
    }
    finally {
    _reply.recycle();
    _data.recycle();
    }
    }
    }
    static final int TRANSACTION_getData = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    static final int TRANSACTION_register = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    static final int TRANSACTION_unRegister = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
    }
    public th.how.bean.NewsEntityParcel getData(int id, java.lang.String title, th.how.bean.NewsEntityParcel parcel) throws android.os.RemoteException;
    public void register(th.how.ipc.IClientInterface callback) throws android.os.RemoteException;
    public void unRegister(th.how.ipc.IClientInterface callback) throws android.os.RemoteException;
    }
    
    

    生成的 IMyAidlInterface接口继承自IIterface接口

    package android.os;
    
    //Binder接口的基类
    public interface IInterface
    {
        
        //通过与这个接口的关联关系检索Binder对象,为了代理对象能够返回正确的值,必须使用该方法获取,而不是直接转型
        public IBinder asBinder();
    }
    

    接口内部,定义了一个继承自Binder并且实现IMyAidlInterface接口的抽象类Stub, 实现了返回接口对象方法,IBinder对象方法以及onTransact方法, 以及内部类Proxy。

    • onTransact(...)方法实现了对aidl文件中定义的方法的调用, 注释中解释了其中一个方法,其余类似。
    • Proxy 在代理类中实现了aidl文件中定义的方法以及调用

    相关文章

      网友评论

        本文标题:Android 进程间通信(一)

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