Android中的IPC机制(2)-Binder机制(下)

接着上篇binder简要介绍(见 http://blog.csdn.net/u014315849/article/details/50517121 ),我们来分析binder机制的设计。binder主要框架分为三个部分:服务端,binder驱动,客户端。

binder在Android最常见的使用场景就是一个程序的activity与系统service进行交互。比如我通过wifi的service来获取wifi控制代理对象,来对wifi进行相关的操作。注意:这里的系统service是指System server,而不是sdk 中的Service类。

从Linux空间上来看,Activity,系统service它们都分属不同的进程,不同进程之间的数据交换就是涉及到了IPC通信。而如果我们从开发者调用角度来看,我们看不到进程的概念。从公共对象请求代理的高度来看Binder,我们会惊异于这种设计思路。Binder相对于Activtiy,service来说是一个很低层的概念。当设计android程序涉及到IPC,我们无需考虑底层的实现细节,而去只关心怎么去获取相关服务并通信,这也使我们更专注于软件的开发,而非传统基于C/S架构去思考它们之间的数据是如何去实现交换的。

在用户空间,我们需要做的就是去请求相关有能力的服务对象,不必去了解这个通讯是如何完成。这种设计架构给不仅解决了通讯,引入了一种新的设计理念,也与java面向对象的开发思想契合在一起。这里,我们看不到binder,我们感觉就像是客户端直接身服务端请求,然后通过服务端的一个代理对象处理相关工作。Activity与service之间仿佛是一种很直接的,自然的通信。

对于android外部性空间来说,我们不知道服务对象在哪里,我们只需通过公共代理对象去请求服务。Android系统中,系统级的service都是由serviceManager来管理。借着serviceManger就可以获取service的对象引用。

先了解下serviceManger,它本身也是一个service,但它管理着系统其它的service。Framework提供了一个系统函数BinderInternal.getContextObject(),可以获取该Service对应的Binder引用。通过这个静态函数返回的ServiceManager提供的方法又可以获取其它系统Service的Binder引用。所以serviceManager是整个系统service的总管,也是系统的一个核心对象,它是开机就自启动的。其它的service都要向它进行注册并保管引用,这样保证所有的服务都可以通过servericeManger获取到引用。这种设计模式的一个好处就是仅暴露一个Binder引用,而其它的系统服务可以隐藏起来,从而有助于系统服务的扩展,以及调用系统服务的安全检查 。现在我们来看下serviceManger是怎样处理service的注册和查询的。我先看下serviceManger源码:

在源码里有一个HashMap,HashMap里保存着系统service的名字,和引用。

private static HashMap<String, IBinder> sCache = new HashMap<String, IBinder>();  

继续看源码了解客户端向服务的请求是怎样完成的:在客户端通过ServiceManager类getService(String name)方法传递一个需要的服务,然后ServcieManager在sCache的HashMap中去查询与name对应的service,然后再将这个service的IBinder引用返回给客户端。

  1. public static IBinder getService(String name) {  
  2.          try {  
  3.              IBinder service = sCache.get(name);  
  4.              if (service != null) {  
  5.                  return service;  
  6.              } else {  
  7.                  return getIServiceManager().getService(name);  
  8.              }  
  9.          } catch (RemoteException e) {  
  10.              Log.e(TAG, "error in getService", e);  
  11.          }  
  12.          return null;  
  13.      }  


与此同时,ServiceManager还提供了addService,checkService两个重要方法,用来维护sCache列表登记的Service的名称以及引用。我们用一个图来描述这整个过程

通过上面的分析了解:客户端首先需要通过Binder的进程都需要先获得ServiceManager代理对象才能进行Binder通讯。所以,ServiceManager在C/C++层面提供服务代理,又在Java层面提供服务代理。

接着我们通过一个简单跨进程通讯Demo来加深对Binder的了解和使用。我们分三步:

1.设计服务端,新建 一个基于Binder的类。

2.设计客户端,获取远程Binder对象。

3.操作获取的Binder对象执行操作。

创建Service端

我们只要基于Binder类新建一个服务类即可。在设计这个客户端之前,我们要考虑两个问题:

a.客户端如何获得服务端的Binder的引用。

b.客户端和服务端必须事先约定好 服务端函数的参数在包裹中的顺序。

对于第一个问题,我们向一个本地的service类进行连接,在这个客户端与servcie建立连接后返回一个Binder,即我们设计的服务端。

对于第二个问题,Android中SDK中为我们提供了一个aidl工具,借着这个工具我们可以将aidl文件上转化为一个java类文件,在该java文件中,同时重载了transact和onTransact()方法,统一了存入包裹和读取包裹参数。

设计服务端

先看下工程目录,了解整个代码的构成

1)、创建一个IAidlBinder服务,这个服务里有两个服务函数,getInfo(),getFruit(),这里我们就编写一个IAidlBinder文件,代码如下:

  1. package com.binderserver;  
  2.   
  3. import com.binderserver.Fruit;  
  4. interface IAidlBinder{  
  5.     String getInfo();  
  6.     Fruit getFruit();  
  7. }  
1.java原子类型,如int,long,String等变量。
2.Binder引用。
3.实现了Parcelable的对象。
这里文件名称第一个"I"的含义是IIterface类,即这是一个可以提供远程服务的类。我们创建好文件后,aidi工具会以文件的名称在gen目录下生成一个java类。
接着看看aidl生成的IAidlBinder.java代码。
linux


这些代码主要完成三个任务:

1.定义一个Java interface,内部包含aidl文件声明的服务函数,类名称为IAidlBinder,该类基于IIterface接口,即需要提供一个asBinder()函数。

2.定义一个Proxy类,该类将作为客户端访问服务端的代理。这里代理主要任务就是统一包裹内写入参数的顺序。

3.定义一个stub类,这是一个基于Binder的abstract类。该类实现了IAidlBinde接口,主要由服务端来使用。

2)、 因为在这个接口里传输的有对象Fruit,所以要它要处理Paracelable,下面是它的源码。

  1. package com.binderserver;  
  2.   
  3. import Android.os.Parcel;  
  4. import android.os.Parcelable;  
  5.   
  6. public class Fruit implements Parcelable {  
  7.   
  8.     private String name;  
  9.     private String color;  
  10.     private int number;  
  11.       
  12.       
  13.     public String getName() {  
  14.         return name;  
  15.     }  
  16.   
  17.     public void setName(String name) {  
  18.         this.name = name;  
  19.     }  
  20.   
  21.     public String getColor() {  
  22.         return color;  
  23.     }  
  24.   
  25.     public void setColor(String color) {  
  26.         this.color = color;  
  27.     }  
  28.   
  29.     public int getNumber() {  
  30.         return number;  
  31.     }  
  32.   
  33.     public void setNumber(int number) {  
  34.         this.number = number;  
  35.     }  
  36.       
  37.     public static final Parcelable.Creator<Fruit> CREATOR = new Creator<Fruit>() {  
  38.   
  39.         @Override  
  40.         public Fruit createFromParcel(Parcel source) {  
  41.             Fruit fruit = new Fruit();  
  42.             fruit.name = source.readString();  
  43.             fruit.color = source.readString();  
  44.             fruit.number = source.readInt();  
  45.             return fruit;  
  46.         }  
  47.   
  48.         @Override  
  49.         public Fruit[] newArray(int size) {  
  50.             // TODO Auto-generated method stub   
  51.             return new Fruit[size];  
  52.         }  
  53.           
  54.     };  
  55.   
  56.     @Override  
  57.     public int describeContents() {  
  58.         // TODO Auto-generated method stub   
  59.         return 0;  
  60.     }  
  61.   
  62.     @Override  
  63.     public void writeToParcel(Parcel dest, int flags) {  
  64.         dest.writeString(name);  
  65.         dest.writeString(color);  
  66.         dest.writeInt(number);  
  67.           
  68.     }  
  69.   
  70. }  

3)、创建一个名为Book的aidl文件,代码如下:

parcelable Fruit;  

4)、新建一个名为ServerService的java文件,

  1. package com.binderserver;  
  2.   
  3. import android.app.Service;  
  4. import android.content.Intent;  
  5. import android.os.IBinder;  
  6. import android.os.RemoteException;  
  7.   
  8. import com.binderserver.IAidlBinder.Stub;  
  9.   
  10. public class ServerService extends Service {  
  11.   
  12.     private Fruit mFruit;  
  13.       
  14.     @Override  
  15.     public void onCreate() {  
  16.         // TODO Auto-generated method stub   
  17.         super.onCreate();  
  18.         mFruit = new Fruit();  
  19.         mFruit.setName("apple");  
  20.         mFruit.setColor("red");  
  21.         mFruit.setNumber(10);  
  22.     }  
  23.   
  24.     @Override  
  25.     public IBinder onBind(Intent intent) {  
  26.         // TODO Auto-generated method stub   
  27.         return serviceBinder;  
  28.     }  
  29.   
  30.     private IAidlBinder.Stub serviceBinder = new Stub() {  
  31.           
  32.         @Override  
  33.         public String getInfo() throws RemoteException {  
  34.             // TODO Auto-generated method stub   
  35.             return "I'm a server";  
  36.         }  
  37.           
  38.         @Override  
  39.         public Fruit getFruit() throws RemoteException {  
  40.             // TODO Auto-generated method stub   
  41.             return mFruit;  
  42.         }  
  43.     };  
  44.       
  45. }  

5)、将这个服务端运行起来,供客户端调用,界面很简单,如下 :

客户端的设计

客户端的工程目录如下 :

客户端的实现:

1)、将服务端的的aidl文件及要被调用的类,直接拷贝到工程目录下。注意:为了客户端调用远程服务,不要改变原aidl文件的地址,不然会报错:

Binder invocation to an incorrect interface

2)、创建主Activity,用来调用Aidl服务。代码如下:

  1. package com.binderclient;  
  2.   
  3. import Android.app.Activity;  
  4. import android.content.ComponentName;  
  5. import android.content.Context;  
  6. import android.content.Intent;  
  7. import android.content.ServiceConnection;  
  8. import android.os.Bundle;  
  9. import android.os.IBinder;  
  10. import android.os.RemoteException;  
  11. import android.util.Log;  
  12. import android.view.View;  
  13. import android.view.View.OnClickListener;  
  14. import android.widget.Button;  
  15. import com.binderserver.IAidlBinder;;  
  16.   
  17. public class BinderClientActivity extends Activity implements OnClickListener {  
  18.     /** Called when the activity is first created. */  
  19.     private IAidlBinder binder;  
  20.     private Button mGetInfo;  
  21.     @Override  
  22.     public void onCreate(Bundle savedInstanceState) {  
  23.         super.onCreate(savedInstanceState);  
  24.         setContentView(R.layout.main);  
  25.         mGetInfo = (Button) findViewById(R.id.getinfo);  
  26.         mGetInfo.setOnClickListener(this);  
  27.     //注意这里intent要在ServerService进行静态注册。   
  28.         Intent intent = new Intent("com.binderserver.ServerService");  
  29.         bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);  
  30.     }  
  31.       
  32.     private ServiceConnection serviceConnection = new ServiceConnection(){  
  33.   
  34.         @Override  
  35.         public void onServiceConnected(ComponentName name, IBinder service) {  
  36.             binder = IAidlBinder.Stub.asInterface(service);  
  37.               
  38.         }  
  39.   
  40.         @Override  
  41.         public void onServiceDisconnected(ComponentName name) {  
  42.             binder = null;  
  43.         }  
  44.           
  45.     };  
  46.     @Override  
  47.     public void onClick(View v) {  
  48.         switch (v.getId()) {  
  49.         case R.id.getinfo:  
  50.             if(binder == null){  
  51.                 Log.d("Lawrence""@#$%^&*()_+!#$%^&*_!@#$%^&*()");  
  52.             }else {  
  53.                 try {  
  54.                     String print = "The name of this fruit is:   " + binder.getFruit().getName() + "\n"  
  55.                             + "The color of this fruit is:   " + binder.getFruit().getColor() + "\n"  
  56.                             + "The number of this fruit is:   " + binder.getFruit().getNumber() + "\n"  
  57.                             + "The server says:   " + binder.getInfo();  
  58.                     mGetInfo.setText(print);  
  59.                 } catch (RemoteException e) {  
  60.                     // TODO Auto-generated catch block   
  61.                     e.printStackTrace();  
  62.                 }  
  63.             }  
  64.             break;  
  65.   
  66.         default:  
  67.             break;  
  68.         }  
  69.           
  70.     }  
  71. }  

3)、运行工程,界面如下:

4)、点击getInfo按钮,调用远程服务。界面如下 :

上面就是一个以aidl为例的远程Binder调用的简单设计。

附:Demo源码地址:

免费下载地址在 http://linux.linuxidc.com/

用户名与密码都是www.linuxidc.com

具体下载目录在 /2012年资料/7月/24日/Android之Binder设计分析/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值