Android 进程间通信(一)

通常情况下,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 {
    
    //创建实例    
    public T createFromParcel(Parcel source);
        
    //创建一组新的Parcelable类数组
    public T[] newArray(int size);
}


public interface ClassLoaderCreator extends Creator {
     
    //特殊的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 CREATOR = ParcelableCompat.newCreator(
            new ParcelableCompatCreatorCallbacks() {
        @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 进程间通信(一))