Android开发艺术-第二章 IPC 机制
2.1 Android IPC 简单介绍
IPC 意为进程间通信或者跨进程通信,线程是 CPU 调度的最小单元,是一种有限的系统资源。
进程一般指一个执行单元。不论什么操作系统都须要对应的 IPC 机制。
如 Windows 上能够通过剪切板 管道 和邮槽来进行;Linux 上能够通过命名管道 共享内容 信号量等来进行。
在 Android 中最有特色的进程间通信方式就是 Binder 了,同一时候也支持 Socket 实现随意两个终端之间的通信。
2.2 Android 中的多进程模式
(1) 通过给四大组件指定 android:process 属性,能够开启多线程模式。默认进程的进程名字是包名。
android:process=":sunquan"
android:process="cn.sunquan.com.xx"
“:”指当前的进程名前面附加上当前的包名,简写的方式。其次进程名以“:”开头的进程属于当前应用的私有进程,其它应用组件不能够跟它在同一个进程中,而进程名不以“:”开头的进程属于全局进程。其它的应用能够通过 ShareUID 方式和它跑到同一个进程中。
(2) 系统为每个应用分配一个唯一的 UID,具备同样 UID 的应用才干共享数据。两个应用通过 ShareUID 跑在同一个进程中须要有同样 ShareUID 的而且签名同样。在这样的情况下它们能够相互訪问对方的私有数据比方 data 文件夹、 组件信息等。无论它们是否跑在同一个进程中。
当然假设它们跑在同一个进程中,那么除了能共享 data 文件夹、组件信息,还能够共享内存数据,或者说它们看起来像一个应用的两个部门。
(3) Android 为每个应用分配了一个独立的虚拟机,或者说为每个进程都分配了一个独立的虚拟机。不同的虚拟机在内存分配上有不同的地址空间,这就导致在不同虚拟机上訪问同一个类的对象会产生多份副本。所以执行在不同进程中的四大组件。仅仅要它们之间须要通过内存来共享数据,都会共享失败,这就是多进程所带来的主要影响。
(4) 多进程通常会造成例如以下几方面的问题:
1. 静态成员和单例模式全然失效:不在同一块内存
2. 线程同步机制全然失效:无论锁对象还是锁全局类都无法保证线程同步,由于不同进程锁的不是一个对象。
3. SharePreference 的可靠性下降:SharePreference 底层是通过读/写 XML 文件来实现,并发写显然可能出问题。SharePreference 不支持两个进程同一时候执行写操作,否则会有一定几率的丢失。
4. Application 会多次被创建:当一个组件跑在一个新的进程中。系统会创建新的进程同一时候分配独立的虚拟机。应用重新启动一次,会创建新的 Application。
执行在同一个进程中的组件属于同一个虚拟机和同一个 Application。不同进程的组件的确会拥有独立的虚拟机、Application 以及内存空间。
同一应用的不同组件,运作在不同的进程中,那跟它们分别属于两个应用的部门没有本质差别。
(5) 尽管不能直接共享内存可是通过跨进程通信还是能够实现数据交互。实现跨进程的方式:通过 Intent 来传递数据;共享文件;SharePreference;基于 Binder 的 Messager 和 AIDL 以及 socket。
2.3 IPC 基础概念介绍
(1) Serializable 是 Java 所提供的一个序列化接口,为对象提供标准的序列化和反序列化操作。Parceable 接口是 Android 提供的序列化方式。
(2) 实现 Serializable 接口,并声明一个 serialVersionUID 就可以让一个对象实现序列化。serialVersionUID 是一串long型的数字,用来辅助序列化和反序列化过程。
原则上序列化后的数字中的 serialVersionUID 仅仅有和当前累的 serialVersionUID 同样才干够正常的被反序列化。serialVersionUID 的具体工作机制:序列化得时候系统会把当前类的 serialVersionUID 写入序列化得文件里(也能够是其它中介),当反序列化得时候系统会检測文件里的 serialVersionUID,看它是否和当前类的 serialVersionUID 一致,如一致说明序列化的类的版本号和当前类的版本号同样。这个时候反序列化能够成功,否则说明当前类和序列化的类相比发生了某些变换,一般来说我们应该指定 serialVersionUID 的指。
注意:1.静态成员变量属于类不属于对象,不參与序列化过程;2.用transient 关键字标记的成员变量不參与序列化过程。
(3) 实现 Parceable 接口,一个类的对象能够通过实现序列化并能够通过 Intent 和 Binder 传递。Pacel 内部包装了可序列化的数据,能够在 Binder 中自由传输,能够直接序列化得有 Intent、Bundle、Bitmap、List、Map等。前提是它们里面的每个元素都是可序列化的。
(4) Serializable 和 Parceable 的差别:Serializable 是 Java 中的序列化接口,其使用起来简单可是由于大量 I/O 操作而开销大。
Parceable 是 Android 中的序列化方式。更适用在 Android 平台上,缺点是使用起来麻烦,可是效率高。Parceable 主要用在内存序列上,而序列化到存储设备上或者序列化后通过网络传输则建立适用 Serializable。
(5) Binder 是 Android 中的一个类。它实现了 Binder 接口。从 IPC 角度来说,Binder 是 Android 中一种跨进程通信方式。Binder 还能够理解为一种虚拟的物理设备,设备驱动是 /dev/binder。该通信方式在 Linux 中没有;从 Android Framework 角度来说。Binder 是ServiceManager 连接各种 Manager 和对应 ManagerService 的桥梁;从 Android 应用层来说,Binder 是client和服务端进行通信的媒介。当 bindService 的时候。服务端会返回一个包含了服务端业务调用的 Binder 对象。通过这个 Binder 对象,client就能够获取服务端提供的服务或数据。这里的服务包含普通服务和基于 AIDL 的服务。
Android 开发中。Binder 主要用在 Service 中。包含 AIDL 和 Messager。当中普通 Service 中的 Binder 不涉及进程间的通信。较为简单。
而 Messager 的底层事实上也是 AIDL。
(6) aidl工具依据 aide文件自己主动生成 Java 接口的解析:声明了几个接口的方法,同一时候声明几个整型 id 来标识这几个接口,id 用来标识在 transact 过程中client请求的是哪个方法。接着会声明一个 内部类 Stub 。这个 Stub 就是一个 Binder 类。 当client和服务器位于同一个进程中。则不会走 跨进程的 transact 过程,假设不在同一个进程,方法调用须要走 transact 过程,这个逻辑有 Stub 内部代理 Proxy 来完毕。
其核心实现就是它的内部类 Stub 和 Stub 内部代理 Proxy。
其几个方法分析例如以下:
1. asInterface (android.os.Binder obj):用于将服务端的 Binder 对象转换成client所需的 AIDL 接口类型的对象。
假设client和服务端位于同一进程。那么此方法返回服务端的 Stub 对象本身,否则返回系统封装后的 Stub.proxy 对象。
2. asBinder:用于放回当前 Binder 对象
3. onTransact :执行在服务端中的 Binder 线程中,当client发起跨进程请求中,远程请求会通过系统底层封装后由此方法来处理。
该方法的原型为 public Boolean onTranact (int code, android.os.Parcel data, android.os.Parcel reply, int flags)。服务端通过 code 能够确定client所请求的目标方法是什么,接着从 data 中取出目标方法所需的參数(假设目标方法有參数的话)。然后执行目标方法。当目标方法执行完毕后。就向 reply 中写入返回值(假设目标方法有返回值的话)。
假设此方法返回 false。那么client的请求会失败,能够通过这个特性做权限验证。
4. Proxy#[method] :执行在client,当client调用此方法时,首先创建该方法所需的输入型 Parcel 对象 _data、输出型 Parcel 对象 _repley 和返回值对象。把该方法的參数信息写入 _data 中(假设有參数),然后调用 transact 方法发起 RPC(远程过程调用)请求,同一时候当前线程挂起。然后服务端的 onTransact 方法会被调用,直到 RPC 过程返回后。当前线程继续执行,并从 _reply 中取出 RPC 过程的返回结果。最后返回 _reply 中的数据。
注意:1.当client发起请求时,由于当前线程会被挂机直到服务端进程返回数据。假设远程方法耗时,那么不能在 UI 线程中发起此远程请求。2.服务端的 Binder 方法执行在 Binder 的线程池中。无论 Binder 方法是否耗时都应採用同步的方式去实现,由于执行在一个线程中。
5. AIDL 文件的本质就是系统提供一种高速实现 Binder 的工具。我们能够自己手动写。也能够通过 AIDL 文件让系统自己主动生成。
6. Binder 有两个非常重要的方法:linkToDeath 和 unlinkToDeath。Binder 执行在服务端,服务端进程由于某些原因异常终止了,服务端的 Binder 连接断裂,导致client远程调用失败。通过 linkToDeath 能够给 Binder 设置一个死亡代理,当 Binder 死亡时,会收到通知,这时能够又一次发起连接请求从而恢复连接。
设置 Binder 死亡代理例如以下:
首先声明一个 DeathRecipient 对象,DeathRecipient 是一个接口,内部仅仅有一个方法 binderDied,当 Binder 死亡时候。系统回调此方法,我们能够在移除之前绑定的 binder 代理并又一次绑定远程服务。
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
if (mRemoteBookManager == null) return; mRemoteBookManager.asBinder().unlinkToDeath(mDeathRecipient, 0);
mRemoteBookManager = null;
// TODO:这里又一次绑定远程Service
}
};
其次在client绑定远程服务成功后。给 Binder 设置死亡代理:
mservice= IMessageBoxManager.Stub.asInterface(binder);
binder.linkToDeath(mDeathRecipient, 0)
当中 linkToDeath 的第二个參数是个标记位。直接设置0就可以。此外通过 Binder 的方法 isBinderAlive 也能够推断 Binder 是否死亡。
2.4 Android 中的 IPC 方式
(1) 使用 Bunble:Bunble 实现了 Parcelable 接口,能够在不同的进程间传输。
Bunble 不支持的类型无法通过它在进程间传输。
(2) 使用文件共享:由于 Android 系统基于 Linux,并发读/写文件能够没有限制的进行,两个线程对同一个文件进行写操作都是执行,但可能出问题。
文件共享方式适合对数据同步要求不高的进程间进行通信。并要妥善处理并发读/写的问题。
SharedPreference 是一个特例,属于文件的一种。但系统对其的读/写有一定的缓存策略,即在内存中会有一份 SharedPreference 文件的缓存,因此在多进程下,系统对它的读/写变得不可靠,面对高并发的读/写訪问。SharedPreference 有非常大几率丢失数据。所以不建议在进程间通信中使用 SharedPreference。
(3) 使用 Messenger:Messenger 是一种轻量级的 IPC 方案,其底层实现是 AIDL,以串行方式处理client发来的消息,其服务端一次处理一个请求。不存在并发执行的情形(对于有大量并发请求,Messenger 就不合适适用)。
(4) 使用 AIDL :首先服务端创建一个 Service 用来监听client的连接请求。创建一个 AIDL 文件,将暴露给client的接口在这个 AIDL 文件里声明。最后 Service 中实现这个 AIDL接口。client绑定服务端 Service,建立连接就可訪问远程服务端的方法。
1. AIDL 支持的数据类型:基本数据类型(int、long、chat、boolean、double 等);String 和 CharSequence;List(仅仅支持 ArrayList,里面的子元素都必须能够被 AIDL 支持)。Map(仅仅支持 HashMap。里面每个元素都必须被 AIDL 支持,包含 key 和 value);Parcelable(所以实现了 Parcelable 接口的对象);AIDL(全部的 AIDL 接口也能够在 AIDL 文件里使用)。
2. 自己定义的 Parcelable 对象和 AIDL 文件就算和当前 AIDL 文件位于同一包内也要显式 import。
3. 假设 AIDL 文件里用到自己定义 Parcelable 对象,必须新建一个和它同名的 AIDL 文件,并在当中声明它为 Parcelable 类型。
4. AIDL 中除了基本数据类型,其它类型的參数必须标上方向:in、out 或者 inout。in 表示输入类型參数,out 表示输出型參数。
5. AIDL 接口中仅仅支持方法,不支持声明静态常量。差别于传统的接口。
6. 为方便 AIDL 的开发。建议把全部和 AIDL 相关的类和文件全部放入同一个包中。当client是还有一个应用时。能够直接把整个包拷贝到clientproject中。
7. RemoteCallbackList 是系统专门提供用于删除进程 listener 的接口,RemoteCallbackList 是一个泛型。支持管理随意的 AIDL 接口,全部的 AIDL 接口都继承自 Interface 接口,在它的内部有一个 Map 结构专门用来保存全部的 AIDL 回调。其 Map 的 key 是 Binder 类型,value 是 Callback 类型。当client进程终止后,它能够自己主动移除client所注冊的 listener。
另外 RemoteCallbackList 内部自己主动实现了线程同步的功能,所以使用它进行注冊和解注冊时。不须要额外的线程同步工作。使用 RemoteCallbackList 须要注意是:它不是一个 List。遍历 RemoteCallbackList 须要依照下面方式进行。当中 beginBroadcast 和 finishBroadcast 必须配对使用,哪怕仅仅是获取 RemoteCallbackList 中的元素个数:
int N =mListenerList.beginBroadcast();
for(int i = 0; i < N; i++){
//
}
mListenerList.finishBroadcast();
- client的 onServiceConnected 和 onServiceDisconnected 方法都执行在 UI 线程中,所以不能够在它们里面直接调用服务端的耗时方法。
服务端的方法本身就执行在服务端的 Binder 线程池中,所以服务端方法本身就能够执行大量耗时操作,这个时候切记不要在服务端方法中开线程去进行异步任务。除非明白干什么。
- Binder 可能意外死亡。须要又一次连接服务。有两种方法:给 Binder 设置 DeathRecipient 监听,死亡时会收到 binderDied 方法回调,然后能够重连;一种是在 onServiceDisconnected 中重连。其二者差别:onServiceDisconnected 在client的 UI 线程中被回调。binderDied 在client的 Binder 线程池中被回调,不能訪问 UI。
- AIDL 使用经常使用的权限验证方法: 一是在 onBind 中进行验证,验证不通过返回 null,client直接无法绑定服务。验证方式如 使用 permission 验证,在 AndroidMenifest 中声明所需权限(自己定义 permission)。此方式同样适用于 Messenger 中。二是在服务端 onTransact 方法中进行权限验证。假设验证失败返回 false,这样服务端就不会终止执行 AIDL 中的方法从而达到保护服务端的效果。验证方式也能够採用permission。还能够採用 Uid 和 Pid 来做验证。通过 getCallingUid 和 getCallingPid 拿到client所属应用的 Uid 和 Pid。
这个两个參数能够用来做一些验证工作,比方验证包名。
(5) 使用 ContentProvider:ContentProvider 是 Android 中提供的专门用于不同应用间进行数据共享方式。和 Messenger 一样,其底层实现是 Binder,当事实上现过程比 AIDL 简单很多。主要以表格的形式来组织数据,能够包含多个表。还支持文件数据,比方图片和视频等。
文件数据和表格数据的结构不同,因此处理此类数据能够在 ContentProvider 中返回文件的句柄给外界从而让文件来訪问 ContentProvider,Android 系统提供的 MediaStore 功能就是文件类型的 ContentProvider。
ContentProvider 的底层数据看起来像一个 SQLite 数据库,可是 ContentProvider 对底层的数据存储方式没有不论什么要求。能够使用 SQLite 数据库,也能够使用普通文件,甚至能够採用内存中的一个对象进行数据的存储。要观察一个 ContentProvider 中的数据改变情况,能够通过 ContentResolver 的 registerContentObserver 方法来注冊观察者。能够通过 unregisterContentObserver 方法来解除观察者。
(6) 使用 Socket : Socket 被称为套接字,分为流式套接字和用户数据报套接字两种。分别对应于网络的传输控制层中的 TCP 和 UDP 协议。
2.5 Binder 连接池
当项目到一定程度,无限制的添加 Service 是不正确的,Service 是四大组件之中的一个。也是一种系统资源。我们须要将全部的 AIDL 放在同一个 Service 中去管理。工作机制:每个业务模块创建自己的 AIDL 接口并实现此接口,不同的业务模块之间是不能有耦合的。全部实现细节都单独开来,然后向服务端提供自己的唯一标识和其相对应的 Binder 对象。对于服务端来说仅仅须要一个 Service 就能够了,服务端提供一个 queryBinder 接口,这个接口能够依据业务模块的特征来返回对应的 Binder 对象给它们。不同的业务模块拿到所需的 Binder 对象后就能够进行远程方法调用,由此可见,Binder 连接池的主要作用就是将每个业务模块的 Binder 请求同一转发到远程 Service 中去执行,从而避免反复创建 Service。建议在开发中使用 BinderPool。
2.6 选用合适的 IPC 方式
Android开发艺术-第二章 IPC 机制的更多相关文章
- 第二章: IPC机制
这章关于进程的概念,没有深入太多,只做了解跟学习 IPC: Inter-Process Communication,进程间通信或者跨进程通信,两个进程之间进行数据交换的过程 2.1介绍 线程:CPU调 ...
- Android艺术开发探索——第二章:IPC机制(下)
Android艺术开发探索--第二章:IPC机制(下) 我们继续来讲IPC机制,在本篇中你将会学习到 ContentProvider Socket Binder连接池 一.使用ContentProvi ...
- Android开发艺术探索——第二章:IPC机制(中)
Android开发艺术探索--第二章:IPC机制(中) 好的,我们继续来了解IPC机制,在上篇我们可能就是把理论的知识写完了,然后现在基本上是可以实战了. 一.Android中的IPC方式 本节我们开 ...
- Android开发艺术探索——第二章:IPC机制(上)
Android开发艺术探索--第二章:IPC机制(上) 本章主要讲解Android的IPC机制,首先介绍Android中的多进程概念以及多进程开发模式中常见的注意事项,接着介绍Android中的序列化 ...
- 《android开发艺术探索》读书笔记(二)--IPC机制
接上篇<android开发艺术探索>读书笔记(一) No1: 在android中使用多进程只有一种方法,那就是给四大组件在AndroidMenifest中指定android:process ...
- Android开发艺术探索第五章——理解RemoteViews
Android开发艺术探索第五章--理解RemoteViews 这门课的重心在于RemoteViews,RemoteViews可以理解为一种远程的View,其实他和远程的Service是一样的,Rem ...
- Android开发艺术探索笔记——第一章:Activity的生命周期和启动模式
Android开发艺术探索笔记--第一章:Activity的生命周期和启动模式 怀着无比崇敬的心情翻开了这本书,路漫漫其修远兮,程序人生,为自己加油! 一.序 作为这本书的第一章,主席还是把Activ ...
- 《Android开发艺术探索》读书笔记 (13) 第13章 综合技术、第14章 JNI和NDK编程、第15章 Android性能优化
第13章 综合技术 13.1 使用CrashHandler来获取应用的Crash信息 (1)应用发生Crash在所难免,但是如何采集crash信息以供后续开发处理这类问题呢?利用Thread类的set ...
- 《Android开发艺术探索》读书笔记 (9) 第9章 四大组件的工作过程
第9章 四大组件的工作过程 9.1 四大组件的运行状态 (1)四大组件中只有BroadcastReceiver既可以在AndroidManifest文件中注册,也可以在代码中注册,其他三个组件都必须在 ...
随机推荐
- unix网络编程-配置unp.h头文件
第一步进入:www.unpbook.com,下载unp的随书代码.新建一个目录,将压缩包拷贝到这一目录下面,然后将压缩包直接解压:tar -zxvf 压缩包名.tar.gz 完成上一步后,进入到un ...
- 将自己的数据制作成voc格式
VOCdevkit2007文件下只保存VOC2007,VOC2007下只保存Annotations ImageSets JPEGImages. JPEGImages存放所有的图片数据(即训练测试验证的 ...
- python爬虫---从零开始(三)Requests库
1,什么是Requests库 Requests是用python语言编写,基于urllib,采用Apache2 Licensed 开源协议的HTTP库. 它比urllib更加方便,可以节约我们大量的工作 ...
- CSS中列表项list样式
CSS列表属性 属性 描述 list-style-属性 用于把所有用于列表的属性设置于一个声明中. list-style-image 将图象设置为列表项标志. list-style-position ...
- 【mybatis】mybatis数据源源码剖析(JNDI、POOLED、UNPOOLED)
一.概述 二.创建 mybatis数据源的创建过程稍微有些曲折. 1. 数据源的创建过程: 2. mybatis支持哪些数据源,也就是dataSource标签的type属性可以写哪些合法的参数? 弄清 ...
- 去BAT,你应该要看一看的面试经验总结
我去年12月份从上一家公司离职,一直到今年3月份,基本上都在面试中度过来的. 先交代下背景:坐标上海,做技术开发,我本人面试的职位是linux服务器开发,最倾向的职位是服务器开发主程或技术经理.我本人 ...
- vue 运行时 + 编译器 vs. 只包含运行时
https://cn.vuejs.org/v2/guide/installation.html#运行时-编译器-vs-只包含运行时 文档中的这个地方,说的不清楚 If you need to comp ...
- POJ 1201 Intervals(差分约束 区间约束模版)
关于差分约束详情可阅读:http://www.cppblog.com/menjitianya/archive/2015/11/19/212292.html 题意: 给定n个区间[L,R], 每个区间至 ...
- luogu3415 祭坛
先二分答案转化成判定问题. 考虑拿一根扫描线从 \(x=0\) 扫到 \(x=n\),每次移动扫描线更新每个位置它上面的点数和下面的点数,这样可以确定在当前的扫描线上哪些位置对于 \(y\) 轴方向是 ...
- WebStorm下载安装
下载地址:https://www.jetbrains.com/webstorm/ 注册码: http://idea.codebeta.cn