Android学习笔记(1) - AIDL之一

本文介绍了Android中的AIDL(Android Interface Description Language),用于简化进程间通信(IPC)。通过一个AIDL使用示例,阐述了AIDL如何生成中间Java代码,以及如何在服务端和客户端之间实现通信。重点分析了IBinder接口、Stub类的角色和通信过程,揭示了Binder框架下的IPC机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.  AIDL使用例子

AIDL,Android Interface Describle Language是android接口描述语言, 它并不是代码(虽然语法和Java很类似),而是需要通过中间编译器编译成Java代码。关于AIDL如何使用的例子网上资料很多(比如下面两个链接),本次学习借用网上例子自己实现,并分析大致的代码流程。

        https://www.jianshu.com/p/ef86f682a8f9

        https://www.jianshu.com/p/0641aef2c9e9

首先借用上面链接中的一张图。简单来说就是AIDL描述了进程间通信的接口,通过编译器编译成中间Java代码,进程实现基于编译的中间代码实现很少的部分代码就可以实现通信功能(Android也提供很多帮助类,如下面看到的Service, ServiceConnection等)。我们知道Android上绝大部分的进程间通信使用的都是Binder,其实AIDL底层也是基于Binder,但AIDL的出现使得APP开发者无需进行繁杂的Binder操作,可以十分方便的实现IPC。

顾名思义,该语言是用来描述接口的,也即server和client之间通信API的定义以及接口中用到的数据的定义。如下是示例中的两个定义,它的语法和Java类似,第一行是包名,第二行import,后面通过interface定义接口。

// IAddService.aidl
package hai.mytest;

// Declare any non-default types here with import statements
import hai.mytest.Userd;

interface IAddService {
    int add(in int v1, in int v2);

    List<Userd> addUser(in Userd user);
}
// Userd.aidl
package hai.mytest;

parcelable Userd;

在实际使用的时候如何进行编译。在Androidstudio中编译结果位于build/generated/source/aidl/debug(release)/xxx/,调用的编译工具可以从build的log中获知。如果需要再源码中编译则可以在Android.mk中通过call all-Iaidl-files-under函数来加入编译,如下所示。需要注意的是aidl文件需要同时放在server和client,而且路径要一致,然后在server和client继承中间代码并实现相应接口。

LOCAL_SRC_FILES := $(call all-java-files-under, src) \
    $(call all-Iaidl-files-under, src)

    如下是接口IAddService.aidl文件生成的同名Java文件IAddService.java,实际上是一个IAddService接口,这个接口继承android.os.IInterface。其中2个函数add和addUser是用户(如前面代码所示)增加的接口,这两个接口需要用户自己实现。另外内部有一个静态抽象类Stub(为了整个代码清晰,暂时省略具体实现,用...代替;下面展开分析),它继承自android.os.Binder同时需要实现父类接口,从android.os.Binder名字也能知道,主要目的就是为了实现Binder通信。

/*
 * This file is auto-generated.  DO NOT MODIFY.
 */
package hai.mytest;
public interface IAddService extends android.os.IInterface
{
    /** Local-side IPC implementation stub class. */
    public static abstract class Stub extends android.os.Binder implements hai.mytest.IAddService
    {...}
    public int add(int v1, int v2) throws android.os.RemoteException;
    public java.util.List<hai.mytest.Userd> addUser(hai.mytest.Userd user) throws android.os.RemoteException;
}

首先看一下如何使用,在Server端实现一个自定义的类这里是AdditionService,该类继承自Service,如其名这是一个服务。

public class AdditionService extends Service {
    private ArrayList users;
    public AdditionService() {
    }
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        users = new ArrayList<Userd>();
        return iBinder;
    }
    private  IBinder iBinder = new  IAddService.Stub() {
        @Override
        public int add(int v1, int v2) throws RemoteException {
            return v1 + v2;
        }
        @Override
        public List<Userd> addUser(Userd user) {
            users.add(user);
            return users;
        }
    };
}

Client端的代码如下。所以处理流程是client端通过bind绑定到service(通过参数Intent);service接收到绑定请求会触发回调函数onBind,用户需要在onBind中返回一个IBinder对象给client端,这个IBinder对象是通信的关键,首先它是一个binder具备通信的功能,其次它实现了用户定义的接口能够实现用户逻辑。bindService中的connection参数是一个回调类,用户实现onServiceConnected来得到service端返回的IBinder,并通过asInterface方法将返回的IBinder转换成AIDL中间类(接口)对象。这样在client端可以直接调用用户定义的接口,service端对应的接口就会被调用。这就看起来就像是在同一个进程中调用完全一样。

    IAddService myService;
    ...

    private void initService() {
        connection = new AdditionServiceConnection();
        Intent i = new Intent(MainActivity.this, AdditionService.class);
        bindService(i, connection, Context.BIND_AUTO_CREATE);
    }
    private void releaseService() {
        unbindService(connection);
        connection = null;
    }
    class AdditionServiceConnection implements ServiceConnection {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            myService = IAddService.Stub.asInterface(service);
            Log.i(TAG, "onServiceConnected");
            Toast.makeText(MainActivity.this, "Service connected", Toast.LENGTH_SHORT).show();
        }
        @Override
        public void onServiceDisconnected(ComponentName name) {
            myService = null;
            Toast.makeText(MainActivity.this, "Service disconnected", Toast.LENGTH_SHORT).show();
        }
    }

2.  代码分析

2.1 android.os.IBinder 接口

前面已经看到中间接口IAddService的内部类Stub继承自android.os.IBinder。以下翻译自谷歌文档。

IBinder是远程对象的基础接口,它定义了远程通信的协议。Binder类实现了IBinder接口,一般不需要用户去实现,一个Binder实例就可以认为是一个远程对象的代表(binder的这一套也可以在同一个进程中使用,binder内部会处理这个问题),一般用户如果想实现进程通信也不会去继承Binder对象而是通过AIDL,或者有时候需要一个在进程间通信的token,可以使用Binder。

对于IBinder核心的API是transact和Binder.onTransact一一匹配,通过这两个方法可以向一个IBinder对象发送一个调用并相应的得到Binder对象的对应的回调,这两个方法是同步的,也就是说只有得到Binder.onTrascat返回结果的时候transact才会返回,这种定义和同进程中的调用方式一致。

通过transact传输的数据叫做Parcel,Android一个通用的内存数据类,这个类内部还维护了一下元数据meta-data。元数据可以对本进程的IBinder对象进行引用,所以我们可以将IBinder的引用在进程之间进行传输。比如当我们将本进程中的一个IBinder写入到Parcel(写入的是引用)并且发送到另一个进程时,另一个进程可以将这个引用发送回本进程,本进程通过引用可以得到具体的IBinder对象,也就是说具有唯一性。这就是为什么Binder对象可以用来作为在不同进程间进行传输的token()。

IBinder的生命周期...

确实如前面所猜想的,Stub用来做进程间通信,而且通信的主要函数是transact和onTransact。但是在中间接口IAddService并没有实现transact接口,这是为何?我们知道对于例子中的应用,实际的流程是单向的即client调用,service实现(大部分的应用都如此,也可以通过回调或者其它的binder来实现反方向的调用)。简单画一下流程就是client端定义的接口(不论是用户定义的还是Binder实现必须的比如pingBinder等),说到底都是将参数进行打包并通过Transact发送到远端(每个接口使用不同的ID区分);此时远端的onTransact会被调用,它将参数解析出来并通过接口的ID找到对应的方法并调用,等调用完之后将结果按照原路返回。以上其实和上面的翻译是对应的,即transact和onTransact一一对应,且同步。

2.2 Stub类如何通信

所以我们想找transact函数如何通信只需要找到对应的接口往下分析即可。以Add为例,取出主要的流程。

IAddService myService = null;
...
myService = IAddService.Stub.asInterface(service);
...
res = myService.add(v1, v2);

首先分析myService的实例化,通过asInterface的作用是从远端的IBinder生成一个Proxy实例,如其名它一个远程的代理。

    /**
     * Cast an IBinder object into an hai.mytest.IAddService interface,
     * generating a proxy if needed.
     */
    public static hai.mytest.IAddService asInterface(android.os.IBinder obj)
    {
        if ((obj==null)) {
            return null;
        }
        android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
        if (((iin!=null)&&(iin instanceof hai.mytest.IAddService))) {
            return ((hai.mytest.IAddService)iin);
        }
        return new hai.mytest.IAddService.Stub.Proxy(obj);
    }

Proxy是Stub的一个子类。它将远程IBinder保存在mRemote中,并且实现了我们定义的add和addUser方法。可以看到它们确实只是调用transact方法。首先在Parcel中依次写入了DESCRIPTOR(这是一个类名字的字符串,和前面讲的Binder token不是一个概念,以后会学习),参数v1和v2之后就直接调用了mRemote的transact,然后就坐等返回了。可以看到其中的接口ID是Stub.TRANSACTION_add,是一个整数。

    private static class Proxy implements hai.mytest.IAddService
    {
        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 int add(int v1, int v2) throws android.os.RemoteException
        {
            android.os.Parcel _data = android.os.Parcel.obtain();
            android.os.Parcel _reply = android.os.Parcel.obtain();
            int _result;
            try {
                _data.writeInterfaceToken(DESCRIPTOR);
                _data.writeInt(v1);
                _data.writeInt(v2);
                mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
                _reply.readException();
                _result = _reply.readInt();
                }
            finally {
                _reply.recycle();
                _data.recycle();
                }
            return _result;
        }
        @Override public java.util.List<hai.mytest.Userd> addUser(hai.mytest.Userd user)     throws android.os.RemoteException
        {
            android.os.Parcel _data = android.os.Parcel.obtain();
            android.os.Parcel _reply = android.os.Parcel.obtain();
            java.util.List<hai.mytest.Userd> _result;
            try {
                _data.writeInterfaceToken(DESCRIPTOR);
                if ((user!=null)) {
                _data.writeInt(1);
                user.writeToParcel(_data, 0);
                }
                else {
                    _data.writeInt(0);
                }
                mRemote.transact(Stub.TRANSACTION_addUser, _data, _reply, 0);
                _reply.readException();
                _result = _reply.createTypedArrayList(hai.mytest.Userd.CREATOR);
            }
            finally {
                _reply.recycle();
                _data.recycle();
            }
            return _result;
        }
    }

对于service端来讲需要分析onTransact,这个函数的流程也是十分清晰。在TRANSACTION_add分支中得到远端client传过来的参数并调用本地的add函数,然后将结果返回。

    @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
    {
        java.lang.String descriptor = DESCRIPTOR;
        switch (code)
        {
            case INTERFACE_TRANSACTION:
            {
                reply.writeString(descriptor);
                return true;
            }
            case TRANSACTION_add:
            {
                data.enforceInterface(descriptor);
                int _arg0;
                _arg0 = data.readInt();
                int _arg1;
                _arg1 = data.readInt();
                int _result = this.add(_arg0, _arg1);
                reply.writeNoException();
                reply.writeInt(_result);
                return true;
             }
             case TRANSACTION_addUser:
             {
                 data.enforceInterface(descriptor);
                 hai.mytest.Userd _arg0;
                 if ((0!=data.readInt())) {
                     _arg0 = hai.mytest.Userd.CREATOR.createFromParcel(data);
                 }
                 else {
                     _arg0 = null;
                 }
                 java.util.List<hai.mytest.Userd> _result = this.addUser(_arg0);
                 reply.writeNoException();
                 reply.writeTypedList(_result);
                 return true;
             }
             default:
             {
                 return super.onTransact(code, data, reply, flags);
             }
        }
    }

3.  未完待续

到目前为止,结合AIDL的基本使用方法和中间生成的代码分析了AIDL是如何完成进程间IPC的(基于Binder进程间通信框架,后面学习)。对于应用开发者来说中间源文件屏蔽了大部分通信的实现使得用户对AIDL的使用十分简单。

遗留问题:

    Java的Interface中 static abstract 子类和方法

    Parcel

    service到client的反向调用

    API深入,如queryLocalInterface

    源码中编译脚本分析

参考:

    https://developer.android.com/reference/android/os/IBinder.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值