一个月以前,就把IPC看完了,但是这几天看到Window这一块和Activity的启动过程以后,就发现ipc用的地方太多了,没办法,又回来复习了一遍,并做了一点笔记。
《android技术开发探索》
多进程:
在Menifest文件中给组件指定android:process属性,有两种情况,1进程名为“com.example.ipcdemo:remote”这是当前应用的私有进程,其他应用的组件不可以和他跑在同一个进程中。2 进程名为“com.example.ipcdemo.remote”则属于全局进程,其他应用通过ShareUID方式就可以和他跑在同一个进程中了(前提是两个应用的ShareUID相同并且签名也得相同),这样就可以互相访问对方的私有数据、组件信息。
android为每一个进程分配了一个独立的虚拟机,不同的虚拟机在内存上有不同的地址空间,这就导致了不同的虚拟机在访问同一个类的对象会拆生多份副本。一般来说,多进程会造成如下几方面的问题;
静态成员和单例模式完全失效
线程同步机制完全失效
SharedPreferences的可靠性下降
Application会多次创建
序列化:
Serializable接口(java提供的)和Parcelable接口(android自带的)
Serializable需要提供一个serialVersionUID,这是为了反序列化可以被成功
Serializable接口:
静态变量不会参与序列化过程,因为他属于类而不是对象
用transient关键字标记的成员变量不参与序列化过程。
Parcelable接口:
只要实现这个接口,一个类的对象就可以实现序列化并可以通过Intent和Binder传递了
两者都可以进行Intent间的数据传输,Serializeable主要用于存储设备和网络传输,而Praceable主要用于内存上。
声明一个aidl接口,他会自动给你生成一个java类,在java类中,有几个方法比较重要:
asInterface()将服务端Binder对象转化成客户端所需要的AIDL接口类型的对象,如果是本进程就返回服务端Stub本身,否则返回Stub.proxy对象
asBinder:此方法用于返回Binder对象。
onTransact:这个方法运行在服务端的Binder线程池中,当客户端发起请求时,远程请求通过系统底层封装后交由此方法来处理。服务端通过code确定客户端所请求的目标方法是什么?接着从data取出目标参数,然后执行目标方法,执行完毕,如果有返回值就想reply中写入返回值。
proxy#getBookList: 这个方法运行在客户端,当客户端远程调用此方法时,它首先创建该方法所需要的输入性Parcel对象_data,输出型_reply和返回值对象List,然后把参数写入_data中,接着调用transact方法来发起RPC(远程过程调用),transact方法都做些了什么呢?它其实是一个本地方法,他的实现在Native层,里面进行一系列的函数调用,最终调用了talkWithDriver函数,通信过程交给了驱动来完成,这个函数通过ioctl系统调用,Client陷入了内核态,同时将当前线程挂起。驱动完成一系列的操作之后唤醒Server进程,然后服务端的onTransact方法会被调用,这个方法将结果返回给驱动,驱动将唤醒挂起的Client进程,当前线程继续执行,并从_reply 中取出结果。
————————————————————————————
进程隔离:为了保护操作系统进程互不干扰而设计的一组不同硬件和软件的技术。为了避免进程A写入进程B的情况发生。
用户空间/内核控件:
Kernel是一个高安全级别的东西,所以用户代码一般是不能访问Kernel的,一般的解决方案都是系统调用,通过一个统一的入口接口,所有的资源访问都是在内核控制下执行的。当一个任务执行系统调用而陷入内核代码时,我们就称为进程处于内核态。
内核模块/驱动:
Linux的动态可加载模块(Loadable Kernel Module)解决这个问题,模块是具有独立功能的程序,它可以被单独编译,但是不能被独立运行。它在运行时被链接到内核作为的一部分在内核空间运行。这样,android系统可以添加一个模块运行在内核空间,用户进程之间通过这个模块作为桥梁,就可以完成通信了。
在android系统中,运行在内空间的、负责各个用户进程间的通信通过Binder的内核模块叫做Binder驱动。(驱动一般是指设备驱动程序,是一种可以是计算机和设备通信的特殊程序)驱动就是操作硬件的接口,为了支持Binder通信过程,Binder使用了一种“硬件”,因此这个模块被称为驱动。
为什么要选择Binder?因为性能和安全//具体就不讲了,这儿有更好的回答。
Binder通信模型:
假设A要给B打电话(相当于ClientA要和ServerB通信),肯定需要一个电话本(相当于ServiceManager)和一个基站(Binder驱动)。
进程A和进程B是如何通信的呢?内核可以访问A和B的所有数据,所以最简单的方式就是通过内核中专。先把A数据copy到内核,再从内核copy到B中,用户空间要操作内和空间,就得需要系统调用,即copy_from_user,copy_to_user。
但是Binder不是这么干的!
首先,Server进程向SM(ServiceManager)注册,然后client向SM查询,需要一个Server,但是进程间的通信都要经过内核,即Binder驱动,它不会返回给client一个真实的object,而是一个objectProxy,它和真实的object有同样的功能,它唯一做的事情就是把参数包装然后交给驱动。然后驱动再把参数给真的Server,然后把结果返回来交给驱动,驱动给client。由于驱动返回的objectProxy太真实了,所以就有一种假象直接把Server进程里面的对象object传给了client进程,因此,我们可以说Binder对象是可以进行跨进程传递的对象。
由于SM和server不在一个进程里,所以Server进程向SM注册的过程也是跨进程通信,这儿是暗箱操作;SM中存在的也是Client的代理,然后Client向SM查询的时候,驱动会返回Client另外一个代理,Server进程的对象只有一个,其他的都是他的代理。
一句话总结:client只不过是Server的代理,代理对象协助驱动完成了进程通信。
我们使用AIDL接口经常会碰到这些类,每个类代表什么?
IBinder:是一个接口,代表了一种跨进程的能力,只要实现了这个接口,就能将对象进行跨进程传输了,这是驱动底层支持的,数据流经过驱动的时候,驱动会自动识别IBinder类型的数据,从而完成不同进程Binder本地对象以及Binder代理对象的转换。
IInterface代表的是Server对象具有什么能力,具体的说,就是aidl里面的接口。
Binder和BinderProxy都继承IBinder,具有远程传输的能力,BinderProxy是Binder的一个内部类,在跨进程时,驱动会自动完成两个对象的转换。
编译工具会给我们生成一个Stub静态的内部类,它继承了Binder,说明是一个Binder本地对象,它实现了IInterface接口,说明他有远程server承诺给client的能力。
我们在bind一个Service之后,在onServiceConnection的回调里面,就是通过asInterface方法拿到一个远程service的