AIDL Service Android进程间通信机制
转载出处:http://www.apkbus.com/home.php?mod=space&do=blog&uid=664680&id=59465
我们知道,在Android app中的众多activity,service等组件可以运行在同一进程中,也可以运行在不同进程中。当组件运行在同一进程中进行通信就显得比较简单,在之前的Android线程间通信机制中已经讲过了;而当它们运行在不同的进程 中时,就需要使用我们本文中所要介绍的Binder机制了。
Binder作为一种进程间通信机制,负责提供远程调用的功能(RPC),它的系统组件主要包括四种:Client, Server, ServiceManager, Binder Driver. 它们之间的关系如下图所示:
从图中我们可以看出,Client, Server, ServiceManager运行在系统的用户态,而Binder Driver运行在内核态。为了完成Client端到Server端的通信任务,用户空间的需要操作Binder Driver提供的/dev/binder文件来完成交互。那么ServiceManager的工作是什么呢?ServiceManager负责管理Server并向Client端提供一个Server的代理接口(proxy)。通过代理接口中定义的方法,Client端就可以使用Server端提供的服务了。整个过程如下:
Client端调用代理接口的方法,将Client的参数打包为parcel对象发送给内核空间中BinderDriver;
Server端读取到BinderDriver中的请求数据,将parcel对象解包并处理;
处理好后,将处理结果打包返回给BinderDriver,再交给Client端。
另外,Client端与Server端的调用过程是同步的,即在Server返回结果之前,Client端是阻塞的。调用过程如下所示:
OK,下面我们通过AIDL的一个例子来分析一下以上过程时如何进行的。
AIDL小栗子
首先,创建一个aidl文件“ICalculateAIDL.aidl”,这个接口里面定义了要对外提供的服务,我们这里定义了计算加法和减法的函数:
package com.cqumonk.calculate.aidl; interface ICalculateAIDL {
int add(int a,int b);
int minus(int a,int b);
}
创建好后,rebuild一下我们的项目,可以生成同名的java文件。这是一个接口,里面包含了一个名为Stub的静态抽象类,以及我们在aidl文件中定义的加减法函数。 里面详细代码在后面分析,接口文件结构如下:
然后我们需要实现服务端方面的功能,创建一个service,我们顺手把生命周期中各方法都打印出来,并完成加法和减法函数的实现:
public class CalculateService extends Service {
private static final String TAG="SERVER";
public CalculateService() {
}
@Override
public void onCreate() {
super.onCreate();
Log.e(TAG,"OnCreate");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e(TAG,"onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Override
public IBinder onBind(Intent intent) {
Log.e(TAG,"onBind");
return mBinder;
}
@Override
public void onDestroy() {
super.onDestroy();
Log.e(TAG,"onDestroy");
}
@Override
public boolean onUnbind(Intent intent) {
Log.e(TAG,"onUnbind");
return super.onUnbind(intent);
}
@Override
public void onRebind(Intent intent) {
Log.e(TAG,"onRebind");
super.onRebind(intent);
}
private final ICalculateAIDL.Stub mBinder=new ICalculateAIDL.Stub(){
@Override
public int add(int a, int b) throws RemoteException {
return a+b;
}
@Override
public int minus(int a, int b) throws RemoteException {
return a-b;
}
};
}
在service中,我们使用刚刚生成的ICalculateAIDL.Stub静态抽象类创建了一个Binder对象,实现了其中有关计算的业务方法,并在onBind方法中返回它。另外我们还需要在manifest文件中对该服务进行注册:
<service
android:name=".CalculateService"
android:enabled="true"
android:exported="true" >
<intent-filter>
<action android:name="com.cqumonk.adil.calculate"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</service>
这时候,我们的服务端工作就完成了。我们开始编写客户端的代码来调用这个服务。首先,我们定义一个activity,包含四个按钮:
然后我们可以在activity中启动之前定义好的service并调用它所提供的服务:
public class MainActivity extends Activity implements View.OnClickListener {
Button mBind;
Button mUnbind;
Button mAdd;
Button mMinus;
TextView txt_res;
private static final String TAG="CLIENT";
private ICalculateAIDL mCalculateAIDL;
private boolean binded=false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mBind= (Button) findViewById(R.id.btn_bind);
mUnbind= (Button) findViewById(R.id.btn_unbind);
mAdd= (Button) findViewById(R.id.btn_add);
mMinus= (Button) findViewById(R.id.btn_minus);
txt_res= (TextView) findViewById(R.id.txt_res);
mBind.setOnClickListener(this);
mUnbind.setOnClickListener(this);
mAdd.setOnClickListener(this);
mMinus.setOnClickListener(this);
}
@Override
protected void onStop() {
super.onStop();
unbind();
}
private void unbind(){
if (binded){
unbindService(mConnection);
binded=false;
}
}
@Override
public void onClick(View v) {
int id=v.getId();
switch (id){
case R.id.btn_bind:
Intent intent=new Intent();
intent.setAction("com.cqumonk.adil.calculate");
bindService(intent,mConnection, Context.BIND_AUTO_CREATE);
break;
case R.id.btn_unbind:
unbind();
break;
case R.id.btn_add:
if(mCalculateAIDL!=null){
try {
int res=mCalculateAIDL.add(3,3);
txt_res.setText(res+"");
} catch (RemoteException e) {
e.printStackTrace();
}
}else{
Toast.makeText(this,"please rebind",Toast.LENGTH_SHORT).show();
}
break;
case R.id.btn_minus:
if(mCalculateAIDL!=null){
try {
int res=mCalculateAIDL.minus(9,4);
txt_res.setText(res+"");
}catch (RemoteException e) {
e.printStackTrace();
}
}else{
Toast.makeText(this,"please rebind",Toast.LENGTH_SHORT).show();
}
break;
}
}
private ServiceConnection mConnection=new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.e(TAG,"connect");
binded=true;
mCalculateAIDL=ICalculateAIDL.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.e(TAG,"disconnect");
mCalculateAIDL=null;
binded=false;
}
};
}
当我们点击绑定按钮,观察日志:
点击加法和减法按钮,都可以完成计算并返回值。然后点击解绑按钮:
我们并未发现有连接断开的日志打印,这时候,我们继续点击加减法按钮,发现仍然可以完成计算功能,这又是为毛呢?我们看到unbind和destroy的日志打印,说明连接已经断开,service已经被销毁,但是我们返回的stub对象仍然是可以继续使用的。而并不是说service仍然在运行。
过程分析
首先,我们调用bindservice方法来启动了service:
一方面连接成功时调用了serviceConnection的onServiceConnected方法。我们从该方法中获取到一个Binder对象(注意,这个binder并不是我们在service中实现的那个哦。当service时本apk中的service时,这里返回的是同一个binder),我们通过此binder来与server进行通信。为了区分,我们称为clientBinder。
public void onServiceConnected(ComponentName name, IBinder service) {
Log.e(TAG,"connect");
binded=true;
mCalculateAIDL= ICalculateAIDL.Stub.asInterface(service);
}
另一方面,onBind方法返回了我们实现的Stub对象,其实也是一个Binder,用于和Client进行通信。 (之前我们定义了一个aidl接口文件,并根据它生成了ICalculateAIDL接口。这个接口中有我们定义的两个方法以及一个 静态抽象内部类Stub,它是一个Binder的子类。)我们来看一下Stub是如何定义的:
public static abstract class Stub extends android.os.Binder implements com.cqumonk.calculate.aidl.ICalculateAIDL
它继承了Binder类也实现了ICalculateAIDL接口, 我们实现了定义的add和minus方法。我们仍然要强调一下它并不是clientBinder,在负责与clientBinder进行通信交互的同时,它也维护了service描述符与服务端service的映射。
我们在Client中的onServiceConnected里调用了stub对象的asInterface方法,并将之前得到的clientBinder传入:
public static com.cqumonk.calculate.aidl.ICalculateAIDL asInterface(android.os.IBinder obj) { if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);//根据包名获取本地实现的一个接口的实例,如果是本地service则可以获取到
if (((iin != null) && (iin instanceof com.cqumonk.calculate.aidl.ICalculateAIDL))) { return ((com.cqumonk.calculate.aidl.ICalculateAIDL) iin); //如果得到的实例是ICalculateAIDL的对象,则返回
}
return new com.cqumonk.calculate.aidl.ICalculateAIDL.Stub.Proxy(obj);//如果无法得到本地实现的对象则会返回一个代理对象}
在这个方法中,首先在系统中查找注册的的service,如果没有找到,那么一定是别的apk实现的service,于是返回一个此service的静态代理类对象供Client调用。 我们来看一下这个代理,创建时我们将clientBinder传入了,同时也它实现了ICalculateAIDL接口:
private static class Proxy implements com.cqumonk.calculate.aidl.ICalculateAIDL {
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 a, int b) 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(a); //将参数打包
_data.writeInt(b);
mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0); //调用binderDriver的提供的方法将参数发给服务端
_reply.readException();
_result = _reply.readInt(); //读取到返回结果
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public int minus(int a, int b) 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(a);
_data.writeInt(b);
mRemote.transact(Stub.TRANSACTION_minus, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
代理中也实现了ICalculateAIDL接口定义的方法,我们以add方法为例,里面将参数打包发送给Server端。在Server端收到请求后,会调用service中我们实现的那个stub对象(mBinder)的onTransact方法:
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_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_minus: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.minus(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
调用完成后把结果打包返回给Poxy处理,最后返回给客户端。
总结
由上面的例子我们可以看出,在跨进程通信的时候,Client端使用的Poxy里面封装了一个binder与Server端的stub(也是一个binder对象)进行交互,两个binder作为接口调用BinderDriver的transact来发送数据包,以及onTransact接收处理数据包。
AIDL Service Android进程间通信机制的更多相关文章
- 浅谈Service Manager成为Android进程间通信(IPC)机制Binder守护进程之路
文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6621566 上一篇文章Android进程间通信 ...
- android 进程间通信 messenger 是什么 binder 跟 aidl 区别 intent 进程间 通讯? android 消息机制 进程间 android 进程间 可以用 handler么 messenger 与 handler 机制 messenger 机制 是不是 就是 handler 机制 或 , 是不是就是 消息机制 android messenge
韩梦飞沙 韩亚飞 313134555@qq.com yue31313 han_meng_fei_sha messenger 是什么 binder 跟 aidl 区别 intent 进程间 通讯 ...
- Android进程间通信(一):AIDL使用详解
一.概述 AIDL是Android Interface Definition Language的缩写,即Android接口定义语言.它是Android的进程间通信比较常用的一种方式. Android中 ...
- Android IPC机制(三)在Android Studio中使用AIDL实现跨进程方法调用
在上一篇文章Android IPC机制(二)用Messenger进行进程间通信中我们介绍了使用Messenger来进行进程间通信的方法.可是我们能发现Messenger是以串行的方式来处理client ...
- [转]Android进程间通信--消息机制及IPC机制实现
Android为了屏蔽进程的概念,利用不同的组件[Activity.Service]来表示进程之间的通信! 组件间通信的核心机制是Intent,通过Intent可以开启一个Activity或Servi ...
- Android 进程间通信——AIDL
代码地址如下:http://www.demodashi.com/demo/12321.html 原文地址:http://blog.csdn.net/vnanyesheshou/article/deta ...
- Android IPC机制之AIDL
什么是AIDL AIDL:Android Interface Definition Language,即Android接口定义语言. Android系统中的进程之间不能共享内存,因此,需要提供一些机制 ...
- android进程间通信:使用AIDL
android 的binder其实是基于 openbinder实现的,openbinder的地址:http://www.angryredplanet.com/~hackbod/openbinder/d ...
- Android进程间通信(IPC)机制Binder简要介绍和学习计划
文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6618363 在Android系统中,每一个应用 ...
随机推荐
- C# 展开和折叠代码的快捷键(总结)
C# 展开和折叠代码的快捷键 VS2005代码编辑器的展开和折叠代码确实很方便和实用.以下是展开代码和折叠代码所用到的快捷键,很常用: Ctrl + M + O: 折叠所有方法 Ctrl + M + ...
- 封装定制的Kali Live ISO
打造专属的Kali ISO – 简介 封装定制的Kali ISO很简单,很有趣,很有意义.你可以用Debian的live-build脚本对Kali ISO进行全面的配置.这些脚本以一系列配置文件的方式 ...
- 强引用strong和弱引用weak的定义
1.强引用表示从属关系,引用对象拥有被引用的对象.弱引用则暗示引用对象不拥有被引用的对象.一个对象的寿命是由它被强引用多少次来决定的.只要对象还存在强引用,就不会释放该对象. 注意:但是对象之间的引用 ...
- linux命令:touch
1:命令介绍: touch用来创建文件或修改文件和目录的时间戳,包括存取时间和更改时间. 2:命令格式: touch [选项] 文件 3:命令参数: -a 或--time=atime或--time ...
- H5实现俄罗斯方块(一)
这几天一直忙于公司的项目,涉及到流程问题,(有时间会写成博客的)...一直没有更新... 为了更加巩固js的基础,自己封装类似于JQuery的操作库来对dom进行操作. 一:前度页面的绘制. < ...
- 转:超链接a标签display属性的block和inline-block的用法说明
我们经常在设计网站的导航部分的时候,如果想让导航超链接hover显示背景,但稍不注意,默认的inline会让你抓狂,因为display:inline会将超链接显示为内联元素,即没有宽和高的作用效果,这 ...
- php 在客户端生成txt文件并且下载
在访问php时生成txt文件 $filename = 'file.text'; //也可以是其他后缀格式的 $ua = $_SERVER["HTTP_USER_AGENT"]; f ...
- 蓝牙(Profile)构成
蓝牙剖面(Profile)构成 蓝牙剖面(Profile)及其相互关系 蓝牙SIG定义13种蓝牙剖面及其相互关系如下图: 一个剖面若直接或间接采用了另一个剖面的部分或全部功能则称该剖面依赖于另一剖面. ...
- IE8 CSS hack 测试
IE8正式版出来有一段日子了,但是针对ie8正式版的CSS hack却很少,其实这是值得庆幸的,因为ie8修复了很多IE6和IE7的一些BUG,更加接近W3C标准.针对IE8正式版的CSS hack目 ...
- httpclient 发送一个请求
httpclient版本 4.1 发送一个post请求 public static JSONObject post(String url,JSONObject json){ HttpClient cl ...