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接收处理数据包。

时间: 2024-10-17 13:09:17

AIDL Service Android进程间通信机制的相关文章

AIDL实现Android进程间通信实例

实现Android进程间通信实例有好几种方法: 1,AIDL 2,Broadcast 3,Intent 4,Content Provider 记录一下AIDL实现Android进程间通信的方式: Android的AIDL进程间通信实现步骤: 一:创建AIDL服务端:aidl文件,自动生成的接口java文件,服务service xxxInterface.aidl  (远程服务的接口定义) package com.example.aidltest; interface IMyAidlInterfac

初识Android进程间通信之----Binder机制

[转载请注明出处:http://blog.csdn.net/feiduclear_up/article/details/51405967 CSDN废墟的树] 前言 前面两篇博客分别介绍了Android进程间通信之AIDL的使用,以及使用AIDL传递复杂对象以及Bitmap对象.所谓AIDL:Android Interface Definition Language,是一种Android接口定义语言,用于编写Android进程间通信代码.也就是说AIDL只是一个实现进程间通信的一个工具,真正实现A

关于Android中的四大组件(AIDL Service的使用)

跨进程调用Service(AIDL Service) Android系统中的进程之间不能共享内存,因此,需要提供一些机制在不同进程之间进行数据通信. 在前一篇文章(关于Android中的四大组件(Service的开启与关闭))中介绍了开发人员如何定制自己的服务,但这些 服务并不能被其它的应用程序访问,为了使其它的应用程序也可以访问本应用程序提供的服务,Android系统采用了 远程过程调用(Remote Procedure Call,RPC)方式来实现.与很多其它的基于RPC的解决方案一样,An

Android进程间通信(IPC)的AIDL机制:Hello World示例

Android实现IPC可使用Android本身提供的AIDL机制.网上也有很多相关文章,但写的过于繁琐和麻烦,重点也不突出.本文抽丝剥茧从工程角度给出一个最简单的Android AIDL例程关键代码,以最简单的形式说明如何在代码中使用Android AIDL. AIDL首先在逻辑上可分为"服务端"和"客户端".在本示例中,则以两个完全不同.互相独立的Eclipse 项目代表. (1)server.一个Android App,即AIDL的服务端服务提供者. (2)c

浅谈Service Manager成为Android进程间通信(IPC)机制Binder守护进程之路

文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6621566 上一篇文章Android进程间通信(IPC)机制Binder简要介绍和学习计划简要介绍了Android系统进程间通信机制Binder的总体架构,它由Client.Server.Service Manager和驱动程序Binder四个组件构成.本文着重介绍组件Service Manager,它是整个Binder机制的守护进程,用来管理

Android IPC机制(三)在Android Studio中使用AIDL实现跨进程方法调用

在上一篇文章Android IPC机制(二)用Messenger进行进程间通信中我们介绍了使用Messenger来进行进程间通信的方法,但是我们能发现Messenger是以串行的方式来处理客户端发来的信息,如果有大量的消息发到服务端,服务端仍然一个一个的处理再响应客户端显然是不合适的.另外,Messenger用来进程间进行数据传递但是却不能满足跨进程的方法调用,接下来我们来使用AIDL来实现跨进程方法调用,此前我们都是用Eclipse来实现的,这次我们看看在Android Studio中使用AI

Android进程间通信之----Aidl传递对象

转载请注明出处 CSDN废墟的树 前言 有关Android进程间通信之Aidl编程的基本使用步骤已经在上一篇博客中有讲解,Android studio 下的aidl编程实现Android的夸进程间通信.上一篇博客中只是演示了怎么利用Aidl实现跨进程间传递Java基本类型,以及Aidl传递Bitamap对象.可能在一些场景下你需要跨进程传递一个对象,那么Aidl是否能传递一个对象呢?答案是肯定的,网上也有很多相关的资料,之所以写这篇博客:一是当作自己学习笔记咯,二是把自己遇到的问题分享出来. A

谈谈Android Binder机制及AIDL使用

Binder原理 1.概述 Android系统中,涉及到多进程间的通信底层都是依赖于Binder IPC机制.例如当进 程A中的Activity要向进程B中的Service通信,这便需要依赖于Binder IPC.不仅于 此,整个Android系统架构中,大量采用了Binder机制作为IPC(进程间通信, Interprocess Communication)方案. 当然也存在部分其他的IPC方式,如管道.SystemV.Socket等.那么Android为什 么不使用这些原有的技术,而是要使开

Android进程间通信(IPC)机制Binder简介和学习计划

在Android系统,每个应用程序是由多个Activity和Service部件,这些Activity和Service有可能在相同的处理被执行,此外,还可以在不同的过程中进行. 然后.不是在同一个过程Activity或者Service是怎样通信的呢?这就是本文中要介绍的Binder进程间通信机制了. 我们知道,Android系统是基于Linux内核的,而Linux内核继承和兼容了丰富的Unix系统进程间通信(IPC)机制. 有传统的管道(Pipe).信号(Signal)和跟踪(Trace).这三项