Android进程间通讯
目录 Binder框架 Binder介绍 Android跨进程通讯 Activity Content Provider Broadcast AIDL
Binder 框架(一)
Binder 框架(二)
Binder 应用层类继承关系
Android系统通信方式 Binder Binder通信的实现 : 1.Binder通信是通过linux的binder driver来实现的, 2.Binder通信操作类似线程迁移(thread migration),两个进 程间IPC看起来就象是一个进程进入另一个进程执行代码 然后带着执行的结果返回; 3.Binder的用户空间为每一个进程维护着一个可用的线程池, 线程池用于处理到来的IPC以及执行进程本地消息, Binder通信是同步而不是异步。
Binder Android中的 Binder通信实现要点: 1. Android中的Binder通信是基于Service与Client的工作模型的; 2. 所有需要IBinder通信的进程都必须创建一个IBinder接口; 3. 系统中有一个进程管理所有的system service: 4. Android不允许用户添加非授权的System service; 5. 现在源码开放了,我们可以修改一些代码来实现添加底层system Service的目的; 6. 对用户程序来说,我们也要创建server,或者Service用于进程间通信; 7. ActivityManagerService管理JAVA应用层所有的service创建与连接 (connect),disconnect; 8. 所有的Activity也是通过这个service来启动,加载的; 9. ActivityManagerService也是加载在Systems Servcie中的;
Binder Android的 Service工作流程 1.Android虚拟机启动之前系统会先启动service Manager进程; 2.service Manager打开binder驱动,并通知binder kernel驱动程序这个 进程将作为System Service Manager; 3.然后该进程将进入一个循环,等待处理来自其他进程的数据。 4.用户创建一个System service后,通过defaultServiceManager得到一 个远程ServiceManager的接口,通过这个接口我们可以调用 addService函数将System service添加到Service Manager进程中; 5.然后client可以通过getService获取到需要连接的目的Service的IBinder 对象,这个IBinder是Service的BBinder在binder kernel的一个参考, 6.所以service IBinder 在binder kernel中不会存在相同的两个IBinder对 象,每一个Client进程同样需要打开Binder驱动程序。对用户程序而 言,我们获得这个对象就可以通过binder kernel访问service对象中的 方法。 7.Client与Service在不同的进程中,通过这种方式实现了类似线程间的 迁移的通信方式,对用户程序而言当调用Service返回的IBinder接口 后,访问Service中的方法就如同调用自己的函数。
binder 进程有自己的地址空间,不同进程间的通信并不能直接引用地址。一般的解 决途径是,发送进程把需要传送的数据按照一定格式(marshall)转换成二进制形式 /特定格式的数据,发往接收进程;接收进程收到二进制形式、特定格式的数据后, 反转换(unmarshall)成原文数据,然后使用。binder使用的就是这种步骤。 binder使用的是同步c/s模型,s循环阻塞在接收数据操作上,随时处理c的数 据,处理后发送回c;c则将请求服务的数据发送到s,阻塞在收取s返回数据,收 到数据后,继续自己的工作。 binder的框架可以分成3层 最下层是linux os和binder driver。binder driver本质上是进程间的共享内存, 各进程将要发送到其它进程的数据写入到driver,从driver读取其它进程发送来的 数据。 中间层是cpp实现的framework,完成数据的接收发送转换,和c/s流程的支持。 其实到中间层,binder的架构就已经完全具备了。但android使用的是java做为 一般app的开发语言。所以还需要jni和相应java binder类的支持,这就是中间层上 面的第三层:jni/java
binder driver binder driver是binder机制的基础,是实现ipc的通道。 binder driver与framework和app的功能操作是用ioctl方式实现的。 最基本的操作是数据读写操作。一次读写操作有两个子操作组成:写 子操作和读子操作。driver为每个进程和线程维护了一个数据结构, 其中有一个list,挂接了其它进程写入的数据,还有一个信号量。写子 操作负责将数据挂接到接收进程对应数据结构的list上。读子操作负责 处理自己进程中list上的数据,传回framework和app。 binder driver的第二个功能是为了支持rpc,就是维护本地函数和远 端proxy的handle之间的对应关系。更复杂的是维护两个远端Proxy的 handle之间的对应关系,这两个proxy是同一rpc的proxy。
binder framework binder的framework是cpp实现的,这里分成本地local/s和远端remote/c两半来描 述。 公共类IBinder派生出两个子类,BBinder用于local,BpBinder用于remote。 本地实现的类以BBinder为基类派生,接收数据后,处理,并将结构发会远 端。 远端proxy的类使用BpBinder,接收和发送数据到本地。 进一步方便开发,引入了IInterface类,开发者从Interface派生子类,定义 自己需要的rpc操作。 在IInterface的装饰下,从BBinder派生出了本地的关键类 BnInterface<INTERFACE>;从BpRefBase派生出了包含成员BpBinder对象 的远端关键类BpInterface<INTERFACE>。 于是,创世纪中,神说:‘要有光’,就有了光。神看光是好的,就把光 暗分开了。 神称光为昼,称暗为夜。有晚上,有早晨,这是头一日。
binder jni,java类和aidl工具 binder framework的机制要被java使用,需要经过包装。 除了jni相关部分,android在java中还定义了几个相关的接口 和类,IBinder,Binder,BinderProxy,IInterface, BinderInternal。 aidl工具则是方便java实现c/s结构的一个工具,开发者编 写简单的接口描述idl文件,则aidl自动生成local和remote的 Binder类。让开发者关注在实际的功能开发上,不必为binder 机制耗费无谓的精力
parcel 为了便于ipc之间传递的数据的操作,binder引入了parcel的 概念。parcel可以想成快递公司的包装箱,需要传递的各 种类型的数据都被打包进parcel类,binder负责传递parcel 对象,接收端则从parcel解出数据。这样的机制即减少了 各种数据类型对传递的复杂性,又可以通过增加打包/解 包parcel的数据类型,轻易实现扩展。 parcel已经支持容纳基本数据类型和一些复合数据类型。 在framework层面,parcel提供了Flattenable基类,可以 扩展parcel容纳的数据类型。 在java parcel层面,parcel提供了Parcelable接口,可以 扩展parcel容纳的数据类型。
依赖于binder的service ipc/rpc已经被binder机制解决掉了,那么service面临的 唯一问题就是service如何让想使用service的client招到 service。解决的方案就是 service manager。service manager是一个特殊进程,每个service都会注册登记到 service manager中,而client可以从service manager查询得 到自己需要使用的service。
Android跨进程通讯 在android SDK中提供了4种用于跨进程通讯的方式。这4种方式正好 对应于android系统中4种应用程序组件: Activity、 Content Provider、 Broadcast Service。 其中Activity可以跨进程调用其他应用程序的Activity;Content Provider可以跨进程访问其他应用程序中的数据(以Cursor对象形式返回 ),当然,也可以对其他应用程序的数据进行增、删、改操 作; Broadcast可以向android系统中所有应用程序发送广播,而需要跨进程通 讯的应用程序可以监听这些广播;Service和Content Provider类似,也 可以访问其他应用程序中的数据,但不同的是,Content Provider返回的 是Cursor对象,而Service返回的是Java对象,这种可以跨进程通讯的服 务叫AIDL服务
访问其他应用程序的Activity Activity既可以在进程内(同一个应用程序)访问,也可以跨进程访问。Activity的 跨进程访问与进程内访问略有不同。虽然它们都需要Intent对象,但跨进程访问并不需要 指定Context对象和Activity的 Class对象,而需要指定的是要访问的Activity所对应的 Action(一个字符串)。有些Activity还需要指定一个Uri(通过 Intent构造方法的第2 个参数指定)。 如果想在同一个应用程序中访问Activity,需要指定Context对象和Activity的Class 对象。 Intent intent = new Intent(this , Test.class ); startActivity(intent); Activity的跨进程访问与进程内访问略有不同。虽然它们都需要Intent对象,但跨进程访问并不 需要指定Context对象和Activity的 Class对象,而需要指定的是要访问的Activity所对应的Action (一个字符串)。有些Activity还需要指定一个Uri(通过 Intent构造方法的第2个参数指定)。 Android 对进程的重要性评级的时候,选取它最高的级别。另外,当被另外的一 个进程依赖的时候,某个进程的级别可能会增高。一个为其他进程服务的进程 永远不会比被服务的进程重要级低。因为服务进程比后台activity进程重要级高 ,因此一个要进行耗时工作的activity最好启动一个service来做这个工作,而不 是开启一个子进程――特别是这个操作需要的时间比activity存在的时间还要长 的时候。例如,在后台播放音乐,向网上上传摄像头拍到的图片,使用service 可以使进程最少获取到“服务进程”级别的重要级,而不用考虑activity目前是什么 状态。broadcast receivers做费时的工作的时候,也应该启用一个服务而不是开 一个线程。
Activity共享 1. 在AndroidManifest.xml文件中指定Action。指定Action要 使用<action>标签,并在该标签的android:name属性中指定 Action 2. 在AndroidManifest.xml文件中指定访问协议。在指定Uri (Intent类的第2个参数)时需要访问协议。访问协议需要使 用<data>标签的android:scheme属性来指定。如果该属性的 值是“abc”,那么Uri就应该是“abc://Uri的主体 部分”,也 就是说,访问协议是Uri的开头部分。 3. 通过getIntent().getData().getHost()方法获得协议后的Uri 的主体部分。这个Host只是个称谓,并不一定是主机名。读 者可以将其看成是任意的字符串。 4. 从Bundle对象中获得其他应用程序传递过来的数据。 5. 这一步当然是获得数据后做进一步的处理了。至于如何处 理这些数据,就得根据具体的需求决定了。
Content Provider Android应用程序可以使用文件或SqlLite数据库来存储数据。 Content Provider提供了一种在多个应用程序之间数据共享的方式( 跨进程共享数据)。应用程序可以利用Content Provider完成下面的 工作 1. 查询数据 2. 修改数据 3. 添加数据 4. 删除数据 虽然Content Provider也可以在同一个应用程序中被访问, 但这么做并没有什么意义。Content Provider存在的目的向其他应用 程序共享数据和允许其他应用程序对数据进行增、删、改操作。 Android系统本身提供了很多Content Provider,例如,音频、视频 、联系人信息等等。我们可以通过这些Content Provider获得相关信 息的列表。这些列表数据将以Cursor对象返回。因此,从Content Provider返回的数据是二维表的形式。
对于访问Content Provider的程序,需要使用ContentResolver对象。该对象需要使用getContentResolver方法获得,代码如下: ContentResolver cr = getContentResolver(); 与Activity一样,Content Provider也需要与一个URI对应。每一个Content Provider可以控制多个数据集,在这种情况下,每一个数据集会对应一个单独的URI。所有的URI必须以“content://”开头。 为了程序更容易维护,也为了简化程序代码,一般将URI定义成一个常量
编写Content Provider的具体步骤 1. 编写一个继承于android.content.ContentProvider的子类。 该类是ContentProvider的核心类。在该类中会实现 query、 insert、update及delete方法。实际上调用ContentResolver类 的这4个方法就是调用 ContentProvider类中与之要对应的方 法。在本文中只介绍query。至于insert、update、delete和 query的用法类 似。也是通过Uri传递参数,然后在这些方法 中接收这些参数,并做进一步地处理。 2. 在AndroidManifest.xml文件中配置ContentProvider。要 想唯一确定一个ContentProvider,需要指定这个 ContentProvider的URI,除此之外,还需要指定URI所对应的 ContentProvider类。这有些象Servlet的定义,除了要 指定 Servlet对应的Web地址,还要指定这个地址所对应的Servlet 类。
广播(Broadcast) 广播是一种被动跨进程通讯的方式。当某个程序向系统发送 广播时,其他的应用程序只能被动地接收广播数据。这就 象电台进行广播一样,听众只能被动地收听,而不能主动 与电台进行沟通。 在应用程序中发送广播比较简单。只需要调用 sendBroadcast方法即可。该方法需要一个Intent对象。通 过Intent对象可以发送需要广播的数据。 先建一个android工程:sendbroadcast。在XML布局 文件中放两个组件:EditText和Button,当单击按钮后, 会弹出显示 EditText组件中文本的对话框,关闭对话框后, 会使用sendBroadcast方法发送消息,并将EditText组件 的文本通过Intent对象发送出去。
AIDL服务 服务(Service)是android系统中非常重要的组件。 Service可以脱离应用程序运行。也就是说,应用程序只起到 一个启动Service的作用。一但Service被启动,就算应用程序 关闭,Service仍然会在后台运行。 android系统中的Service主要有两个作用:后台运行和跨 进程通讯。后台运行就不用说了,当Service启动后,就可以 在Service对象中 运行相应的业务代码,而这一切用户并不会 察觉。而跨进程通讯是这一节的主题。如果想让应用程序可 以跨进程通讯,就要使用我们这节讲的AIDL服 务,AIDL的全 称是Android Interface Definition Language,也就是说, AIDL实际上是一种接口定义语言。通过这种语言定义接口后, Eclipse插件(ODT)会自动生成相应的Java代码接 口代码。
编写AIDL步骤 1. 在Eclipse工程的package目录中建立一个扩展名为aidl的 文件。package目录就是Java类所在的目录。该文件的语法 类似于Java代码。aidl文件中定义的是AIDL服务的接口。这 个接口需要在调用AIDL服务的程序中访问。 2. 如果aidl文件的内容是正确的,Eclipse插件会自动生成一 个Java接口文件(*.java)。 3. 建立一个服务类(Service的子类)。 4. 实现由aidl文件生成的Java接口。 5. 在AndroidManifest.xml文件中配置AIDL服务,尤其要注意 的是,<action>标签的android:name属性值就是客户端要引 用该服务的ID,也就是Intent类构造方法的参数值。