第二章 - IPC机制
2.1 Android IPC简介
1、IPC(Inter-Process Communication)进程间通信或跨进程通信
2、线程与进程?
线程 -CPU调度最小单元
进程 -①有限的系统资源一个执行单元
②可包含多个线程
2.2 Android中的多进程模式
1、Android使用多进程唯一方法 - 指定android:process属性
2、默认进程名是包名
android:process= “A”
A就是所指定的进程名
如果":A"那就是在默认进程名(包名)后面街上":A"
3、!注意:开启多进程并不是简单地给四大组件指定android:process
原因:开启多进程后,每一个进程拥有独立的虚拟机(不同虚拟机在内存分配上有不同的地址空间),Application,以及内存空间
导致的问题:
①静态成员变量和单例模式完全失效
②线程同步机制完全失效
③SharePreferences可靠性下降(SP底层通过读写xml实现)
④Application多次创建
4、解决方法 - 跨进程通信
例如:Intent、共享文件、SharePreferences、Binder的Messenger和ADIL以及相关Socket等
5、IPC基本概念 - Serializable接口、Parcelable接口以及Binder
Serializable接口:
①采用ObjectOutputStream序列化、ObjectInputStream反序列化
②serialVersionUID不是必须的,可以用来辅助序列化与反序列化
序列化时候会将serialVersionUID写入序列化文件中 ,反序列化时候会对比当前类的serialVersionUID,不一致则反序列化失败
Parcelable接口:
①Parcel类内部包装了可序列化的数据
②writeToParcel(Parceldest, int flags)方法 - 序列化功能
另:书本上相关描述并不详细,从网上补充了相关知识
Serializable与Parcelable接口的原理与区别
http://blog.csdn.net/androiddevelop/article/details/22108843?utm_source=tuicool&utm_medium=referral
区别与选取:
Serializable使用IO读写存储在硬盘上,而Parcelable是直接在内存中读写,很明显内存的读写速度通常大于IO读写,所以在Android中通常优先选择Parcelable。
6、Binder的使用及上层原理
①IPC角度 - Binder是Android中一种跨进程通信方式
Android Framework角度 -Binder是ServiceManager连接各种Manager的桥梁
Android应用层角度 - Binder是客户端与服务器端进行通信的媒介
②AIDL(Android interface Definition Language)示例分析Binder原理
建立了aidl后,gen目录下会自动生成一个.java文件
该类继承IInterface接口,声明了两个aidl中已声明的方法,并声明两个整型id用于标识这两个方法;
接着,声明了一个内部类Stub(一个Binder类);
当客户端与服务器端都位于同一个进程,方法调用不会走跨进程的transact过程,而两者位于不同进程时,方法调用就要走跨进程的transact过程,而这个逻辑就是Stub的内部代理Proxy来完成。
故核心就是实现它的内部类Stub与Stub的内部代理类Proxy
详解:
DESCRIPTOR - Binder唯一标识
asInterface(android.os.IBinder obj) - 用于转换服务器端Binder对象,转换成客户端所需的AIDL接口类对象
asBinder - 返回当前Binder对象
onTransact - 运行在服务器端中的Binder线程池,客户端发起跨进程请求时,远程请求会通过系统底层封装后交由此方法处理。
Proxy#定义的方法名 - 运行在客户端,客户端远程调用时,创建所需要的输入型Parcel对象_data、输出型Parcel对象_reply、返回值对象List,将参数写入data(如果有参数 ),接着调用transact方法发起RPC,同时挂起当前线程吗,然后服务器端onTransact方法被调用知道RPC过程返回。从_reply取出RPC返回结果,最后返回。
注意:①因为远程方法耗时,则不能在UI线程发起远程请求
②由于服务器端Binder方法运行在Binder线程池,所以Binder方法不论是否耗时,都应采用同步方法实现。
当然,我们也可以手动实现Binder,这与通过AIDL文件让系统工作原理实现是一样的。
可以说,AIDL文件本质是系统为我们提供的实现Binder的工具
另:Android aidl使用详解
http://blog.csdn.net/stonecao/article/details/6425019
使用AIDL
http://blog.csdn.net/saintswordsman/article/details/5130947
2.4 Android中的IPC方式
1、Bundle方式
Bundle实现了Parcelable接口,使用Bundle是最简单的进程通信方法。但是如果传输的数据不是Bundle所支持的,则无法使用。
Bundle无法支持所要传递的数据时候,通过Intent启动Service组件,让Service在后台计算,计算完毕之后启动目标组件。
核心思想:需要在A进程的计算任务转移到B进程的后台Service中执行。
2、文件共享方式(数据同步要求不高可以采用)
A中将结果序列化存到某个文件,B中读取该文件,并将其反序列化。
注意,多个线程对同一个文件进行读写操作,有可能导致数据丢失。
3、使用Messenger(一种轻量级的IPC方案,底层是AIDL)
在Messenger中放入需要传递的数据,在不同进程中传递Messenger对象。
但,如果有大量并发请求时候,Messenger就不太适合了;
另外,Messenger不能跨进程调用服务器端的方法,需要用到AIDL;
使用方法:
服务器端- 创建Service来处理客户端连接请求,同时创建一个Handler并通过它创建一个Messenger对象,然后在Service的onBind中返回这个Messenger对象底层的Binder。
客户端- 绑定服务器端Service,绑定成功后用服务器端返回的IBinder对象创建Messenger对象。如果需要服务器回应客户端,则还需要创建一个Handler并创建一个新的Messenger,并把这个Messenger对象通过Messenger的replyTo参数传递给服务器端,服务器端通过这个replyTo参数就可以回应客户端。
4、使用AIDL(大量并发请求,Messenger不适合)
P71~90有具体实现,这里不再累述
服务器端:
创建Service用来监听客户端请求、创建AIDL文件,将暴露给客户端的接口在这个AIDL接口中声明,最后在Service中实现这个AIDL。
客户端:
绑定服务器端的Service,将服务器端返回的Binder对象转成AIDL接口所属类型,即可调用AIDL中的方法了。
!注意:
AIDL文件中,并不是所有数据类型都可以使用的。
5、使用ContentProvider(底层Binder实现)
P91~103
例子:系统预置了许多ContentProvider(通讯录、日程表),可以通过ContentProvider的query、update、insert、delete方法访问这些应用获取相关数据。
实现:
写一个类继承ContentProvider并实现六个抽象方法,onCreate、query、update、insert、delete和getType。(onCreate由系统回调并运行在主线程中、其余5个方法由外部回调并运行在Binder线程池中。)
另:!注意ContentProvider的注册与权限
!注意query、update、insert、delete四大方法存在多线程并发访问的,内部要注意做好线程同步。
6、使用Socket(套接字)
P103 ~121
分为流式套接字和用户数据报套接字两种,分别对应于网络的传输控制层中的TCP(面向连接的协议,提供稳定的双向通信功能)和UDP协议(无连
接,提供不稳定的单向通信功能)
Socket使用:
①权限声明、不能在主线程中访问网络
②服务器端:Service启动时,会在线程中建立TCP服务,当有客户端连接,会生成一个新的Socket(每次创建的Socket可分别和不同的客户端通信)
当客户端断开连接,服务器端也会相应关闭对应Socket并结束通话线程。
2.6选用合适的IPC方式
方式 -适用场景
Bundle- 四大组件间的通信
文件共享- 无并发访问情形,交换简单的数据及时性不高的场景
AIDL - 一对多通信且有RPC需求
Messenger - 低并发的一对多即时通信,无RPC需求,或者无需返回结果的RPC需求
ContentProvider - 一对多的进程间的数据共享
Socket- 网络数据交换