您的位置:首页 > 移动开发 > Android开发

Android 跨进程通信基础

2015-03-13 18:14 489 查看
Android跨进程通信基础——Binder, BinderProxy, parcel, parcelable, Stub, Stub.Proxy(该文章最早于2013年6月7日发表于有道云笔记 进入阅读)

百度、google 过很多文章,都没能找到能够从 API 使用者角度简单描述 Binder,BinderProxy,Parcel,Parcelable,Stub,Stub.Proxy 之间关系的文章,要么高深莫测,要么混乱不清。最终决定还是自己动手,看源码,看文档,现总结如下:

Binder,BinderProxy 形成了进程间通信的基础,相当于公路桥梁;

Parcel 在 IBinder 基础上传输数据,相当于运输工具;

Parcelable 基本数据类型和实现了 Parcelable 接口的复合数据类型才可被 Parcel 传输,相当于摆放整齐、安检合格的货物;

Stub,Stub.Proxy 实现跨进程调用的接口,相当于收发货方。

注:Binder,BinderProxy 都实现了 IBinder 接口

下面以 Activity 与 Service 通信为例来分析其运行机制。

示例目的:通过跨进程调用方式在 Activity 进程端调用 Service 进程端的方法。这些方法由接口 RemoteSSO 定义。这里假设两者是运行在不同进程的(也可以运行在相同进程,具体因配置而不同)。

代码实现

1、定义 RemoteSSO.aidl 文件:

package com.sina.sso;
interface RemoteSSO {
String getPackageName();
String getActivityName();
}

2、ADT会在gen目录下生成RemoteSSO.java文件如下:

/*
* This file is auto-generated.  DO NOT MODIFY.
* Original file: D:\\workspace_android\\IeltsPraBook\\src\\com\\sina\\sso\\RemoteSSO.aidl
*/
package com.sina.sso;
public interface RemoteSSO extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.sina.sso.RemoteSSO
{
private static final java.lang.String DESCRIPTOR = "com.sina.sso.RemoteSSO";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.sina.sso.RemoteSSO interface,
* generating a proxy if needed.
*/
public static com.sina.sso.RemoteSSO asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.sina.sso.RemoteSSO))) {
return ((com.sina.sso.RemoteSSO)iin);
}
return new com.sina.sso.RemoteSSO.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;
}
case TRANSACTION_getPackageName:
{
data.enforceInterface(DESCRIPTOR);
java.lang.String _result = this.getPackageName();
reply.writeNoException();
reply.writeString(_result);
return true;
}
case TRANSACTION_getActivityName:
{
data.enforceInterface(DESCRIPTOR);
java.lang.String _result = this.getActivityName();
reply.writeNoException();
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.sina.sso.RemoteSSO
{
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 java.lang.String getPackageName() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getPackageName, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public java.lang.String getActivityName() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getActivityName, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_getPackageName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getActivityName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
public java.lang.String getPackageName() throws android.os.RemoteException;
public java.lang.String getActivityName() throws android.os.RemoteException;
}

3、定义 MyRemoteSSO extends RemoteSSO.Stub。由于 RemoteSSO.Stub 是抽象类,我们应该继承该类并实现相应的功能。这里是以下两方法:

public java.lang.String getPackageName() throws android.os.RemoteException;
public java.lang.String getActivityName() throws android.os.RemoteException;

4、在 Service 的 onBind() 方法里面返回 MyRemoteSSO 对象。

5、在 Activity 中调用 bindService(),并在连接后通过以下转换就可以与另一个进程中的 Service 通信,调用相关方法了。

ServiceConnection mServConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder binder) {
RemoteSSO mInterface = RemoteSSO.Stub.asInterface(binder);
...
mInterface. getPackageName();
mInterface.  getActivityName();
...
}
}

至此,通道构造完毕,可以进行跨进程通信了。

运行机制

1、绑定 Service,建立跨进程通信通道

a、Activity 中调用 context.bindService(),Android 通过系统服务来启动或者连接到 Service;

b、系统服务通知 Service 回调 onBind() 方法返回 Binder 对象的一个实例(Binder 对象事实上是 RemoteSSO.Stub 的子类对象,而 RemoteSSO.Stub 继承自 Binder);

c、系统服务通知 Activity 进程端生成一个对应的 BinderProxy 对象,并作为 onServiceConnected(ComponentName name, IBinder binder) 方法的回调参数 binder,此时我们需要将 binder 作为参数手动调用 RemoteSSO.Stub. asInterface(binder) 来获取一个实现了 RemoteSSO 接口的代理对象 proxy 以供我们调用,此时通道建立完成,我们可以通过 proxy 来调用 Service 端的方法了。

由于 proxy 是实现了自定义接口 RemoteSSO 的,因此外部看来只是调用了接口方法而不是代理。事实上,proxy 是一个 RemoteSSO.Stub.Proxy 对象的实例,参数 binder(BinderProxy对象实例)便作为了 proxy 的一个变量备用。

Binder 和 BinderProxy 是成对的,在进程间用 descriptor 来标记对应关系,descriptor 通常是 RemoteSSO 的全路径名,在自动生成的 RemoteSSO.java 接口中已经自动生成。同时 Stub 和 Stub.Proxy 也是成对的,Service 端返回的是继承了 Binder 的 Stub 的子类对象实例,而 Activity 端用来实现调用的接口实例是将 BinderProxy 作为变量的 Stub.Proxy 对象的实例。

2、调用与返回

毕竟,我们的最终目的是通信,这里就是调用方法。那么当我们拿到 RemoteSSO 接口的实例 proxy,调用 getPackageName() 的时候,执行过程是怎样的呢?

a、由于 proxy 是 RemoteSSO.Stub.Proxy 对象的实例,则 proxy.getPackageName() 便是 RemoteSSO.Stub.Proxy 的 getPackageName()。

@Override
public java.lang.String getPackageName() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getPackageName, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}

该代码是自动生成的。注意这里的 mRemote 就是 BinderProxy。

b、代理对象 RemoteSSO.Stub.Proxy 将方法的参数打包到 Parcel 对象的实例 _data 里,并由 BinderProxy 调用 transact()(见 mRemote.transact(Stub.TRANSACTION_getPackageName, _data, _reply, 0); )通过底层系统级跨进程机制通知 Service 端的 Binder(Service.onBind() 返回的 RemoteSSO.Stub 子类对象)调用 transact(int code, Parcel data, Parcel reply, int flags);注意方法名是通过 code 参数标记的,见常量 Stub.TRANSACTION_getPackageName。

c、Binder.transact() 回调 RemoteSSO.Stub 的 onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags),进而调用自定义接口方法,并将返回值写入到 reply 进行打包并返回。这里是 getPackageName(),该自定义方法是手动在 RemoteSSO.Stub 的子类中实现的(具体要做什么只有自己知道,ADT 不可能帮你生成)。

@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_getPackageName:
{
data.enforceInterface(DESCRIPTOR);
java.lang.String _result = this.getPackageName();
reply.writeNoException();
reply.writeString(_result);
return true;
}
case TRANSACTION_getActivityName:
{
data.enforceInterface(DESCRIPTOR);
java.lang.String _result = this.getActivityName();
reply.writeNoException();
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}

注意这里根据 code 对方法名的标记找到对应的实际要调用的方法。

d、Binder.transact()、BinderProxy.transact() 一层层返回,即 RemoteSSO.Stub.Proxy.getPackageName() 方法的 mRemote.transact(Stub.TRANSACTION_getPackageName, _data, _reply, 0) 方法返回,此时 _reply 已经在 Service 进程中被填充了返回值,接下来可以读取该值并返回了。

至此,整个调用过程结束。

FAQ

大家有没有想过,如果调用发生在同一个进程会发生什么?

public void onServiceConnected(ComponentName name, IBinder binder) {
RemoteSSO mInterface = RemoteSSO.Stub.asInterface(binder);
...
}

public static com.sina.sso.RemoteSSO asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.sina.sso.RemoteSSO))) {
return ((com.sina.sso.RemoteSSO)iin);
}
return new com.sina.sso.RemoteSSO.Stub.Proxy(obj);
}

事实上,在同一个进程中 onServiceConnected() 的参数 binder 是 Binder 对象而不是 BinderProxy 对象,因此在执行 asInterface() 的时候返回的是 binder 本身(见obj.queryLocalInterface(DESCRIPTOR);),也就是 RemoteSSO.Stub 子类对象,而不是 RemoteSSO.Stub.Proxy 对象,此时在执行 getPackageName() 时直接就是调用手动实现的该方法,也就是直接调用了 Service 端的该方法,而没有绕一个大圈。如果必然是在同一个进程,可以直接这样处理来获得调用接口:

public void onServiceConnected(ComponentName name, IBinder binder) {
RemoteSSO mInterface = (RemoteSSO)binder;
...
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息