Android跨进程通信IPC

参考:

android中跨进程通讯的4种方式

android跨进程通IPC):使用AIDL

使用AIDL实现程间的通信

Android Service完全解析,关于服务你所需知道的一切()

Activity与一个远程Service建立关联同样使用AIDL来进行跨进程通信了(IPC这是由于远程的Service是在另一个进程中运行的,因此他们之间的通信是跨进程的通信。

Android开启多进程模式:

(1)通过给四大组件指定android:process属性就可以开启多进程模式。若没有为Activity指定process属性,那么它运行在默认进程中,默认进程的进程名为包名。

进程名以:开头的进程属于当前应用的私有进程,其他应用的组件不可以和它跑在同一个进程中:”的含义是指要在当前的进程名前面附加上当期的包名,这是一种简写的方法。

而进程名不以:开头的进程属于全局进程,其他应用通过ShareUID方法可以和它跑在同一个进程中。这是一种完整的命名方式,不会附加包名信息。

android:process=":xyz" //进程名是packageName:xyz
android:process="aaa.bbb.ccc" //进程名是aaa.bbb.ccc

(2)Android系统会为每个应用分配一个唯一的UID,具有相同UID的应用才能共享数据。两个应用通过ShareUID跑在同一个进程中是有要求的,需要这两个应用有相同的ShareUID并且签名相同才可以。 在这种情况下,它们可以相互访问对方的私有数据,比如data目录、组件信息等,不管它们是否跑在同一个进程中。如果它们跑在同一个进程中,还可以共享内存数据,它们看起来就像是一个应用的两个部分

(3)android系统会为每个进程分配一个独立的虚拟机,不同的虚拟机在内存分配上有不同的地址空间,所以不同的虚拟机中访问同一个类的对象会产生多个副本。
(4)使用多进程容易造成以下几个问题:
1.静态成员和单例模式完全失效;
2.线程同步机制完全失效:无论锁对象还是锁全局对象都无法保证线程同步;
3.SharedPreferences的可靠性下降:SharedPreferences不支持并发读写;
4.Application会多次创建:当一个组件跑在一个新的进程的时候,系统要在创建新的进程的同时分配独立的虚拟机,应用会重新启动一次,也就会创建新的Application运行在同一个进程中的组件是属于同一个虚拟机和同一个Application
同一个应用的不同组件,如果它们运行在不同进程中,那么和它们分别属于两个应用没有本质区别。

SerializableParcelable接口可以完成对象的序列化过程,当我们需要通过IntentBinder传递数据时就需要使用Parcelable或者Serializable。还有的时候我们需要把对象持久化到存储设备上或者通过网络传输给其他客户端,这时候也需要使用Serializable来完成对象的持久化。(具体使用见《开发艺术探索》P42

两者如何选择:SerializableJava中的序列化接口,其使用起来简单但是开销大,序列化和反序列化过程都需要大量I/O操作。ParcelableAndroid中的序列化方式,因此更适合用在Android平台上,它的缺点就是使用起来稍微麻烦些,但是效率很高,这是Android推荐的序列化方式,因此我们要首选ParcelableParcelable主要用在内存序列化上,通过Parcelable将对象序列化到存储设备中或者将对象序列化后通过网络传输也都是可以的,但是这个过程会稍显复杂,因此这两种情况下建议使用的是Serializable

(1)使用Bundle

Bundle实现了Parcelable接口,ActivityServiceReceiver都支持在Intent中传递Bundle数据。

我们可以在Bundle中附加我们需要传输给远程进程的信息并通过Intent发送出去。传输的数据必须可以序列化,比如基本类型、实现了Parcellable接口的对象,实现了Serializable接口的对象以及一些Android支持的特殊对象。

具体实现参考

Android中如何使用Bundle传递对象[使用Serializable或者Parcelable]

Android为什么要设计出Bundle而不是直接使用HashMap来进行数据传递?

1.Bundle内部是由ArrayMap实现的,ArrayMap的内部实现是两个数组,一个int数组是存储对象数据对应下标,一个对象数组保存key和value,内部使用二分法对key进行排序,所以在添加、删除、查找数据的时候,都会使用二分法查找,只适合于小数据量操作,如果在数据量比较大的情况下,那么它的性能将退化。而HashMap内部则是数组+链表结构,所以在数据量较少的时候,HashMap的Entry Array比ArrayMap占用更多的内存。因为使用Bundle的场景大多数为小数据量,我没见过在两个Activity之间传递10个以上数据的场景,所以相比之下,在这种情况下使用ArrayMap保存数据,在操作速度和内存占用上都具有优势,因此使用Bundle来传递数据,可以保证更快的速度和更少的内存占用。

 

2.另外一个原因,则是在Android中如果使用Intent来携带数据的话,需要数据是基本类型或者是可序列化类型,HashMap使用Serializable进行序列化,而Bundle则是使用Parcelable进行序列化。而在Android平台中,更推荐使用Parcelable实现序列化,虽然写法复杂,但是开销更小,所以为了更加快速的进行数据的序列化和反序列化,系统封装了Bundle类,方便我们进行数据的传输。

(2)使用文件共享

种方式简单,适合在对数据同步要求不高的进程之间进行通信,并且要妥善处理并发读写的问题。通过文件共享这种方式来共享数据度文件格式是没有具体要求的,比如可以是文本文件,也可以是XML文件,只要读/写双发约定数据格式即可。

SharedPreferences是一个特例,虽然它也是文件的一种,但是由于系统对它的读写有一定的缓存策略,即在内存中会有一份SharedPreferences文件的缓存,因此在多进程模式下系统对它的读写就变得不可靠,当面对高并发读写访问的时候,有很大几率会丢失数据,因此,不建议在进程间通信中使用SharedPreferences

(3)使用Messenger

Messenger是一种轻量级的IPC方案,它的底层实现就是AIDLMessenger是以串行的方式处理请求的,即服务端只能一个个处理,不存在并发执行的情形,详细的示例见原书。

Messenger中进行数据传递必须将数据放入Message中,而MessengerMessage都实现了Parcelable接口,因此都可以跨进程传输。简单来说,Message中所支持的数据类型就是所支持的传输类型。实际上,通过Messenger来传递MessageMessage中能使用的载体只有what,arg1,arg2,Bundle以及replyto.Message中的另一个字段Object在同进程中是很实用的,但是在进程间通信的时候,在Android2.2之前object字段不支持跨进程传输,即便在2.2以后,也仅仅是系统提供的实现了Parcelable接口的对象才能通过它来传输。这就意味着我们自定义的Parcelable对象是无法通过object字段来传输的。

(4)使用AIDL

Messenger是以串行的方式处理客户端发来的信息,如果大量的信息同时发送到服务端时,服务端仍然只能一个个处理,如果有大量的并发请求,那么Messenger就不太合适了。同时Messenger的作用主要是为了传递信息,很多时候我们可能需要跨进程调用服务端的方法,这种情形Messenger就无法做到了,但是我们可以利用AIDL来实现跨进程的方法调用。

AIDLAndroid Interface Definition Language)是Android接口定义语言的意思,它可以用于让某个Service与多个应用程序组件之间进行跨进程通信,从而可以实现多个应用程序共享同一个Service的功能。

大致流程分为服务端和客户端两个方面(具体示例见《开发艺术探索》p71):

服务端:服务端首先要创建一个Service来监听客户端的连接请求,然后创建一个AIDL文件,将暴露给客户端的接口在这个AIDL文件中声明,然后在Service中实现这个AIDL即可。

客户端:客户端所要做的事情就稍微简单一些,首先需要绑定服务端的Service,绑定成功后,将服务端返回的Binder对象转成AIDL接口所属的类型,接着就可以调用AIDL中的方法了。

1.AIDL支持的数据类型:基本数据类型、StringCharSequenceArrayListHashMapParcelable以及AIDL
2.以上6种类型的数据就是AIDL所支持的所有类型,其中自定义的Parcelable对象和AIDL对象必须要显式的import进来,不管它们是否和当前的AIDL文件位于同一个包内。如果AIDL文件中用到了自定义的Parcelable对象那么必须新建一个和它同名的AIDL文件,并在其中声明它为Parcelable类型。(具体实例代码见《开发艺术探索》P73
3.AIDL中除了基本数据类,其他类型的参数都要标上方向:inout或者inout
4.AIDL接口中支持方法,不支持声明静态变量;
5.为了方便AIDL的开发,建议把所有和AIDL相关的类和文件全部放入同一个包中,这样做的好处是,当客户端是另一个应用的时候,可以直接把整个包复制到客户端工程中。
6.RemoteCallbackList是系统专门提供的用于删除跨进程Listener的接口。RemoteCallbackList是一个泛型,支持管理任意的AIDL接口,因为所有的AIDL接口都继承自IInterface接口。

AIDL:Android Interface Definition Language,即Android接口定义语言。

Android 使用AIDL提供公开服务接口,使得不同进程间可以相互通信。

aidl对应的接口名称必须与aidl文件名相同不然无法自动编译

aidl对应的接口的方法不能加访问权限修饰符(记一下)

建立AIDL服务要比建立普通的服务复杂一些,具体步骤如下:

1)在Eclipse Android工程的Java包目录中建立一个扩展名为aidl的文件。该文件的语法类似于Java代码,但会稍有不同。

2)如果aidl文件的内容是正确的,ADT会自动生成一个Java接口文件(*.java)。

3)建立一个服务类(Service的子类)。

4)实现由aidl文件生成的Java接口。

5)在AndroidManifest.xml文件中配置AIDL服务,尤其要注意的是,标签中android:name的属性值就是客户端要引用该服务的ID,也就是Intent类的参数值。

(5)使用ContentProvider

专门用于不同应用间进行数据共享的方式,底层实现为Binder
1.ContentProvider主要以表格的形式来组织数据,并且可以包含多个表;
2.ContentProvider还支持文件数据,比如图片、视频等,系统提供的MediaStore就是文件类型的ContentProvider
3.ContentProvider对底层的数据存储方式没有任何要求,可以是SQLite、文件,甚至是内存中的一个对象都行;
4.要观察ContentProvider中的数据变化情况,可以通过ContentResolverregisterContentObserver方法来注册观察者;

(6)使用Socket

Socket是网络通信中“套接字”的概念,分为流式套接字和用户数据包套接字两种,分别对应网络的传输控制层的TCP和UDP协议。

Binder连接池

(1)当项目规模很大的时候,创建很多个Service是不对的做法,因为service是系统资源,太多的service会使得应用看起来很重,所以最好是将所有的AIDL放在同一个Service中去管理。

整个工作机制是:每个业务模块创建自己的AIDL接口并实现此接口,这个时候不同业务模块之间是不能有耦合的,所有实现细节我们要单独开来,然后向服务端提供自己的唯一标识和其对应的Binder对象;对于服务端来说,只需要一个Service,服务端提供一个queryBinder接口,这个接口能够根据业务模块的特征来返回相应的Binder对象给它们,不同的业务模块拿到所需的Binder对象后就可以进行远程方法调用了。

Binder连接池的主要作用就是将每个业务模块的Binder请求统一转发到远程Service去执行,从而避免了重复创建Service的过程。
(2)作者实现的Binder连接池BinderPool的实现源码,建议在AIDL开发工作中引入BinderPool机制。

Android跨进程通信IPC_第1张图片


你可能感兴趣的:(Android)