Android之AIDL

一. 基本认识

在Android中,每个应用(Application)执行在它自己的进程中,无法直接调用到其他应用的资源,当一个应用被执行时,一些操作是被限制的,比如访问内存,访问传感器,等等。这样做可以最大化地保护系统,免得应用程序“为所欲为”。那我们有时需要在应用间交互,怎么办呢?于是,Android需要实现IPC协议。然而,这个协议还是有点复杂,主要因为需要实现数据管理系统(在进程或线程间传递数据)。为了暂时减缓这个“会呼吸的痛”,Android为我们实现了自己的IPC,也就是AIDL。

AIDL的语法很类似Java的接口(Interface),只需要定义方法的签名。AIDL支持的数据类型与Java接口支持的数据类型有些不同

  1. 所有基础类型(int, char, 等)
  2. String,List,Map,CharSequence等类
  3. 其他AIDL接口类型
  4. 所有Parcelable的类

下面来看一下如何使用AIDL

  • 定义AIDL接口

这里写图片描述
在src目录下创建IMyService.aidl文件,这个只要是在eclipse中开发,你的adt插件会像资源文件一样把aidl文件编译成java代码生成在gen文件夹下,不用手动去编译:编译生成IMyService..java。
IMyService.aidl中代码

package com.chm.aidltest;
interface IMyService{
    String getValue();
}
  • 为远程服务(Service)实现对应Stub

首先我们在com.chm.aidltest这个包中新建一个类,取名为MyService.java。为了实现我们的服务,我们需要让这个类中的onBind方法返回一个IBinder类的对象。这个IBinder类的对象就代表了远程服务的实现。为了实现这个服务,我们要用到自动生成的子类IMyService..Stub。在其中,我们也必须实现我们之前在AIDL文件中定义的getValue()函数。下面是我们远程服务的代码:

public class MyService extends Service {

//    public class MyServiceImpl extends IMyService.Stub {
//        @Override
//        public String getValue() throws RemoteException {
//            return "Android开发";
//        }
//    }
    @Override
    public IBinder onBind(Intent intent) {
        // TODO Auto-generated method stub
        //  return new MyServiceImpl();
        return mBinder;
    }

    private IMyService.Stub mBinder = new IMyService.Stub() {

        @Override
        public String getValue() throws RemoteException {
            // TODO Auto-generated method stub
            return "success";
        }
    };
}
  • 绑定服务

一旦实现了服务中的onBind方法,我们就可以把客户程序(在这里是MainActivity.java)与服务连接起来了。为了建立这样的一个链接,我们需要实现ServiceConnection类。我们在MainActivity.java创建一个ServiceConnection类的成员变量serviceConnection ,并且重写了它的两个方法:onServiceConnected和onServiceDisconnected。下面给出内部类的代码。

private ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            // 获得服务对象
            myService = IMyService.Stub.asInterface(service);
            btnInvokeAIDLService.setEnabled(true);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            //释放资源
            myService = null;
        }
    };

// 绑定AIDL服务,显示Intent启动
Intent mIntent = new Intent();
mIntent.setComponent(new ComponentName("com.mobile.aidl""com.mobile.aidl.IMyService"));
bindService(mIntent,serviceConnection, Context.BIND_AUTO_CREATE);   

@Override
protected void onDestroy() {
    super.onDestroy();
    unbindService(serviceConnection);
}

至此,整个功能流程都完成了

二. 深入理解AIDL

IMyService.java源码

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

当通讯的双方在同一个进程中时,onServiceConnected传回的对象是Service.onBind()所返回的对象;但如果是跨进程时,则其返回的是一个BinderProxy对象。所以,可以看到在AIDL生成的类中会有这样的判断:

if (((iin!=null)&&(iin instanceof com.chm.aidltest.IMyService))) {
return ((com.chm.aidltest.IMyService)iin);
}

这实际上就是判断此通讯是在同一进程中,还是跨进程,因为同一进程传回的对象是Service.onBind()所返回的对象,而此对象必然实现了接口(要不然搞毛啊!)。所以,如果仅是在同一个进程之中,不会走Binder进程IPC,而是直接返回Service所提供的对象,直接调用其方法,因此也就不会有对象必须Parcelable的限制!
当在同一个进程中时AIDL实际上变成了这样的:
也就是说它是直接返回了Service.onBind()的对象,这其实和直接实现Binder对象的方式是一样一样的,其他的代码全是多余的。因此,建议, 如果仅是在同一个进程中,就直接使用Binder就好了,没有必要创建AIDL文件。
当在不同的进程中时,客户端Stub.asInterface会返回一个Stub.Proxy对象,调用其上的getValue方法。而服务端仅会执行Stub.onTransact()方法,然后就调到Service的getValue方法了。
当跨进程的时候,就要使用Binder对象的IPC相关的方法和机制。客户端需要实现Binder.transact()方法来执行远程的一个方法,这是给客户端来使用;而服务端则需要实现Binder.onTransact()来响应客户端所请求的方法。对于上层使用者来说,用transact()把函数的信息(参数,标识和开关)发送出去,剩下的就是Binder的工作了,内部还有大量的细节,但是最终会调用到服务端Binder的onTransact()方法,这里识别出函数的标识,然后调用具体的实现,再传回返回值,这样一个IPC的函数调用就完成了。

其实AIDL的作用就是对Binder的二个方法:Binder.transact()和Binder.onTransact()进行封装,以供Client端和Server端进行使用。因为实现transact()和onTransact()方法的方式基本上是相同的,所以就可以用模板来生成具体的代码。理论上讲只需要为Client端生成transact()相关代码,为服务端生成onTransact()代码即可,但因为工具无法准确的确定某一个应用到底是Client端还是Server端,所以它就生成所有的代码,放在一个文件中。这就是你看到的自动生成的文件。
还需要注意的一点是Client端的Proxy是组合Binder对象,调用其transact()方法;而服务端必须继承Binder对象,覆写onTransact()方法。为虾米呢?因为Client是主动发起IPC函数Call,所以它可以直接调用Binder的方法来进行IPC。而Server是被动的,是要接收进来的IPC call,但Service自己无法得知啥时候Call会来,因此必须实现回调(onTransact())给Binder,以让Binder在有IPC Call进来的时候告诉Service。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值