转载自:http://www.linuxidc.com/Linux/2012-07/66195.htm

http://blog.csdn.net/sunxingzhesunjinbiao/article/details/42195013

我们知道,在Android系统中,每一个应用程序都运行在独立的进程中,这也保证了当其中一个程序出现异常而不会影响另一个应用程序的正常运转。在许多情况下,我们activity都会与各种系统的service打交道,很显然,我们写的程序中activity与系统service肯定不是同一个进程,但是它们之间是怎样实现通信的呢?Binder是android中一种实现进程间通信(IPC)的方式之一。

了解Binder

1.binder用来做什么?用来实现不同进程之间的通信。

2.Binder是什么?binder属于一个驱动,工作在linux层面,运行在内核态,它的操作完成是基于一段内存。所以我们开发的程序中对binder的使用都是通过系统的调用来完成的。

3.binder是怎样实现进程通信的?我们来通过Binder的架构来了解它实现进程间通信(IPC)的过程。

Binder架构由服务端,binder驱动,客户端三个部分构成。如图所示。

其中服务端,客户端处在用户空间,而binder驱动处在内核空间。

服务器端。一个Binder服务器端就是一个Binder类的对象。当创建一个Binder对象后,内部就会开启一个线程,这个线程用于接收binder驱动发送的信息,收到消息后,会执行相关的服务代码。、

Binder驱动。当服务端成功创建一个Binder对象后,Binder驱动也会相应创建一个mRemote对象,该对象的类型也是Binder类。客户就可以借助这个mRemote对象来访问远程服务。

客户端。客户端要想访问Binder的远程服务,就必须获取远程服务的Binder对象在binder驱动层对应的mRemote引用。当获取到mRemote对象的引用后,就可以调用相应Binder对象的服务了。

在这里,我们可以看到,客户端是通过Binder驱动来调用服务端的相关服务。首先,在服务端创建一个Binder对象,然后相应在Binder驱动中创建一个Binder对象,接着客户端通过获取Binder驱动中Binder对象的引用来调用服务端的服务。在Binder机制中正是借着Binder驱动将不同进程间的组件bind(粘连)在一起,实现通信。

为什么android选用Binder来实现进程间通信?

一、可靠性。在移动设备上,通常采用基于Client-Server的通信方式来实现互联网与设备间的内部通信。目前linux支持IPC包括传统的管道,System V IPC,即消息队列/共享内存/信号量,以及socket中只有socket支持Client-Server的通信方式。Android系统为开发者提供了丰富进程间通信的功能接口,媒体播放,传感器,无线传输。这些功能都由不同的server来管理。开发都只关心将自己应用程序的client与server的通信建立起来便可以使用这个服务。毫无疑问,如若在底层架设一套协议来实现Client-Server通信,增加了系统的复杂性。在资源有限的手机 上来实现这种复杂的环境,可靠性难以保证。

二、传输性能。socket主要用于跨网络的进程间通信和本机上进程间的通信,但传输效率低,开销大。消息队列和管道采用存储-转发方式,即数据先从发送方缓存区拷贝到内核开辟的一块缓存区中,然后从内核缓存区拷贝到接收方缓存区,其过程至少有两次拷贝。虽然共享内存无需拷贝,但控制复杂。比较各种IPC方式的数据拷贝次数。共享内存:0次。Binder:1次。Socket/管道/消息队列:2次。

三、安全性。Android是一个开放式的平台,所以确保应用程序安全是很重要的。Android对每一个安装应用都分配了UID/PID,其中进程的UID是可用来鉴别进程身份。传统的只能由用户在数据包里填写UID/PID,这样不可靠,容易被恶意程序利用。而我们要求由内核来添加可靠的UID。

所以,出于可靠性、传输性、安全性。android建立了一套新的进程间通信方式。

到了这里,我们对于Binder有了一些简要的理解。

后面我们会继续深入讨论Bind机制:

1.Binder的服务端是如何设计的?

2.Binder的客户端是如何设计的?

3.Binder对象是如何被获取到的?

接着上篇binder简要介绍(见 http://www.linuxidc.com/Linux/2012-07/66195.htm ),我们来分析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. import com.binderserver.Fruit;
  3. interface IAidlBinder{
  4. String getInfo();
  5. Fruit getFruit();
  6. }
1.java原子类型,如int,long,String等变量。
2.Binder引用。
3.实现了Parcelable的对象。
这里文件名称第一个"I"的含义是IIterface类,即这是一个可以提供远程服务的类。我们创建好文件后,aidi工具会以文件的名称在gen目录下生成一个java类。
接着看看aidl生成的IAidlBinder.java代码。

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

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

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

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

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

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

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

parcelable Fruit;

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

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

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

客户端的设计

客户端的工程目录如下 :

客户端的实现:

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

Binder invocation to an incorrect interface

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

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

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

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

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

附:Demo源码地址:

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

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

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

Android中的Binder机制的简要理解的更多相关文章

  1. 谈谈对Android中的消息机制的理解

    Android中的消息机制主要由Handler.MessageQueue.Looper三个类组成,他们的主要作用是 Handler负责发送.处理Message MessageQueue负责维护Mess ...

  2. Android系统进程间通信Binder机制在应用程序框架层的Java接口源代码分析

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6642463 在前面几篇文章中,我们详细介绍了A ...

  3. 探索Android中的Parcel机制(上)

    一.先从Serialize说起 我们都知道JAVA中的Serialize机制,译成串行化.序列化……,其作用是能将数据对象存入字节流其中,在须要时又一次生成对象.主要应用是利用外部存储设备保存对象状态 ...

  4. 重温Android中的消息机制

    引入: 提到Android中的消息机制,大家应该都不陌生,我们在开发中不可避免的要和它打交道.从我们开发的角度来看,Handler是Android消息机制的上层接口.我们在平时的开发中只需要和Hand ...

  5. Android深入浅出之Binder机制(转)

    Android深入浅出之Binder机制 一 说明 Android系统最常见也是初学者最难搞明白的就是Binder了,很多很多的Service就是通过Binder机制来和客户端通讯交互的.所以搞明白B ...

  6. Android中Alarm的机制

    本次给大家分析的是Android中Alarm的机制所用源码为最新的Android4.4.4.首先简单介绍如何使用Alarm并给出其工作原理,接着分析Alarm和Timer以及Handler在完成定时任 ...

  7. Android中的Parcel机制(上)

    一.先从Serialize说起 我们都知道JAVA中的Serialize机制,译成串行化.序列化--,其作用是能将数据对象存入字节流当中,在需要时重新生成对象.主要应用是利用外部存储设备保存对象状态, ...

  8. 浅析Android中的消息机制(转)

    原博客地址:http://blog.csdn.net/liuhe688/article/details/6407225 在分析Android消息机制之前,我们先来看一段代码: public class ...

  9. 浅析Android中的消息机制(转)

    在分析Android消息机制之前,我们先来看一段代码: public class MainActivity extends Activity implements View.OnClickListen ...

随机推荐

  1. HoloLens开发手记 - 应用程序模型 App model

    HoloLens使用Universal Windows Platform (UWP)提供的应用模型.UWP应用模型定义了应用如何被安全和完全地安装.更新.版本控制和移除.它管理了应用生命周期 - 应用 ...

  2. 我做PHP,但是我要批判下整天唱衰.NET的淫

    笔者每天都能看到月经贴-".NET已死"!!! 笔者之前一直在CSDN上面写博客,泡论坛,但是有约莫一年来着了发现CSDN上面的博客都没啥更新,CSDN首页推荐的一些文章也没啥新意 ...

  3. 如何实现EndNote中的PDF批量导出

    如果在EndNote数据库中已建立大量的参考文献,且每条文献都有PDF文件对应,怎样将需要的某十几条甚至几十条参考文献对应的PDF文件从数据库导出另存在新建的文件夹   1. 按住“Ctrl”键,逐条 ...

  4. Who Says What to Whom on Twitter-www2011-20160512

    分析性论文 what: who? 本文将Twitter中的用户分为了两大类--普通用户和精英用户,精英用户又被分为四类,分别为媒体(media).名人(celebrities).博主(bloggers ...

  5. mysql 安装失败解决方法

    在安装mysql的过程中,出现的最麻烦的问题和解决方法 安装后,启动不成功,就卡了,程序就没有响应. 如何解决: 找到mysql安装目录下的 #Path to the database root da ...

  6. sqlite之聚合函数的使用

    聚合函数对一组值执行计算并返回单一的值.聚合函数对一组值执行计算,并返回单个值.除了 COUNT 以外,聚合函数都会忽略空值. 聚合函数经常与 SELECT 语句的 GROUP BY 子句一起使用. ...

  7. javascript 红宝书笔记之函数、变量、参数、作用域

    ECMAScript 不介意传进来多少个参数,也不介意传进来的参数类型.     理解参数:      命名的参数只提供便利,不是必需的.     ECMAScript 的变量包含两种不同的数据类型的 ...

  8. Echarts-画柱状,折线图

    导入echarts包 <script src='../scripts/libraries/echarts/echarts-all.js'></script> 堆积图js var ...

  9. CocoaPods安装使用及上传

    1.升级Ruby环境 sudo gem update --system 如果Ruby没有安装,请参考 如何在Mac OS X上安装 Ruby运行环境 2.安装CocoaPods时我们要访问cocoap ...

  10. Xcode 修改工程名以及注意事项

    1.先把整个工程文件夹名改为新的工程名. 2.打开工程,单击,输入新的工程名,会出现,点击确定. 3.回到工程界面,在中选择 Manage Schemes,然后再弹出的对话框,把工程名改为新的名字. ...