android-IPC/Binder/D-BUS(Binder/Messager/AIDL)进程间通信(消息机制)

> 自定义Binder,服务端的onTransact(), 客户端的transact()。
AIDL、Messenger等常用的进程间通信框架都是对Binder的封装。
Android自定义Binder的使用,实现进程间通信- https://github.com/OboBear/MyBinder

-- 进程间通讯机制已经存在好多种,Corba,DCOP,COM,SOAP, XML-RPC ...

Intent对openBinder的封装,linux的open binder和D-bus, D-bus在蓝牙blueZ的应用。

- Android中的Handler的Native层研究- https://my.oschina.net/u/3863980/blog/1933086
由于Native的Handler涉及到c++以及对饮Linux系统接口的调用:
Linux中的匿名管道
epoll函数
Handler的Native层源码研究
 Android的Native源码中运用的是匿名管道

进程间的通信方式:
管道(pipe)和命名管道(FIFO)
信号(signal)
消息队列
共享内存
信号量
套接字(socket)

  管道是Linux中进行进程通信或者线程通信的一种手段之一,管道分为匿名管道(pipe)以及命名管道(named pipe),管道是内核维护的一个缓存, 它提供两个 fd, 从一个fd写入数据, 从另一个fd读出数据. 所以它是半双工的。
  epoll是Linux对于select以及poll的增强版,在Linux的2.6内核提出。对于select,poll以及epoll的而言,三个都是IO多路复用的机制,可以监视多个描述符的读/写等事件,一旦某个描述符就绪(一般是读或者写事件发生了),就能够将发生的事件通知给关心的应用程序去处理该事件。但select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间。

  调用Natvie层代码在Native初始化一个NativeMessageQueue和Looper,在Looper中会开启一个匿名管道,由epoll来监听I/O事件的变化,当管道中有数据的时候,通过epoll通知系统读取数据。最后返回一个NativeMessageQueue的指针交由Java层的MessageQueue方便下次寻址访问。
  在主线程的MessageQueue没有消息时,便阻塞在loop的queue.next()中的nativePollOnce()方法里,详情见Android消息机制1-Handler(Java层),此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe管道写端写入数据来唤醒主线程工作。这里采用的epoll机制,是一种IO多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,本质同步I/O,即读写是阻塞的。 所以说,主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源。

-- AIDL/IPC/IBinder , 

AIDL中支持的数据类型有:Java基本类型,String,CharSequence, List, Map,其他AIDL定义的AIDL接口,实现Parcelable的类
Android的IPC机制-- http://blog.youkuaiyun.com/zizidemenghanxiao/article/details/50341773
Android IBinder机制简单介绍-- http://blog.youkuaiyun.com/mr_liabill/article/details/49837851
  运行在同一个进程中的组件是属于同一个虚拟机和同一个Application的,同理运行在不同进程的组件是属于两个不同的虚拟机和Application的。
  Client端使用的Poxy里面封装了一个binder与Server端的stub(也是一个binder对象)进行交互,两个binder作为接口调用BinderDriver的transact来发送数据包,以及onTransact接收处理数据包。
  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端是阻塞的。

> Android Binder

  管道、消息队列、Socket实现一次进程通信都需要2次内存拷贝,效率太低;共享内存虽然不需要拷贝内存,但管理复杂;Binder只需要一次内存拷贝,从性能角度来看,低于共享内存方式。
  Android系统为每个已安装的App都分配了用户ID(UID),UID是鉴别进程身份的重要标识,通过UID可以进行一系列的权限校验。另一方面 ,传统IPC的接入点是开放的,任何程序都可以根据协议进行访问,无法阻止恶意程序的访问,Android需要一种基于C/S架构的IPC机制,Server端需要能够对Client的请求进行身份校验,来保证数据的安全性。
  进程隔离的实现使用了虚拟地址空间,什么是虚拟地址空间呢?首先需要了解操作系统中的虚拟内存概念,它是一种提高编程效率和提高物理内存利用效率的一种技术。简单来说,就是应用程序看到了都一片连续完整的内存地址空间,而实际上这些地坛空间是映射到碎片化的物理内存中的,这个映射的过程对应用程序来说是透明的。

  Linux是使用的是虚拟内存寻址方式,用户空间的虚拟内存地址是映射到物理内存中的,对虚拟内存的读写实际上是对物理内存的读写,这个过程就是内存映射,这个内存映射过程是通过系统调用mmap()来实现的。
  Binder借助了内存映射的方法,在内核空间和接收方用户空间的数据缓存区之间做了一层内存映射。这样一来,从发送方用户空间拷贝到内核空间缓存区的数据,就相当于直接拷贝到了接收方用户空间的数据缓存区,从而减少了一次数据拷贝。

  Binder是基于C/S架构的,对于通信双方来说,发起请求的进程属于Client,接收请求的进程属于Server,由于存在进程隔离,双方不能直接通信,Binder是如何实现的呢?  ServiceManager和Binder驱动属于两个不同的进程,它们是为Client和Server之间的进程间通信服务的,也就是说Client和Server之间的进程间通信依赖ServiceManager和Binder驱动之间的进程间通信。

-- Binder机制是如何创造第一只下蛋的鸡呢?
  当Android系统启动后,会创建一个名称为servicemanager的进程,这个进程通过一个约定的命令BINDERSETCONTEXT_MGR向Binder驱动注册,申请成为为ServiceManager,Binder驱动会自动为ServiceManager创建一个Binder实体(第一只下蛋的鸡);
  并且这个Binder实体的引用在所有的Client中都为0,也就说各个Client通过这个0号引用就可以和ServiceManager进行通信。Server通过0号引用向ServiceManager进行注册,Client通过0号引用就可以获取到要通信的Server的Binder引用。

-- Binder是基于C/S结构的一种面向对象的IPC机制。包含:Client、Server、Binder驱动和ServiceManager四大组成部分。各组成部分中的Binder含义都有所有不同:
 1.对于Client,Binder是Server本地对象的一个引用,这个引用实际上是一个代理对象,Client通过这个代理对象来间接访问Server的本地对象;
 2.对于Server,Binder是提供具体实现的本地对象,需向ServiceManager注册;
 3.对于Binder驱动,它是连接Client来Server的桥梁,负责将代理对象转化为本地对象,并将Server的执行结果返回给Client。
 4.对于ServiceManager,它保存了Server Binder字符名称和Binder引用的映射,Client通过它来找到Server的Binder引用。
 Binder驱动中保留了Binder代理对象和Binder本地对象的具体结构

> Linux D-BUS , Windows D-Bus,D-Bus,kdbus和Binder
 libdbus库,常见的框架(qt,Glib,Java,Python,C sharp etc.),消息有四种类型:METHOD_CALL, METHOD_RETURN, ERROR, and SIGNAL.
 D-BUS官方网站:http://www.freedesktop.org/wiki/Software/dbus
 “D-Bus is a message bus system, a simple way for applications to talk to one another. In addition to interprocess communication, D-Bus helps coordinate process lifecycle; it makes it simple and reliable to code a "single instance" application or daemon, and to launch applications and daemons on demand when their services are needed. ”
 D-Bus的特点是轻量级、快速,为主流桌面环境提供统一的进程间通信界面。Android通过D-Bus来耦合用户界面和服务这种划分策略是软件系统安全设计的一个典型分离权限设计手法
 D-Bus 设计用来作为用户交互接口与系统服务(守护进程)之间的分隔和通讯以及系统服务(守护进程)之间的通讯。D-Bus 设计之初并未考虑为Cluster,P2P,分布式应用之间的通讯.
 D-Bus适合于控制任务,但并不适合传输大量的数据。例如,D-Bus能通知音频服务程序改变音量(D-Bus著名的例子),却不适合传送音频数据。
 采用C/S架构的IPC机制,D-Bus是在用户空间实现的方法,效率低,消息拷贝次数和上下文切换次数都明显多过于Binder。针对D-Bus这些缺陷,于是就产生了kdbus,这是D-Bus在内核实现版,效率得到提升,与Binder一样在内核作为字符设计,通过open()打开设备,mmap()映射内存。

> binder,OpenBinder
跨进程通信- https://github.com/BaronZ88/HelloBinder
binder的kernel实现- https://github.com/android/kernel_common.git 

binder的kernel实现- https://android.googlesource.com/platform/frameworks/native

android进程间通信:使用AIDL-- http://blog.youkuaiyun.com/saintswordsman/article/details/5130947

 

-- Binder 令牌(Tokens)
  安卓的一项核心设计思想是希望能提供一个不需要依赖中央检验部门来检验程序请求的开放平台.为此,Android使用了程序沙盒和Linux的进程分离来防止程序以无法控制和不安全的方式访问系统内部或者其他程序.这种架构是开发者与使用者共同选择的:既不需要额外的保护来防止恶意程序,同时系统要自动的处理好所有事情.
-- 每个Binder对象引用都是由以下两者之一分配的:
1.同一个进程中指向一个Binder对象的虚拟内存地址,或
2.一个唯一的32位句柄(由Binder内核驱动分配的)指向不同进程中Binder的虚拟内存地址.
  系统的frameword广泛地使用Binder令牌来保证跨进程交互的安全性:一个客户端可以通过创建一个Binder对象作为令牌与服务进程共享,并且服务端能使用它验证来自客户端的请求排除所有伪造请求.

-- 窗口令牌(Window Tokens),系统弹窗
  一个窗口令牌是一种特殊的Binder令牌,窗口管理器用于唯一标识系统中的一个窗口.窗口令牌对于安全十分重要,因为它们会阻止恶意程序出现在其他程序界面之上.窗口管理器通过要求应用程序将它们的窗口令牌作为添加或者删除一个窗口的参数传递过来(拥有 android.permission.SYSTEM_ALERT_WINDOW 权限的程序,即"在其他程序界面上绘制"权限.

 -- IPC/Binder: Binder/Messager/AIDL

  IPC机制之一:简介、多进程模式:http://blog.youkuaiyun.com/huivs12/article/details/49020039;
  Android系统没有采用上述提到的各种进程间通信机制,而是采用Binder机制,难道是因为考虑到了移动设备硬件性能较差、内存较低的特点?不得而知。Binder其实也不是Android提出来的一套新的进程间通信机制,它是基于OpenBinder来实现的。OpenBinder最先是由Be Inc.开发的,接着Palm Inc.也跟着使用。现在OpenBinder的作者Dianne Hackborn就是在Google工作,负责Android平台的开发工作。
Binder- http://www.angryredplanet.com/~hackbod/openbinder/docs/html/BinderIPCMechanism.html

  涉及到底层的功能性的应用,比如游戏加速,修改内存,挂机脚本神马的,发现里面的通信机制无一例外的都是使用的socket

-- Liunx 中跨进程通信涉及到的一些基本概念:
1.进程隔离;

2.进程空间划分:用户空间(User Space)/内核空间(Kernel Space);

3.系统调用:用户态/内核态。
-- Android 系统是基于 Linux 内核的,Linux 已经提供了管道、消息队列、共享内存和 Socket 等 IPC 机制。
Binder 是一种进程间通信机制,基于开源的 OpenBinder 实现;OpenBinder 起初由 Be Inc. 开发,后由 Plam Inc. 接手。
如何在Android下使用Binder- https://blog.youkuaiyun.com/xnwyd/article/details/7188223
  binder在Android中提供从一个任务到另一个任务里的线程的同步调用(CPU)。这个过程中调用线程挂起直到应答线程返回,不需要消息队列。RAM只是用来在不同的调用者间共享数据。Android里binder库的关键是调用进程把自己的CPU时间片让给应答进程。
  Binder是基于OpenBinder,在Android系统上使用的进程间通信机制。Binder基于Client-Server通信模式,本质上可以理解为它实现了Client对Server对象的远程调用。Binder机制定义了四个组件,分别是Client,Server,ServiceManager和binder驱动,其中Client,Server,ServiceManager运行于用户空间,binder驱动运行于内核空间。
  binder驱动是内核中的一个字符设备驱动/dev/binder,它是整个Binder通信机制的核心。Client,Server,ServiceManager通过open()和ioctl()文件操作函数与binder驱动进行通信,从而实现了Client向Server发送请求,Server处理请求并返回结果到Client。具体来说,它负责进程之间Binder通信的建立,Binder在进程之间的传递,Binder引用计数管理,数据包在进程之间的传递和交互等一系列底层支持。
  ServiceManager是一个守护进程,负责管理服务,即所有的Server需要向ServiceManager注册服务。同时,ServiceManager向Client提供查询和获取Server的接口。
  基于Client-Server的通信方式广泛应用于从互联网和数据库访问到嵌入式手持设备内部通信等各个领域。智能手机平台特别是Android系统中,为了向应用开发者提供丰富多样的功能,这种通信方式更是无处不在,诸如媒体播放,视音频频捕获,到各种让手机更智能的传感器(加速度,方位,温度,光亮度等)都由不同的Server负责管理,应用程序只需做为Client与这些Server建立连接便可以使用这些服务,花很少的时间和精力就能开发出令人眩目的功能。
  另一方面是传输性能。socket作为一款通用接口,其传输效率低,开销大,主要用在跨网络的进程间通信和本机上进程间的低速通信。消息队列和管道采用存储-转发方式,即数据先从发送方缓存区拷贝到内核开辟的缓存区中,然后再从内核缓存区拷贝到接收方缓存区,至少有两次拷贝过程。共享内存虽然无需拷贝,但控制复杂,难以使用。

-- 传统的 IPC 通信方式有两个问题:
 1.性能低下,一次数据传递需要经历:内存缓存区 --> 内核缓存区 --> 内存缓存区,需要 2 次数据拷贝;
 2.接收数据的缓存区由数据接收进程提供,但是接收进程并不知道需要多大的空间来存放将要传递过来的数据,因此只能开辟尽可能大的内存空间或者先调用 API 接收消息头来获取消息体的大小,这两种做法不是浪费空间就是浪费时间。

-- Linux现有的所有进程间IPC通信方式:
 1. 管道:在创建时分配一个page大小的内存,缓存区大小比较有限;
 2. 消息队列:信息复制两次,额外的CPU消耗;不合适频繁或信息量大的通信;
 3. 共享内存:无须复制,共享缓冲区直接付附加到进程虚拟地址空间,速度快;但进程间的同步问题操作系统无法实现,必须各进程利用同步工具解决;
 4. 套接字:作为更通用的接口,传输效率低,主要用于不通机器或跨网络的通信;
 5. 信号量:常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
 6. 信号: 不适用于信息交换,更适用于进程中断控制,比如非法内存访问,杀死某个进程等;

-- 在Linux中使用的IPC通信机制如下:
 1.传统IPC:无名pipe, signal, trace, 有名管道
 2.AT&T Unix 系统V:共享内存,信号灯,消息队列
 3.BSD Unix:Socket

 在linux通信机制中,目前只有socket支持C/S的通信模式。

 Binder中有两种索引,一是本地进程地址空间的一个地址,另一个是一个抽象的32位句柄(HANDLE),它们之间是互斥的:所有的进程本地对象的索引都是本地进程的一个地址(address, ptr, binder),所有的远程进程的对象的索引都是一个句柄(handle)

> AIDL机制,AIDL/IPC/多应用/多线程;AIDL(以及Binder);Binder/IBinder ; Messenger

 代理实现/存根通信。

Binder详解: http://blog.youkuaiyun.com/yangzhiloveyou/article/details/14043801

Android深入浅出之Binder机制: http://www.cnblogs.com/innost/archive/2011/01/09/1931456.html
Android的IPC机制Binder的各个部分: http://blog.chinaunix.net/uid-9185047-id-3281772.html
Android实战技术:理解Binder机制: http://blog.youkuaiyun.com/hitlion2008/article/details/9842289
Android系统篇之----Binder机制和远程服务调用机制分析-- http://blog.youkuaiyun.com/jiangwei0910410003/article/details/52467919
    在Android系统的虚拟机中,每一个app运行都是在一个独立的沙箱里面的,这样的话,一个应用崩溃也不会影响到其他应用,这样我们就提出了一个问题,跨进程如如何进行通讯?如何传递数据?其实两个进程是不能直接通讯的,他需要Android系统底层的帮助来进行通讯!那就是我们每个人都知道的四大组件
    如果您不需要执行并发IPC在不同的应用程序中 你就用Binder ,或者如果你想执行IPC,但不需要处理多线程,实现你的接口Messenger,无论如何,确保你了解实现AIDL之前绑定服务。

1.基本数据类型 2.String 3.CharSequence 4.List 5.Map 6.Parcelable(序列化)
关于AIDL的介绍在文档:docs/guide/developing/tools/aidl.html
关于IBinder的介绍在文档:docs/reference/android/os/IBinder.html
以及Binder:docs/reference/android/os/Binder.html

-- COM的一个概念---------Proxy/Stub结构(代理/存根结构) 
 Android就是在传统的C/S架构中加入了一层,实现IPC。我们下面来详细介绍一下android的aidl实现原理:
    AIDL(Android接口定义语言)是类似于其他你遇到过的IDL。它允许您定义的编程接口,客户端和服务达成一致,以互相交流使用进程间通信(IPC)。在Android上,一个进程无法正常访问另一个进程的内存,而AIDL可以为你实现。
-- Binder实现的是RPC,它与具体语言无关,所以理论上,基于Binder可以让Native的进程与Android Java层的应用程序通讯。最关键的,也是最麻烦点就在于客户端如何获取服务端的Service的IBinder对象,但也非不可能,要通过JNI和ClassLoader等一系列方式可以获得Java层的对象,其实Java层API的实现也是以这样子的方式。
  Binder是什么?它可以叫作:IPC、RPC、线程迁移、远程对象访问,本文中理解它为远程对象访问更贴切些,简而言之就是一个进程能访问另一个进程中的对象,调用该对象的方法,就好像对象在自己的进程中一样,这种访问是同步的访问,当然Binder也能实现异步的通信。
  Binder分为Java和C++两套实现,分别用于Java应用和Native应用开发,Java Binder实际上也是基于C++ Binder的一个封装,因此本文只分析C++ Binder。
服务分为2种:Native Service、Android Service。
Native Service:是在系统init阶段通过init.rc脚本建立的服务,完全在C++空间完成的服务。
Androids service:是系统二阶段(init2)初始化时建立的服务,是指在JVM空间完成的服务,虽然也要使用Navite上的框架,但是服务主体存在于Android空间,所有的Androids service都运行在一个进程中:systemsever进程。
  -- Binder实现原理
    Binder本质上说就是一种数据传输方式,当通过服务代理调用服务对象的方法时,服务代理把参数序列化进行传输,服务对象反序列化取出参数,然后调用服务对象的方法。
   进程间的通信是通过Android专门为Linux增加的一个设备(/dev/binder)来实现的。
    本质上是使用了共享内存来进行通信,但该共享内存和我们平常理解会有一点不一样。
我们平常使用的共享内存是两个进程之间,即点到点的,如果有N个进程要两两通信而又不相互干扰,那么就必须有N*N个共享内存。Binder使用的共享内存是进程与binder设备之间,即binder做为一个中间者进行传递,类似会议电视的MCU。
使用了共享内存,在驱动中还是会有一次拷贝的,进程A向进程B传递数据时,数据会被驱动从进程A中拷贝到binder和进程B之间的共享内存中,然后进程B就可以直接读了。

---------------------

非实时,通知性的方式

第一种方式就是Intent,Intent可以非常方便的通讯,但是它是非实时的,无法进行实时的像函数调用那样的实时的通讯。

实时的函数调用
    但是IPC的根本目的还是为了实现函数的调用,即使是传递数据也是要通过函数调用的方式,为什么呢?因为程序运行总是要知道状态,要有逻辑上的行为,因此必须通讯函数才能体现出行为。
    IPC的机制除了进程,或者说不同的应用程序之间进行通讯,同时也能够让不同的组件之间进行像普通对象那样进行实时的调用。因为Android的组件都是由系统框架统一的进行构建和销毁,所以你就无法创建对象,因此,就没有办法像普通的对象那样进行组合或者聚合,从而也就没有办法进行直接调用。但是IPC的方式就可以让Activity/Service获取另外一个Service对象的引用,从而直接调用其上的方法。
    还有,就是这些IPC方法总是产生客户/服务端模式的调用,也即是客户端组件(Activity/Service)持有服务端Service的组件,只能是客户端主动调用服务端的方法,服务端无法反过来调用客户端的方法,因为IPC的另一端Service无法获取客户端的对象。

> 有三种方式可以进行IPC通讯:
1. 直接使用Binder对象
   缺点是这种方式不能进行跨进程,跨应用程序的函数调用。只能实现在同一个进程之中,同一个应用程序之中的不同的组件之间通讯。
   优点就是这种方式使用起来特别简单,对于公开出来的方法没有任何的限制,可以传递任何的对象,甚至是回调等等。
   总体上来讲如果不需要跨进程,这是最好的实现方式,可以完全实现像普通对象之间的组合和聚合。但是这里,最好不要像Android文档中的示例那样,直接返回Service对象,因为Service对象会有很多Public的方法,如果直接返回Service对象会导致公开很多不必须的方法,一旦Client端调用这些方法,将导致奇怪的现象和Bug,一个方法就是用Proxy对Service对象进行封装,只公开需要的接口。

示例:
1.service: 
public class BinderPrinterService extends Service {  
    private static final String TAG = "PlayerService";  
    private IBinder mBinder;  
  
    @Override  
    public void onCreate() {  
        mBinder = new ProxyService(this);  
    }  
      
    @Override  
    public IBinder onBind(Intent intent) {  
        return mBinder;  
    }  
      
    public void print(String msg, TextView tv) {  
        try {  
            Log.e(TAG, "Preparing printer...");  
            tv.setText("Preparing printer...");  
            Thread.sleep(1000);  
            Log.e(TAG, "Connecting printer...");  
            tv.setText("Connecting printer...");  
            Thread.sleep(1000);  
            Log.e(TAG, "Printing.... " + msg);  
            tv.setText("Printing.... ");  
            Thread.sleep(1000);  
            Log.e(TAG, "Done");  
        } catch (InterruptedException e) {  
        }  
        tv.setText(msg);  
        Toast.makeText(this, "Printing is done.", Toast.LENGTH_SHORT).show();  
    }  
}  
  
class ProxyService extends Binder {  
    private BinderPrinterService mService;  
      
    public ProxyService(BinderPrinterService svc) {  
        mService = svc;  
    }  
      
    public void print(String msg, TextView tv) {  
        mService.print(msg, tv);  
    }  
}
  
client:
public class BinderClientActivity extends Activity {  
    ProxyService mService;  
    private TextView mStatusPanel;  
  
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.printer_activity);  
        setTitle("Binder client Activity");  
        mStatusPanel = (TextView) findViewById(R.id.status);  
        ((Button) findViewById(R.id.play)).setText("Print via extending Binder");  
    }  
  
    @Override  
    protected void onStart() {  
        super.onStart();  
        doBindService();  
    }  
  
    private void doBindService() {  
        Intent intent = new Intent(this, BinderPrinterService.class);  
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);  
    }  
  
    @Override  
    protected void onStop() {  
        super.onStop();  
        doUnbindService();  
    }  
  
    private void doUnbindService() {  
        if (mService != null) {  
            unbindService(mConnection);  
        }  
    }  
      
    public void onButtonClick(View v) {  
        if (mService == null) {  
            return;  
        }  
        mService.print("Tree of liberty must be refreshed from time to time with blood of patroits and tyrants",  
                mStatusPanel);  
    }  
      
    private ServiceConnection mConnection = new ServiceConnection() {  
        @Override  
        public void onServiceConnected(ComponentName className, IBinder service) {  
            mService = (ProxyService) service;  
        }  
  
        @Override  
        public void onServiceDisconnected(ComponentName arg0) {  
            mService = null;  
        }  
    };  
}  

2. 使用Messenger对象
    这是利用了消息循环队列来进行通讯的,也就是说服务端封装成了一个Handler,客户端向这个Handler发送Message,服务端再处理这个Message从而实现通讯。
    优点,最突出的优点就是它可以保证线程上的安全,或者说时序上的安全。因为客户端是向Handler发送消息,而不是直接的函数调用,所以呢?Message都是按先后的顺序放到服务端的消息队列当中,然后再由服务端Service决定如何处理这些Message。因为直接的函数调用会导致被调用的函数也会出现在调用者的线程之中,也就是说被调用到的函数也会继续存在于调用者的调用栈中,因此有可能产生线程的问题。而Messenger方式,并不是直接的函数调用,而是仅向Service的Handler发送一个Message,然后调用者就此返回,其调用栈也就此停止,Service可以选择如何处理这一个Message。Messenger方式即可以在同一个进程之中,也可以跨进程实现真正的IPC。
    但是它的缺点也是相当的明显的,就是它是发送一个Message对象,而不是直接的函数调用,所以非常的不直观,另外,Message对象也无法方便的携带过多的参数,如果超过一个对象,只能封装打包成一个对象然后再放到Message.obj中。需要注意的是,如果是在同一个进程之中,Message可以携带任何对象,但如果是跨进程,则Message.obj中存放的必须是实现了Parcelable接口的对象,否则无法实现IPC,会有Exception。还有一个缺点,就是Message对象的标识(Message.what)必须是Client端和Service端都定义一致,否则就无法通讯,这在调试的时候必须注意,因为这不会有编译或者运行时错误,但就是无法正常工作,是比较隐蔽的Bug。

示例:
Service:
public class MessengerPrinterService extends Service {  
    static final int MSG_PRINT = 1;  
  
    private static final String TAG = "PrinterService";  
  
    private Handler mServiceHandler = new Handler() {  
        @Override  
        public void handleMessage(Message msg) {  
            switch (msg.what) {  
                case MSG_PRINT:  
                    print("Freedom is nothing but a chance to be better!", (TextView) msg.obj);  
                    break;  
                default:  
                    super.handleMessage(msg);  
            }  
        }  
    };  
  
    final Messenger mMessenger = new Messenger(mServiceHandler);  
  
    @Override  
    public IBinder onBind(Intent intent) {  
        return mMessenger.getBinder();  
    }  
      
    public void print(String msg, TextView tv) {  
        try {  
            Log.e(TAG, "Preparing printer...");  
            if (tv != null) {  
                tv.setText("Preparing printer...");  
            }  
            Thread.sleep(1000);  
            Log.e(TAG, "Connecting printer...");  
            if (tv != null) {  
                tv.setText("Connecting printer...");  
            }  
            Thread.sleep(1000);  
            Log.e(TAG, "Printing.... " + msg);  
            if (tv != null) {  
                tv.setText("Printing.... ");  
            }  
            Thread.sleep(1000);  
            Log.e(TAG, "Done");  
        } catch (InterruptedException e) {  
        }  
        if (tv != null ) {  
            tv.setText(msg);  
        }  
        Toast.makeText(this, "Messenger Printing is done.", Toast.LENGTH_LONG).show();  
    }  

}  

 

Local client(in the same application):
public class MessengerClientActivity extends Activity {  
    Messenger mService = null;  
  
    private ServiceConnection mConnection = new ServiceConnection() {  
        public void onServiceConnected(ComponentName className, IBinder service) {  
            mService = new Messenger(service);  
        }  
  
        public void onServiceDisconnected(ComponentName className) {  
            mService = null;  
        }  
    };  
  
    public void onButtonClick(View v) {  
        if (mService == null) return;  
        // Create and send a message to the service, using a supported 'what' value  
        Message msg = Message.obtain(null, MessengerPrinterService.MSG_PRINT, 0, 0);  
        msg.obj = findViewById(R.id.status);  
        try {  
            mService.send(msg);  
        } catch (RemoteException e) {  
            e.printStackTrace();  
        }  
    }  
  
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.printer_activity);  
        setTitle("Messenger client Activity");  
        ((Button) findViewById(R.id.play)).setText("Print via constructing Messenger");  
    }  
  
    @Override  
    protected void onStart() {  
        super.onStart();  
        bindService(new Intent(this, MessengerPrinterService.class), mConnection,  
            Context.BIND_AUTO_CREATE);  
    }  
  
    @Override  
    protected void onStop() {  
        super.onStop();  
        if (mService != null) {  
            unbindService(mConnection);  
        }  
    }  
}  
远端的Client(在另外一个应用程序进程里面):
public class AnotherMessengerClientActivity extends Activity {  
    private static final int MSG_PRINT = 1;  
  
    Messenger mService = null;  
  
    private ServiceConnection mConnection = new ServiceConnection() {  
        public void onServiceConnected(ComponentName className, IBinder service) {  
            mService = new Messenger(service);  
        }  
  
        public void onServiceDisconnected(ComponentName className) {  
            mService = null;  
        }  
    };  
  
    public void onButtonClick(View v) {  
        if (mService == null) return;  
        // Create and send a message to the service, using a supported 'what' value  
        Message msg = Message.obtain(null, MSG_PRINT, 0, 0);  
        try {  
            mService.send(msg);  
        } catch (RemoteException e) {  
            e.printStackTrace();  
        }  
    }  
  
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.printer_activity);  
        setTitle("Another Messenger client Activity");  
        ((Button) findViewById(R.id.play)).setText("Print via constructing Messenger");  
    }  
  
    @Override  
    protected void onStart() {  
        super.onStart();  
        Intent intent = new Intent();  
        intent.setClassName("com.example.effectiveandroid", "com.example.effectiveandroid.MessengerPrinterService");  
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);  
    }  
  
    @Override  
    protected void onStop() {  
        super.onStop();  
        if (mService != null) {  
            unbindService(mConnection);  
        }  
    }  
}  

3. 使用AIDL进程通讯
  这是最正统的IPC方式,实际上Messenger的跨进程通讯的底层也是通过AIDL来实现的。它的优点就是完全是为跨进程而设计的,接口由AIDL文件指定,Client端和Service端都通过AIDL所描述的接口来进行调用和实现,不容易出错。需要注意的是接口中的所有的对象都必须实现了Parcelable接口,即使仅仅是在同一个进程之中。它的缺点就是必须要在每一个想要通讯的地方定义一个完全一样的AIDL文件,否则IPC不会成功,而且它是严格的按照IPC的机制来的,所以即使是在同一进程之内,所有的接口的参数和返回值的对象必须是实现了Parcelable接口的,但Binder对象和Messenger就无此限制。需要一点:定义AIDL会严格按照IPC的方式进程即使是在同一个进程之中。所以,除非是真的要跨进程通讯,否则不要使用AIDL。

AIDL文件:
interface PrinterInterface {  
    void print(String msg);  

 
AIDL Service:
public class AIDLPrinterService extends Service {  
    private static final String TAG = "AIDLPrinterService";  
    private Handler mHandler = new Handler();  
    @Override  
    public IBinder onBind(Intent intent) {  
        return mBinder;  
    }  
      
    private PrinterInterface.Stub mBinder = new PrinterInterface.Stub() {  
        @Override  
        public void print(String msg) throws RemoteException {  
            AIDLPrinterService.this.print(msg);  
        }  
    };  
      
    public void print(String msg) {  
        try {  
            Log.e(TAG, "Preparing printer...");  
            Thread.sleep(1000);  
            Log.e(TAG, "Connecting printer...");  
            Thread.sleep(1000);  
            Log.e(TAG, "Printing.... " + msg);  
            Thread.sleep(1000);  
            Log.e(TAG, "Done");  
        } catch (InterruptedException e) {  
        }  
        mHandler.post(new Runnable() {  
            @Override  
            public void run() {  
                Toast.makeText(AIDLPrinterService.this, "via AIDL Printing is done.", Toast.LENGTH_LONG).show();  
            }  
        });  
    }  

 
Local client:
public class AIDLClientActivity extends Activity {  
    private static final String TAG = "PrinterClientActivity";  
    PrinterInterface mService;  
  
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.printer_activity);  
        setTitle("Local AIDL client Activity");  
        ((Button) findViewById(R.id.play)).setText("Print via AIDL");  
    }  
  
    @Override  
    protected void onStart() {  
        super.onStart();  
        doBindService();  
    }  
  
    private void doBindService() {  
        Intent intent = new Intent(this, AIDLPrinterService.class);  
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);  
    }  
  
    @Override  
    protected void onStop() {  
        super.onStop();  
        doUnbindService();  
    }  
  
    private void doUnbindService() {  
        if (mService != null) {  
            unbindService(mConnection);  
        }  
    }  
      
    public void onButtonClick(View v) {  
        if (mService == null) {  
            Log.e(TAG, "what the fucl service is not ready");  
            return;  
        }  
        try {  
            mService.print("This message is from local client via AIDL interface");  
        } catch (RemoteException e) {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
        }  
    }  
      
    private ServiceConnection mConnection = new ServiceConnection() {  
        @Override  
        public void onServiceConnected(ComponentName className, IBinder service) {  
            mService = PrinterInterface.Stub.asInterface(service);  
        }  
  
        @Override  
        public void onServiceDisconnected(ComponentName arg0) {  
            mService = null;  
        }  
    };  

 
client in another application process:
public class AnotherAIDLClientActivity extends Activity {  
    private static final String TAG = "PrinterClientActivity";  
    PrinterInterface mService;  
  
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.printer_activity);  
        setTitle("Another AIDL client Activity");  
        ((Button) findViewById(R.id.play)).setText("Print via AIDL");  
    }  
  
    @Override  
    protected void onStart() {  
        super.onStart();  
        doBindService();  
    }  
  
    private void doBindService() {  
        Intent intent = new Intent();  
        intent.setClassName("com.example.effectiveandroid", "com.example.effectiveandroid.AIDLPrinterService");  
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);  
    }  
  
    @Override  
    protected void onStop() {  
        super.onStop();  
        doUnbindService();  
    }  
  
    private void doUnbindService() {  
    if (mService != null) {  
            unbindService(mConnection);  
        }  
    }  
      
    public void onButtonClick(View v) {  
        if (mService == null) {  
            Log.e(TAG, "what the fucl service is not ready");  
            return;  
        }  
        try {  
            mService.print("call PrinterService via AIDL from another application");  
        } catch (RemoteException e) {  
            e.printStackTrace();  
        }  
    }  
      
    private ServiceConnection mConnection = new ServiceConnection() {  
        @Override  
        public void onServiceConnected(ComponentName className, IBinder service) {  
            mService = PrinterInterface.Stub.asInterface(service);  
        }  
  
        @Override  
        public void onServiceDisconnected(ComponentName arg0) {  
            mService = null;  
        }  
    };  
}  

结论:
对比且总结了一下:如果是在同一个进程(应用程序)之中,且不涉及复杂的线程模式,直接使用Binder对象方式是最方便快捷的;如果是涉及跨进程操作,且不涉及复杂的线程模式就使用AIDL方式;无论是同一进程内还是跨进程,如果涉及比较复杂的线程模式就推荐使用Messenger的方式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值