AIDL的简单示例与解析
2015-08-27 10:22
621 查看
首先声明,本文参考http://blog.csdn.net/hitlion2008/article/details/9824009里面的实现示例,感觉写的已经相当的到位了,我仅仅做一个归纳与总结,感谢大神技术上的无私分享。
首先我们看一下标准的AIDL的写法:
1.我们首先要在服务器工程里面定义一个单独的包com.qs.aidl 然后是在里面我们存放了一个aidl结尾的文件,文件里面的内容为:
这个时候我们能看到gen文件夹中生成了一个相同名字的包,并且生成了一个与我们aidl文件名相同的IPerson.java文件
2.然后将我们的服务器的com.qs.aidl包直接copy到客户端工程中去,同样也生成了相同的文件
3.在服务器中创建一个Service的文件夹然后实现服务器的code
4.在client中调用BindService启动service ,关联到服务器的service,当然这里面需要借用我们的ServiceConnection类来实现,至于拉什么service是由于我们在服务器中设置了action 而客户端Intent创建的时候设置相同的action 即可拉起服务
5.在客户端远程调用服务器提供的相关api,也就是我们aidl中提供的函数
疑点,这里我们会问为什么放一个单独的包?不放里面行不行,为什么?
下面我们尝试着把这个文件直接放在下面的com.qs.aidldemo中去,然后修改一下aidl 文件中的第一行package com.qs.aidldemo;
然后修改一下我们的MainActivity的导包路径,看起来像是没什么问题,编译运行code,我们就会发现出问题了:
这里我们看到了Caused by 什么不正确的interface ,那么这个是个什么意思?那我们先打开现在的client端自动生成的code,我们发现了
这里我们明显看出来了有很大的不同了,包的路径不同,然后为什么报interface不正确呢?
然后我们在看code:
虽然不明白是什么意思,但是我们可以大概猜出来Stub是去关联了一个Interface的,并且我们看一下第二个参数就知道服务器和客户端是不是能够对接上比对的应该是DESCRIPTOR 因此当我们两边的DESCRIPTOR不一样的时候我们的interface对接不上,然后就会报出来不正确的interface,因此我们可以改变aidl文件的位置,但是我们必须服务器客户端同步改变,不然就会出问题,两个进程通信不了,至于为什么要放在单独的包里面我们通过第一张图知道我们服务器客户端包名一般是不同的,因此我们单独存放,就可以直接copy服务器的内容到客户端了,而不用考虑什么包名问题了
现在我们开始看一下相关code了,先看一下服务器的code:
AIDLService.java:
服务器的MainActivity则什么都没有:
然后是客户端的调用操作:
我们可以看到code不多,使用起来也不难,我们说这个
person = IPerson.Stub.asInterface(service);是重点,因为它最后提供了服务器的api的调用,因此让我们看一下我们这个函数做了什么事情:
接收一个IBinder对象,也就是服务器返回给我们的对象,如果我们的函数
onServiceConnected(ComponentName name, IBinder service)没有传递服务器的IBinder对象,那么我们直接返回,表示连接不上服务器,如果这个client和service在同一进程中,那么我们的直接返回当前进程的interface,自己调用自己的实现,但是如果是跨进程,我们则返回去的是一个代理对象,我们再来看看代理对象是什么玩意:
如上,我们是不是发现了什么东西,我们把服务器的IBinder对象给了mRemote,然后通过mRemote对象去调用了transact(Stub.TRANSACTION_greet, _data, _reply, 0);函数,感觉上就是在通过服务器的对象调用服务器的代码,因此我们再看看这个函数是怎么调用服务器code的:
在自动生成的code中我们发现了这样的函数,参数什么的都与我们客户端调用的函数一样,因此我们没有理由不相信其实transact调用的就是onTransact函数,那么我们怎么分辨是调用哪一个函数呢?这个时候定义的变量出场了:
在case中判断然后进入相应的处理,onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)函数中code表示的是调用那个函数的标记,data表示的是我们client传输到服务器的参数,reply表示的是服务器返回给我们的结果,flags 表示的RPC调用的类型0位默认的值,表示正常的远程调用
下面我们看一下服务器写给客户端的相关code:
首先我们从data中拿到数据,然后是调用服务器的add方法,返回一个结果,最后reply写数据给客户端,这样一整个过程就调用完毕了!
下面显示一下code运行结果,总code稍后给出:
下面看一下服务器的AndroidManifest.xml,注意action名字和我们client中设置的是一致的
暂时就写到这里吧,下面我们将自己实现一个AIDL的例子!不对的地方还请大家帮助!
示例代码:代码下载
首先我们看一下标准的AIDL的写法:
1.我们首先要在服务器工程里面定义一个单独的包com.qs.aidl 然后是在里面我们存放了一个aidl结尾的文件,文件里面的内容为:
package com.qs.aidl; interface IPerson { String greet(String someone); String add(String a,String b); }
这个时候我们能看到gen文件夹中生成了一个相同名字的包,并且生成了一个与我们aidl文件名相同的IPerson.java文件
2.然后将我们的服务器的com.qs.aidl包直接copy到客户端工程中去,同样也生成了相同的文件
3.在服务器中创建一个Service的文件夹然后实现服务器的code
4.在client中调用BindService启动service ,关联到服务器的service,当然这里面需要借用我们的ServiceConnection类来实现,至于拉什么service是由于我们在服务器中设置了action 而客户端Intent创建的时候设置相同的action 即可拉起服务
5.在客户端远程调用服务器提供的相关api,也就是我们aidl中提供的函数
疑点,这里我们会问为什么放一个单独的包?不放里面行不行,为什么?
下面我们尝试着把这个文件直接放在下面的com.qs.aidldemo中去,然后修改一下aidl 文件中的第一行package com.qs.aidldemo;
然后修改一下我们的MainActivity的导包路径,看起来像是没什么问题,编译运行code,我们就会发现出问题了:
这里我们看到了Caused by 什么不正确的interface ,那么这个是个什么意思?那我们先打开现在的client端自动生成的code,我们发现了
//客户端里面我们修改了aidl存放的位置 private static final java.lang.String DESCRIPTOR = "com.qs.aidlclientdemo.IPerson"; //服务器端的定义为: private static final java.lang.String DESCRIPTOR = "com.qs.aidl.IPerson";
这里我们明显看出来了有很大的不同了,包的路径不同,然后为什么报interface不正确呢?
然后我们在看code:
/** Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); }
虽然不明白是什么意思,但是我们可以大概猜出来Stub是去关联了一个Interface的,并且我们看一下第二个参数就知道服务器和客户端是不是能够对接上比对的应该是DESCRIPTOR 因此当我们两边的DESCRIPTOR不一样的时候我们的interface对接不上,然后就会报出来不正确的interface,因此我们可以改变aidl文件的位置,但是我们必须服务器客户端同步改变,不然就会出问题,两个进程通信不了,至于为什么要放在单独的包里面我们通过第一张图知道我们服务器客户端包名一般是不同的,因此我们单独存放,就可以直接copy服务器的内容到客户端了,而不用考虑什么包名问题了
现在我们开始看一下相关code了,先看一下服务器的code:
AIDLService.java:
public class AIDLService extends Service{ //拿到一个系统自动生成的代理类,然后实现代理类需要我们实现的两个函数 //这也就是我们服务器提供的两个接口函数,待会我们会在客户端看到他们的使用 private IPerson.Stub stub = new IPerson.Stub() { @Override public String greet(String someone) throws RemoteException { return "hello "+someone; } @Override public String add(String a, String b) throws RemoteException { int aa = Integer.parseInt(a); int bb = Integer.parseInt(b); return String.valueOf(aa+bb); } }; //这里是我们Service的重点,我们给绑定我们的客户端返回的是一个代理类 //可以看到我们还出现了接收intent参数,这个仅仅是测试client传值过来 @Override public IBinder onBind(Intent intent) { System.out.println("data from client =="+intent.getExtras().getString("time")); return stub; } //我们可以看到当client调用bindService之后我们的onCreate方法被调用了 @Override public void onCreate() { super.onCreate(); System.out.println("AIDLServerDemo service onCreate------"); } //我们可以看到当client调用onDestory,也就是直接按返回键退出之后我们的onDestory方法被调用了 @Override public void onDestroy() { // TODO Auto-generated method stub super.onDestroy(); System.out.println("AIDLServerDemo service onDestroy------"); } }
服务器的MainActivity则什么都没有:
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } }
然后是客户端的调用操作:
public class MainActivity extends Activity { private IPerson person; private MyPrinterInterface myInterface; private ServiceConnection conn = new ServiceConnection() { @Override public void onServiceDisconnected(ComponentName name) { } @Override public void onServiceConnected(ComponentName name, IBinder service) { //重点code这里去接收了服务器的代理对象 person = IPerson.Stub.asInterface(service); } }; private ServiceConnection myconn = new ServiceConnection() { @Override public void onServiceDisconnected(ComponentName name) { } @Override public void onServiceConnected(ComponentName name, IBinder service) { //重点code这里去接收了服务器的代理对象 System.out.println("myconn connected"); myInterface = MyPrinterInterface.Stub.asInterface(service); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //首先要拉起服务器的service程序然后才能调用远程服务器提供的api Intent intent = new Intent("android.intent.action.AIDLService"); String time = new Date().toLocaleString(); intent.putExtra("time", time);//这里我们还可以给服务器发送数据,在服务器的onBind函数中它的参数接收这个数据 bindService(intent, conn, Context.BIND_AUTO_CREATE); doBindService(); } public void greet(View v) { try { Toast.makeText(this,person.greet("qiusen "),0).show(); } catch (RemoteException e) { e.printStackTrace(); } } public void add(View v) { try { Toast.makeText(this,person.add("1","2"),0).show(); } catch (RemoteException e) { e.printStackTrace(); } } public void printmsg(View v) { try { if(myInterface==null){ System.out.println("myInterface is not success"); return; } myInterface.print("this is a client msg"); System.out.println("add sum is ="+myInterface.add("1", "7")); } catch (RemoteException e) { e.printStackTrace(); } } //onDestory的时候解绑服务 @Override protected void onDestroy() { super.onDestroy(); if(conn!=null) unbindService(conn); if(myconn!=null) unbindService(myconn); } //做自己服务器的绑定操作,在下面我们会看到code private void doBindService() { Intent intent = new Intent("com.qs.MyAIDLService"); bindService(intent, myconn, Context.BIND_AUTO_CREATE); } }
我们可以看到code不多,使用起来也不难,我们说这个
person = IPerson.Stub.asInterface(service);是重点,因为它最后提供了服务器的api的调用,因此让我们看一下我们这个函数做了什么事情:
/** * Cast an IBinder object into an com.qs.aidl.IPerson interface, * generating a proxy if needed. */ public static com.qs.aidl.IPerson asInterface(android.os.IBinder obj) { if ((obj==null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin!=null)&&(iin instanceof com.qs.aidl.IPerson))) { return ((com.qs.aidl.IPerson)iin); } return new com.qs.aidl.IPerson.Stub.Proxy(obj); }
接收一个IBinder对象,也就是服务器返回给我们的对象,如果我们的函数
onServiceConnected(ComponentName name, IBinder service)没有传递服务器的IBinder对象,那么我们直接返回,表示连接不上服务器,如果这个client和service在同一进程中,那么我们的直接返回当前进程的interface,自己调用自己的实现,但是如果是跨进程,我们则返回去的是一个代理对象,我们再来看看代理对象是什么玩意:
private android.os.IBinder mRemote; Proxy(android.os.IBinder remote) { mRemote = remote; } ......省略部分code..... @Override public java.lang.String greet(java.lang.String someone) 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); _data.writeString(someone); mRemote.transact(Stub.TRANSACTION_greet, _data, _reply, 0); _reply.readException(); _result = _reply.readString(); } finally { _reply.recycle(); _data.recycle(); } return _result; }
如上,我们是不是发现了什么东西,我们把服务器的IBinder对象给了mRemote,然后通过mRemote对象去调用了transact(Stub.TRANSACTION_greet, _data, _reply, 0);函数,感觉上就是在通过服务器的对象调用服务器的代码,因此我们再看看这个函数是怎么调用服务器code的:
@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_greet: { data.enforceInterface(DESCRIPTOR); java.lang.String _arg0; _arg0 = data.readString(); java.lang.String _result = this.greet(_arg0); reply.writeNoException(); reply.writeString(_result); return true; } case TRANSACTION_add: { data.enforceInterface(DESCRIPTOR); java.lang.String _arg0; _arg0 = data.readString(); java.lang.String _arg1; _arg1 = data.readString(); java.lang.String _result = this.add(_arg0, _arg1); reply.writeNoException(); reply.writeString(_result); return true; } }
在自动生成的code中我们发现了这样的函数,参数什么的都与我们客户端调用的函数一样,因此我们没有理由不相信其实transact调用的就是onTransact函数,那么我们怎么分辨是调用哪一个函数呢?这个时候定义的变量出场了:
static final int TRANSACTION_greet = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
在case中判断然后进入相应的处理,onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)函数中code表示的是调用那个函数的标记,data表示的是我们client传输到服务器的参数,reply表示的是服务器返回给我们的结果,flags 表示的RPC调用的类型0位默认的值,表示正常的远程调用
下面我们看一下服务器写给客户端的相关code:
data.enforceInterface(DESCRIPTOR); java.lang.String _arg0; _arg0 = data.readString(); java.lang.String _arg1; _arg1 = data.readString(); java.lang.String _result = this.add(_arg0, _arg1); reply.writeNoException(); reply.writeString(_result);
首先我们从data中拿到数据,然后是调用服务器的add方法,返回一个结果,最后reply写数据给客户端,这样一整个过程就调用完毕了!
下面显示一下code运行结果,总code稍后给出:
下面看一下服务器的AndroidManifest.xml,注意action名字和我们client中设置的是一致的
<service android:name="com.qs.aidldemo.AIDLService"> <intent-filter> <action android:name="android.intent.action.AIDLService"/> <category android:name="android.intent.category.DEFAULT"/> </intent-filter> </service>
暂时就写到这里吧,下面我们将自己实现一个AIDL的例子!不对的地方还请大家帮助!
示例代码:代码下载
相关文章推荐
- ipc通道入侵相关命令整理
- Linux IPC命令的用法详解
- Linux进程通信(IPC)方式简介
- 基于Android AIDL进程间通信接口使用介绍
- Android 使用【AIDL】调用外部服务的解决方法
- Android程序设计之AIDL实例详解
- Linux进程间通信(IPC)方式总结
- Android 跨进程通信基础
- ipc$ 安全
- IPC(进程间通信)几种常用的方法
- 共享内存的进程间通信库
- 详尽解析IPC$的******方法
- Android 使用 AIDL 实现进程间通信,使用基本类型作为输入和输出参数
- AIDL入门
- Messenger实现Android IPC
- Android中的信使Messenger的源码解析
- Android中的信使Messenger的源码解析
- Android深入浅出之Binder机制(一)
- Android深入浅出之Binder机制(二)
- Android Binder设计与实现 - 设计篇(一)