android aidl通讯详解

    xiaoxiao2026-05-18  1

    一,aidl进程通讯介绍

    Android 是进程间内存是分离的,因此需要将对象分解成操作系统可理解的元数据,并将此打包让操作系统帮忙传递对象到另一个进程。这个过程是十分复杂繁重的,因此 Google 定义了 AIDL(Android Interface Definition Language)帮助开发者简化工作。

    二,aidl通讯的实现

    实现步骤:

    1,创建.aidl文件-该文件(YourInterface.aidl)定义了客户端可用的方法和数据的接口。

    2,在服务端和客户端makefile文件中都加入.aidl文件——Eclipse中的ADT插件自动将aidl文件编译成java代码生成在gen文件夹下

    3,Implement your interface methods - The AIDL compiler creates an interface in the Java programming language from your AIDL interface. This interface has an inner abstract class named Stub that inherits the interface (and implements a few additional methods necessary for the IPC call). You must create a class that extends YourInterface.Stub and implements the methods you declared in your .aidl file.  实现你定义aidl接口中的内部抽象类Stub。(这一步骤建议联系下面的代码理解会容易一些)

    class Stub extends Binder implements binder.aidl.AIDLService (这里的代码在gen文件夹下)

    4,服务端继承Service并且重载Service.onBind(Intent)以返回实现了接口的对象实例

    具体实现

    IMyService.aidl:

    package com.myapp.aidl; interface IMyService { public void playMusic(int id); int getName(int id); }

    这里的设置意味着:客户端可以向服务器发送指令和获取数据。

    其中aidl中支持的参数类型为:基本类型(int、long、char、boolean等),String,CharSequence,List,Map。若想要自定义对象传输,后面会详细讲到。

    定义好aidl文件后,请在服务端和客户端粘贴一份一模一样的aidl文件。

    Eclipse在编译的时候会自动在gen下面为我们生成了一个代理类Stub,这个Stub类就是一个普通的Binder,只不过它实现了我们定义的aidl接口。通过它的静态方法asInterface我们就可以在客户端中得到IMyService的实例,进而通过实例来调用其他方法。

    客户端:

    Intent startIntent = new Intent(); ComponentName componentName = new ComponentName( PACKAGE_REMOTE_SERVICE, NAME_REMOTE_SERVICE); startIntent.setComponent(componentName); bindService(startIntent, mConnection, Context.BIND_AUTO_CREATE);

    这里的PACKAGE_REMOTE_SERVICE,NAME_REMOTE_SERVICE就是指定的service。

    IMyService mService; private ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { Log("connect service"); mService = IMyService.Stub.asInterface(service); try { int name = mService.getName(1); } catch (RemoteException e) { } } public void onServiceDisconnected(ComponentName className) { Log("disconnect service"); mService = null; } };

    服务端:

    public class TestService extends Service { @Override public IBinder onBind(Intent arg0) { System.out.println("Service onBind"); return mBinder; } private final IMyService.Stub mBinder = new IMyService.Stub() { @Override String getName(int id){ String name = findBy(id);//findBy()这是伪代码 return name; } @Override public void playMusic(int id){ playMusic(int id);//playMusic()也是伪代码; } }; }

    Manifest.xml中记得配置service,注意:exported="true"要配置为true,不然客户端是没有权限访问服务的。

    <service android:name="com.marttinli.service.MusicService" android:exported="true"> </service>

    如此一个基本的aidl通讯就完成了。

    原理分析

    那么接下来我们开始分析在gen目录下自动生成的文件到底是什么?

    这里有几个重要的类和方法:asInterface(IBinder obj) , Proxy ,transact(int code, Parcel data, Parcel reply, int flags) ,onTransact(int code, Parcel data,Parcel reply, int flags)

    这里涉及到Parcel序列化的一些技术,晚些会专门开一章来讲,这里你先明白Parcel对象是用来在进程间传递的数据对象,其他的基本类型和对象最终都要序列化程Parcel数据才能在进程间传递。

    还记得客户端调用的IMyService.Stub.asInterface(service)吧,就在这里

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

    这里返回了一个Proxy,并把service作为输入。而Proxy正是完成了对我们在aidl接口中定义接口的实现。这里Client把参数转换成Parcel(_data)传递到了Service,而在Server端又会把返回数据保存到_reply中,这就形成了一次交互。

    mRemote是远程对象,transact方法会执行onTransact方法

    @Override public void playMusic(int id) 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((((id != null)) ? (id .asBinder()) : (null))); mRemote.transact(Stub.TRANSACTION_playMusic, _data, _reply, 0); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } }

    @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_playMusic: { data.enforceInterface(DESCRIPTOR); int _arg0; _arg0 = data.readInt(); this.playMusic(_arg0); reply.writeNoException(); return true; } } return super.onTransact(code, data, reply, flags); }而到了onTransact()中,已经是开始和service进行交互了,_data最终转化成了他原来的数据格式(这里是int),然后调用playMusic(),service端实现了playMusic()。

    三,aidl通讯传递对象数据

    aidl的数据传递本来也就是把基本数据类型转化成Parelable作为跨进称数据传输的格式。

    所以一个对象想要传输,也只需要把对象转成Parelable 就可以跨进称传输。

    我还专门为此写了个demo,确保可行。

    服务端3个文件,我假定我需要传输的对象为Music:Music.java,Music.aidl,RemoteMusic.aidl

    Music.aidl

    parcelable Music; RemoteMusic.aidl

    package com.marttinli.aidl; import com.marttinli.aidl.Music; interface RemoteMusic{ Music getMusic(); void uploadMusic(in Music music); } Music.java

    public class Music implements Parcelable { int id; String name; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public int describeContents() { // TODO Auto-generated method stub return 0; } @Override public void writeToParcel(Parcel dest, int flags) { // TODO Auto-generated method stub dest.writeInt(id); dest.writeString(name); } /** * 在想要进行序列号传递的实体类内部一定要声明该常量。常量名只能是CREATOR,类型也必须是 * Parcelable.Creator<T> */ public static final Parcelable.Creator<Music> CREATOR = new Creator<Music>() { /** * 创建一个要序列号的实体类的数组,数组中存储的都设置为null */ @Override public Music[] newArray(int size) { return new Music[size]; } /*** * 根据序列号的Parcel对象,反序列号为原本的实体对象 * 读出顺序要和writeToParcel的写入顺序相同 */ @Override public Music createFromParcel(Parcel source) { int id = source.readInt(); String name = source.readString(); Music music = new Music(); music.setId(id); music.setName(name); return music; } }; } MusiceService.java

    public class MusicService extends Service { MyBinder mBinder = new MyBinder(); @Override public IBinder onBind(Intent intent) { // TODO Auto-generated method stub return mBinder; } private class MyBinder extends RemoteMusic.Stub{ public Music getMusic() throws RemoteException { Music music = new Music(); music.setId(1) ; music.setName("沉默是金") ; return music; } @Override public void uploadMusic(Music music) throws RemoteException { // TODO Auto-generated method stub Log.d("marttinli", "uploadMusic music:"+music.getName()); }} }

    客户端。客户端的Music.java,Music.aidl,RemoteMusic.aidl,这三个文件请从服务器端copy过来,连外面的包也一起copy过来,确保一模一样。

    客户端绑定服务和发送请求的代码就不贴了,第三点就已经说过了。

    这里有几个点要注意的:

    1,Music.java中注释也写了,在序列化时,想要进行序列号传递的实体类内部一定要声明CREATOR 常量。常量名只能是CREATOR,类型也必须是 Parcelable.Creator<T> 

    2,Music.java在序列化时,根据序列号的Parcel对象,反序列号为原本的实体对象 读出顺序要和writeToParcel的写入顺序相同 

    3,标识符in,out,inout,类似c语言里的用法,in表示输入参数,这个很好理解。out表示该参数做为输入,也是作为输出;比如change(int value),输入value=1,传到service后,service重新复制value=2,那么在客户端这个value==2.

    4,Music.java,Music.aidl也要名字一样,后缀不一样而已。

    四,aidl实现数据监听

    有个问题就是,目前我们已经实现了客户端对服务端发送指令和获取数据,但有一种情况:当你发送指令后,服务在处理数据要执行耗时操作才能返回数据。对,这个时候你应该很容易可以想到再定义一个接收数据的接口,只要服务端处理完数据调用该接口方法,在客户端实现的方法就可以获取到对应数据。

    实现步骤(这里接着上面的代码写):

    1,再定义一个RemoteMusicCallback.aidl,顾名思义,这个是服务端执行完耗时操作后对客户端的callback。这里的标识符一定是in,至于原因,稍后会讲到。

    interface RemoteMusicCallback{ void onChanged(in Music music); }2,既然有了Callback,那么必须要声明注册监听和反注册的方法,这里放在原来的RemoteMusic.aidl中是最合适的。那么RemoteMusic.adil变为:

    interface RemoteMusic{ Music getMusic(); void uploadMusic(in Music music); void registerMusic(in RemoteMusicCallback callback); void unRegisterMusic(in RemoteMusicCallback callback); }

    3,然后把Music.java,Music.aidl,RemoteMusic.aidl,RemoteMusicCallback.aidl,连同包一起复制到客户端和服务端。 客户端:

    加入注册music变化的callback:

    RemoteMusicCallback mRemoteMusicCallback = new RemoteMusicCallback.Stub() { @Override public void onChanged(Music music) throws RemoteException { // TODO Auto-generated method stub System.out.println("music:"+music.getName()); } };

    @Override public void onServiceConnected(ComponentName name, IBinder service) { // TODO Auto-generated method stub Log.d("marttinli","connected successful"); mService = RemoteMusic.Stub.asInterface(service); try { mService.registerMusic(mRemoteMusicCallback); } catch (RemoteException e) { // TODO Auto-generated catch block e.printStackTrace(); } } 在取消ServiceConnection绑定的时候先取消注册:

    private void unBindService(){ if (mService!=null) { if (mRemoteMusicCallback!=null&& mRemoteMusicCallback.asBinder().isBinderAlive()) { try { mService.unRegisterMusic(mRemoteMusicCallback); } catch (RemoteException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if (mConnection!=null) { unbindService(mConnection); } } }

    服务端:

    这里有一点要注意的,我们是提供service的,谁注册监听我的数据变化,我就要给谁提供服务。那么必然存在一种可能,很多应用同时监听我的服务,那么这时我就需要把他们都按顺序存放在一个队列里,等到他们监听的服务变化时,我就给他们一一推送过去。这里我用ArrayList来存放队列,在MyBinder下实现队列的添加和删除:

    private class MyBinder extends RemoteMusic.Stub{ public Music getMusic() throws RemoteException { Music music = new Music(); music.setId(1) ; music.setName("沉默是金") ; return music; } @Override public void uploadMusic(Music music) throws RemoteException { // TODO Auto-generated method stub Log.d("marttinli", "uploadMusic music:"+music.getName()); } @Override public void registerMusic(RemoteMusicCallback callback) throws RemoteException { // TODO Auto-generated method stub mCallbacks.add(callback); notifyListeners();//这是一个段伪代码 } @Override public void unRegisterMusic(RemoteMusicCallback callback) throws RemoteException { // TODO Auto-generated method stub mCallbacks.remove(callback); }}

    notifyListeners模拟数据发送:

    private void notifyListeners() throws RemoteException{ new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub int i = 0; for (int j = 0; j < 10; j++) { for (RemoteMusicCallback callback : mCallbacks) { Music music = new Music(); music.setId(i); music.setName("沉默是金 "+i); try { callback.onChanged(music); } catch (RemoteException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } try { Thread.sleep(3000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } i++; } } }).start(); } 有没有发现,这样就实现了对服务端数据的监听。

    在实际开发中实现对service的数据监听经常有这个需求,service通常用来处理一类数据,你通过service获取你想要的数据,也可以监听service数据,一旦某条你监听的数据发生变化,service就会根据注册监听的队列对象一一推送该数据过去。而你只要在监听实现中获取数据。

    不知道你有没有发现,aidl里面定义的都是interface,顾名思义,aidl就是定义的接口,一个供不同进程连接的接口。他们会在gen目录下生成对应的.java文件,也是接口,是继承IInterface的接口;因为需要通过Binder跨进程传输的接口都是需要继承IInterface的。

    五,aidl与Messager在实现进程通讯上的区别

    一般,你只有在客户端需要访问另一进程的 Service ,且需要 Service 多纯种处理客户端请求时才有必要使用 AIDL;如果只需要在进程内和 Service 通信,只需要实现 Binder 通过 onBind() 返回对象;如果需要进程间通信但不需要并发处理请求,可考虑使用 Messenger,Messenger 底层实现和 AIDL 类似,上层采用 handler-message 方式通信,更为简单易用(具体请参考上文)。

    转载请注明原文地址: https://ju.6miu.com/read-1309803.html
    最新回复(0)