Android中AIDL通信机制分析
- 一、背景
·1、AIDL出现的原因
在android系统中,每一个程序都是运行在自己的进程中,进程之间无法进行通讯,为了在Android平台,一个进程通常不能访问另一个进程的内存空间,所以要想对话,需要将对象分解成操作系统可以理解的基本单元,并且有序的通过进程边界。通过代码来实现这个数据传输过程是冗长乏味的,Android提供了AIDL工具来处理这项工作,实现IPC(进行间的通信)与J2e中的RMI类似。
·2、绑定service
我看了很多人都博客都没有说到这里,其实我个人感觉AIDL就是一个远程的客户端而已,把之前BindService的中service的继承类单独拿出来放到远程客户端,在本地绑定service中我们一般是在onBind方法中将内部定义好的一个继承Binder类的对象返回给activity,在active里面获得该对象然后去调用方法对于BindService的介绍见下一篇博客。但是这里不一样我们是要去另一个进程说白点也就是另一个应用里面去获得数据,因此我们需要一些接口上的规则,来规范进程间的通信,让服务端应用进行发送,客户端应用来接受。下面说一下规则是神马?
- 二、AIDL服务端的步骤
·1. 实现.aidl文件
以eclipse开发为例,我们需要在一般是在src文件夹下,新建一个包---文件夹,然后再该文件夹目录下新建一个文件该文件的后缀名以.aidl结尾,这是必须如此的。
·2. 在文件内部首先要给出包名,
·3. 在接口中用到的数据类型,除了基本类型,String,List,Map,CharSequence,之外其他的都需要导包,即使在同一个包内也是需要导包,因此里面的类,以及方法我们都是需要导包的。
·4. 安卓ADT一般会帮我们自动将写好的aidl文件编译生成一个java语法的接口类,这个借口在gen文件下,可以打开里面默认生成了一个内部抽象类stub该类继承Binder实现了起外部接口,
·5. 定义方法名称,所有的.aidl文件已经需要传递的对象接口需要在Service 与Client中各一份,最好是直接复制过去即可。
//----------------------------------下面是提供数据访问的服务端aidl文件定义:
1 package com.example.service; 2 3 interface DataService{ 4 double getData(String arg); 5 6 }
·6. 下面就是在service的自定义类中来实现上面的aidl接口,上面已经解释了我们通过绑定service的来传递的其实是一个IBinder的对象,这里实际实现的接口是在 gen中自动生成的 DataService.java的抽象内部类 Stub,因为该类继承了Binder,而Binder则实现了IBinder接口,所以我们实际上需要的DataService.Stub的对象。
//---------------下面是服务端service中的代码,其实和绑定service的规则是一致的,
//===========目标是一致就是把一个IBinder的对象代理发到客户端,客户端接收到该对象,有了这个对象就可以调用该对象的一些方法。
1 import com.example.service.DataService; 2 3 import android.app.Service; 4 import android.content.Intent; 5 import android.os.Binder; 6 import android.os.IBinder; 7 import android.os.RemoteException; 8 9 public class MyService extends Service { 10 11 @Override 12 public IBinder onBind(Intent intent) { 13 // 这个binder就是要发送给远程客户端的对象,在建立连接后客户端得到该对象,调用该对象的方法来获取数据。 14 return binder; 15 } 16 // 还有一个需要注意的就是我们的service需要在配置文件里面进行注册,同时添加一个action的过滤器方便我们调用,绑定。 17 // 因为需要一个IBinder的对象返回而这个对象是一个接口,所以需要具体实现类的对象,Binder实现了该接口,因此我们需要创建一个Binder的对象, 18 // 又我们定义的aidl文件中的接口中被编译后会自动生成一个内部类stub,该内部类就继承了Binder 19 // 定义提供给客户端client调用的方法 20 Binder binder = new DataService.Stub() { 21 @Override 22 public double getData(String arg) throws RemoteException { 23 // TODO Auto-generated method stub 24 25 if (arg.equals("a")) { 26 return 1; 27 } else if (arg.equals("b")) { 28 return 2; 29 } 30 31 return 0; 32 } 33 }; 34 35 }
//=====================上面直接运行跑起来就可以空的activity.
// 下面说下客户端的运行
// 客户端首先是要有aidl文件的上面已经说到了,下面就是直接上代码了
//---------------首先是布局文件,简单地两个按钮
1 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 xmlns:tools="http://schemas.android.com/tools" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:paddingBottom="@dimen/activity_vertical_margin" 6 android:paddingLeft="@dimen/activity_horizontal_margin" 7 android:paddingRight="@dimen/activity_horizontal_margin" 8 android:paddingTop="@dimen/activity_vertical_margin" 9 tools:context=".MainActivity" > 10 11 <Button 12 android:id="@+id/button1" 13 android:layout_width="wrap_content" 14 android:layout_height="wrap_content" 15 android:layout_alignParentTop="true" 16 android:layout_centerHorizontal="true" 17 android:layout_marginTop="92dp" 18 android:text="绑定service" /> 19 20 <Button 21 android:id="@+id/button2" 22 android:layout_width="wrap_content" 23 android:layout_height="wrap_content" 24 android:layout_below="@+id/button1" 25 android:layout_centerHorizontal="true" 26 android:layout_marginTop="88dp" 27 android:text="进程间的通信" /> 28 29 </RelativeLayout>
下面是activity中的代码,我们要和服务端建立连接,就要在bindService方法中绑定到我们在服务端定义好的service类
Ok
1 import com.example.service.DataService; 2 3 import android.os.Bundle; 4 import android.os.IBinder; 5 import android.app.Activity; 6 import android.content.ComponentName; 7 import android.content.Context; 8 import android.content.Intent; 9 import android.content.ServiceConnection; 10 import android.view.View; 11 import android.widget.Button; 12 13 public class MainActivity extends Activity { 14 15 private Button button1, button2; 16 private DataService dataService; 17 18 @Override 19 protected void onCreate(Bundle savedInstanceState) { 20 super.onCreate(savedInstanceState); 21 setContentView(R.layout.activity_main); 22 button1 = (Button) findViewById(R.id.button1); 23 button2 = (Button) findViewById(R.id.button2); 24 25 // 绑定 26 button1.setOnClickListener(new View.OnClickListener() { 27 28 @Override 29 public void onClick(View v) { 30 // TODO Auto-generated method stub 31 Intent intent = new Intent(DataService.class.getName()); 32 // 绑定service 33 /* bindService(service, conn, flags)该方法的参数解释一下 34 * 1.service: 表示通过该参数要启动的service 35 * 2.conn: 该参数是一个ServiceConnection,该对象是用于剑听雨service之间的连接情况,当访问者与service之间 36 * 成功连接时会调用onServiceConnected(ComponentName name, IBinder service)方法,通过该方法来获取 37 * service中的IBinder对象,通过对象来获取数据。连接失败onServiceDisconnected。 38 */ 39 40 bindService(intent, connection, Context.BIND_AUTO_CREATE); 41 } 42 }); 43 44 button2.setOnClickListener(new View.OnClickListener() { 45 46 @Override 47 public void onClick(View v) { 48 // TODO Auto-generated method stub 49 try { 50 double result = dataService.getData("a"); 51 System.out.println("--->>" + result); 52 53 } catch (Exception e) { 54 // TODO: handle exception 55 e.printStackTrace(); 56 } 57 } 58 }); 59 } 60 61 // 客户端与服务进行连接 62 private ServiceConnection connection = new ServiceConnection() { 63 @Override 64 public void onServiceDisconnected(ComponentName name) { 65 // TODO Auto-generated method stub 66 67 } 68 69 @Override 70 public void onServiceConnected(ComponentName name, IBinder service) { 71 // TODO Auto-generated method stub 72 // 这些都是在API可以参考的,由SDK自动生成的DataService.Stub.asInterface的几个类 73 // 将客户端传递过来的IBinder对象,换为DataService对象, 74 // 在这里需要使用到aidl文件被编译后自动生成的内部类Stub中的一个方法asInterface,该方法的作用就是类型转换 75 dataService = DataService.Stub.asInterface(service); 76 } 77 }; 78 }
总结其实这个aidl和绑定service并没有太大的区别,只不过我们把service的继承类放在了客户端,增加了一些协议规范罢了。