《Android开发艺术探索》笔记2( IPC机制)

##2.1 Android IPC简介
IPC是Inter-Process Communicatin的缩写,含义为进程间通信或跨进程通信,是指两个进程之间进行数据交换的过程。

线程是CPU调度的最小单元,同时线程是一种有限的系统资源。

进程是指一个执行单元,在移动设备中一般指一个程序或应用。一个进程可以包含多个线程,进程和线程是包含与被包含的关系。

在Android里面的主线程叫UI线程,UI线程中进行耗时操作会导致ANR。

在Android里最有特色的进程间通信方式就是Binder,还支持Socket。

多进程通信场景一种是一个应用因为某些原因自身需要采用多进程模式来实现,另外一种情况是当前应用需要向其他应用获取数据。

##2.2 Android中的多进程模式
###2.2.1 开启多进程模式
在Android中多进程是指一个应用中存在多个进程的情况。

在Android中使用多进程只有一种方法,给四大组件在AndroidMenifest中指定android:process属性,除此之外没有其他方法,也就是无法给一个线程或者一个实体类指定其运行时所在的进程。还有一个非常规的多进程方法,通过JNI在native层fork一个新的进程。

“:remote”和“com.lcf.test.remote”的区别,首先,“:”的含义是在当前的进程名前面附加上当前的包名,是一种简写,而后者是一个完整的命名方式,不会附加包名信息;其次,进程名以“:”开头的进程属于当前应用的私有进程,其他应用组件不可以和它跑在同一进程中,而进程名不以“:”开头的进程属于全局进程,其他应用通过ShareUID方式可以和它跑在同一进程中。

Android系统为每个应用分配一个唯一的UID,具有相同UID的应用才能共享数据。两个应用通过ShareUID跑在同一进程中是有要求的,需要两个应用具有相同的ShareUID且签名相同,它们可以互相访问私有数据。

###2.2.2 多进程模式的运行机制
Android为每个应用分配了一个独立的虚拟机,或者说为每个进程都分配一个独立的虚拟机,不同的虚拟机在内存分配上有不同的地址空间,会导致不同的虚拟机中访问同一个类的对象会产生多份副本。

使用多进程会造成的问题:

  1. 静态成员和单例模式完全失效
  2. 线程同步机制完全失效
  3. SharePreferences的可靠性下降
  4. Application会多次创建

##2.3 IPC基础概念介绍

###2.3.1 Serializable接口
Serializable是Java提供的一个序列化接口,是一个空接口,为对象提供标准的序列化和反序列化操作。使用Serializable比较简单,只需要在类的声明中指定一个标识serialVersionUID即可自动实现默认的序列化过程。

在Android中提供了新的序列化方式Parcelable接口,其过程要稍微复杂。

静态成员变量属于类不属于对象,不参与序列化过程

用transient关键字标记的成员变量不参与序列化过程
###2.3.2 Parcelable接口

public class User implements Parcelable{
public int userId;
public String userName;
public boolean = isMale;
public Book book;

	public User(int userId,String userName,boolean isMale){
		this.userId = userId;
		this.userName = userName;
		this.isMale = isMale;
 	}
 	
 	public int describeContent(){
 		return 0;
 	}
 	
 	public void writeToParcel(Parcel out,int flags){
 		out.writeInt(userId);
 		out.writeString(userName);
 		out.writeInt(isMale?1:0);
 		out.writeParcelable(book,0)
 	}
 		
 	public static final Parcelable.Creator<User> CREATOR = new Parcelable.Creator<User>(){
 		public User createFromParcel(Parcel in){
 			return new User(in);
 		}
 		
 		public User[] newArray(int size){
 			return new User[size];
 		}	
 	};
 	
 	private User(Parcel in){
 		userId = in.readInt();
 		userName = in.readString();
 		isMale = in.readInt() == 1;
 		book = in.readParcelable(Thread.currentThread().getContextClass().loader());
 	}
 
}

Parcel内部包装了可序列化的数据,可以在Binder中自由传输。

在序列化过程需要实现的功能有序列化、反序列化和内容描述。序列化功能由writeToParcel方法完成,最终是通过Parcel中的一系列write方法来完成;反序列化功能由CREATOR来完成,其内部表明了如何创建序列化对象和数组,并通过Parcel的一系列read方法来完成反序列化过程;内容描述功能由describeContents方法来完成,几乎所有情况下都返回0,仅当当前对象中存在对象描述符时,此方法返回1。

createFromParcel(Parcel in)从序列化后的对象中创建原始数据

newArray(int size)创建指定长度的原始对象数组

User(Parcel in)从序列化后的对象中创建原始对象

writeToParcel(Parcel out,int flags)将当前对象写入序列化结构柱

describeContents返回当前对象的内容描述

Serializable是Java中的序列化接口,其使用起来简单但是开销很大,序列化和反序列化过程需要大量I/O操作。而Parcelable是Android的序列化方式,更适合Android,缺点是用起来比较麻烦,但是效率高,主要用在内存序列化。

###2.3.3 Binder
Binder是Android中的一个类,实现了IBinder接口。从IPC角度,Binder是Android中的一个跨进程通信方式,Binder还可以理解为一种虚拟的物理设备,它的驱动设备是/dev/binder。从Android Framework角度,Binder是ServiceManager连接各种Manager和相应ManagerService的桥梁。从Android应用层来说,Binder是客户端和服务端进行通信的媒介,当bindService的时候,服务端会返回一个包含服务端业务调用的Binder对象,通过这个Binder对象,客户端可以获取服务端提供的服务或数据,服务包括普通服务和基于AIDL的服务。

Android开发中,Binder主要用于Service中,包括AIDL和Messenger,其中普通Service中的Binder不涉及进程间同学,而Messenger的底层其实是AIDL。

生成的Binder类中声明了一个内部类Stub,这个Stub就是一个Binder类,当客户端和服务端位于同一个进程时,方法调用不走跨进程的transact过程,当两者位于不同进程时,方法调用需要走transact过程,这个逻辑由Stub的内部代理类Proxy来完成。

DESCRIPTOR Binder的唯一标识,一般用当前Binder的类名表示

asInterface(android.os.IBinder obj)用于将服务端的Binder对象转换成客户端所需的AIDL接口类型的对象,这种转换是区分进程的,如果客户端和服务端位于同一进程,那么此方法返回服务端的Stub对象本身,否则返回系统封装后的Stub.proxy对象。

asBinder 返回当前Binder对象

onTransact 此方法运行在服务端的Binder线程池中,当客户的发起跨进程请求时,远程请求会通过系统底层封装后交由此方法处理,此方法返回false则客户端请求失败。

##2.4 Android中的IPC方式
###2.4.1 使用Bundle
四大组件都支持在Intent中传递Bundle数据,实现了Parcelable接口,可以方便地在不同的进程进行传输。
###2.4.2 使用文件共享
两个进程可以通过读/写同一个文件来交互数据。但是有局限性,比如并发读/写的问题。文件共享方式适合在对数据同步要求不高的进程之间进行通信,并且要妥善处理并发读/写问题。
###2.4.3 使用Messenger
通过Messenger可以在不同进程中传递Message对象,Messenger是一种轻量级的IPC方案,底层实现了AIDL,是以串行的方式处理消息。
###2.4.4 使用AIDL

  1. 服务端
    服务端首先需要创建一个Service用来监听客户端的连接请求,然后创建一个AIDL文件,将暴露给客户端的接口在这个AIDL文件中声明,最后在Service中实现这个AIDL接口即可。
  2. 客户端
    客户端首先绑定服务端的Service,绑定成功后将服务端返回的Binder对象转化成AIDL接口所属的类型,然后可以调用AIDL中的方法
  3. AIDL接口的创建
    AIDL文件支持的数据类型:基本数据类型、String、CharSequence、List(ArrayList)、Map(HashMap)、Parcelable和AIDL
  4. 远程服务端Service的实现
    首先创建一个Service,然后再xml中进行注册
  5. 客户端的实现
    首先绑定远程服务,绑定成功后将服务端返回的Binder对象转换成AIDL接口,然后可以通过这个接口去调用服务器的远程方法

###2.4.5 使用ContentProvider
ContentProvider是Android中提供的专门用于不同进程间进行数据共享的方式,底层实现是Binder,提供了一些基本操作,主要以表格的形式来组织数据。
###2.4.6 使用Socket
Socket即套接字,分为流式套接字和用户数据报套接字两种,分别对应于网络的传输控制层中的TCP和UDP协议。TCP协议是面向连接的协议,提供稳定的双向通信功能,TCP的建立需要经过三次握手,提供了超时重连机制,具有很高的稳定性;而UDP是无连接的,提供不稳定的单向通信功能,在性能上UDP具有更高的效率,缺点是不能保证数据一定能正确传输,尤其是在网络拥塞的情况下。
##2.5 Binder连接池
当AIDL比较多时,可以建立Binder池,一般用单例实现,同一个进程只初始化一次。Binder连接池的主要作用是将每个业务模块的Binder请求统一转发到远程Service中去执行,从而避免重复创建Service的过程。当增加新业务添加新的AIDL时,可以拓展BinderPoolImpl方中的queryBinder方法添加新的业务处理。可以提高AIDL的开发效率,并且避免大量的Service创建,
##2.6 选用合适的IPC方式

名称优点缺点适用场景
Bundle简单易用只能传输Bundle支持的数据类型四大组件间的进程间通信
文件共享简单易用不适合高并发场景,并且无法做到进程间即时通信无并发访问情形,交换简单的数据实时性不高的场景
AIDL功能强大,支持一对多并发通信,支持实时通信使用稍复杂,需要处理线程同步一对多通信且有RPC需求
Messenger功能一般,支持一对多串行通信,支持实时通信不能很好处理高并发情形,不支持RPC,数据通过Message进行传输,只能传输Bundle支持的数据类型低并发的一对多即时通信,无RPC需求,或无需返回结果的RPC需求
ContentProvider在数据源访问功能强大,支持一对多并发数据共享,可以通过Call方法扩展其他操作受约束的AIDL,主要提供数据源的CRUD操作一对多的进程间的数据共享
Socket功能强大,可以通过网络传输字节流,支持一对多并发实时通信实现细节稍微有点繁琐,不支持直接的RPC网络数据交换
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值