《IPC机制》
"IPC"(Inter-Process Communication) --->进程间通信或者是跨进程通信,是指两个进程间数据交换的过程。(线程指的是CPU调度的最小单元,而进程一般指一个执行单元,通常指一个应用程序,一般一个进程可以包含多个线程。)
一、多进程(此处多进程,讨论的是一个应用中存在多个进程的情况)
1.同过在Mainifest中给四大组件设置属process来实现多进程。如果没有指定process的属性值,那么他将运行在默认的进程中,默认进程的进程名也就是process的属性值为当前应用的包名。
注:进程名以“:”开头的进程属于当前应用的私有进程,其他应用组件不可以和他跑在同一个进程中,而进程名不以“:”开头的进程属于全局进程,其他应用可以通过ShareUID的方式和他跑在同一个进程中。(Android系统为每个应用都分配了一个唯一的UID,具有相同的UID的应用才能共享数据。两个应用通过shareUID跑在同一个进程中是有要求的,需要这两个应用有相同的shareUID并且签名相同才可以。在这种情况下,他们可以互相访问对方私有数据,比如data目录、组件信息等,不管他们是否跑在同一进程中。当前如果他们跑在同一进程中,那么除了可以共享data目录、组件信息等,还一个共享内存数据或者说可以把他们看成一个应用的两部分。)
2.系统在创建新的进程的同时分配独立的虚拟机,这个过程就相当于是启动一个应用的过程。那么自然就会创建一个新的Application.
3.运行在同一个进程中组件是属于同一个虚拟机和同一个Application的。(相反也同理)。
4.总结:在多进程模式中,不同进程的组件拥有独立的虚拟机、application以及内存空间。
二、概念:序列化和反序列化(实现序列化的两个接口“Serializable”,“Parceable”)
把对象转换为字节序列的过程称为对象的序列化;把字节序列恢复为对象的过程称为对象的反序列化。
serializable和Parceable的区别:
Serializable是java中的序列化接口,其使用起来简单,但是开销比较大,序列化和反序列化过程需要大量I/O操作。而Parcelable是android中的序列化方式,因此更适合android开发平台,它的缺点是使用起来麻烦,但它的效率高。Parcelable主要用在内存序列化上。如果将对象序列化到存储设备中或者将对象序列化后通过网络传输建议使用Serializable更简单些。
【1】对象的序列化主要有两种用途:
1) 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中;
2) 在网络上传送对象的字节序列。
在很多应用中,需要对某些对象进行序列化,让它们离开内存空间,入住物理硬盘,以便长期保存。比如最常见的是Web服务器中的Session对象,当有 10万用户并发访问,就有可能出现10万个Session对象,内存可能吃不消,于是Web容器就会把一些seesion先序列化到硬盘中,等要用了,再把保存在硬盘中的对象还原到内存中。
当两个进程在进行远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式在网络上传送。发送方需要把这个Java对象转换为字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为Java对象。
【2】JDK类库中的序列化API
ObjectOutputStream代表对象输出流,它的writeObject(Object obj)方法可对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。
ObjectInputStream代表对象输入流,它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。
只有实现了Serializable和Externalizable接口的类的对象才能被序列化。Externalizable接口继承自 Serializable接口,实现Externalizable接口的类完全由自身来控制序列化的行为,而仅实现Serializable接口的类可以 采用默认的序列化方式 。
对象序列化包括如下步骤:
1) 创建一个对象输出流,它可以包装一个其他类型的目标输出流,如文件输出流;
2) 通过对象输出流的writeObject()方法写对象。
对象反序列化的步骤如下:
1) 创建一个对象输入流,它可以包装一个其他类型的源输入流,如文件输入流;
2) 通过对象输入流的readObject()方法读取对象。
【3】Binder:跨进程通信的方式
虽然android是基于Linux内核的操作系统,但是它有自己的进程间通信方式,即:Binder。
Socket也可以实现两个进程间通信。
【4】主要概念
IPC:
Inter-ProcessCommunication,进程间的通信或跨进程通信。简单点理解,一个应用可以存在多个进程,但需要数据交换就必须用IPC;或者是二个应用之间的数据交换。
Binder:
Binder是Android的一个类,它实现了IBinder接口。从IPC角度来说,Binder是Android中的一种跨进程通信方式和媒介。通过这个Binder对象,客户端就可以获取服务端提供的服务或数据,这里的服务包括普通服务和基于AIDL的服务。
AIDL:
Android Interface Definition language,它是一种Android内部进程通信接口的描述语言。
AIDL即Android Interface Definition Language(安卓接口定义语言),当我们创建了这个接口后,系统会自动生成其对应的Binder类,它继承了IInterface, 内部有一个静态抽象类Stub和Stub内部的Proxy类。其中Stub继承了Binder类,所以AIDL中的Stub即为一个Binder对象。 在服务端实现该接口后,支持在客户端远程调用(RPC)。
综上AIDL定义的接口,它除了是一个接口以外,它还是一个Binder对象,支持在接口和Binder之间相互转换(asBinder(), asInterface())。
详解参考:https://blog.csdn.net/daihuimaozideren/article/details/79456953(很详细!!)
Binder是一个类,它实现了IBinder接口,而IBinder接口定义了与远程对象的交互协议。通常在进行跨进程通信时,不需要实现IBinder接口,直接从Binder派生即可。
除了实现IBinder接口外,Binder中还提供了两个重要的接口。(2)onTransact(),服务端响应,用于接收调用请求
因为以上的原因,Binder成为了客户端与服务端的通信媒介,其主要用在Service组件应用中。
Service与客户端通信,有两种方式,AIDL和Messenger。AIDL基于Binder,而Messenger基于AIDL。
【4】Android中多进程的应用场景
链接:http://blog.spinytech.com/2016/11/17/android_multiple_process_usage_scenario/
https://blog.csdn.net/goodlixueyong/article/details/49853079
【5】使用多进程带来的影响
(1)静态成员变量和单例模式完全失效。ps:靠内存共享数据失败。
(2)线程同步机制失效。ps:既然多进程都不是同一块内存了,那么不管是锁对象还是锁全局类都无法保证线程同步,因为不同进程锁的不是同一个对象。 (3)SharePreferences的可靠性下降。ps:SharePreferences不支持两个进程同时执行写操作,否则会导致一定几率的数据丢失,这是因为sharePreserences底层是通过读写XML文件来实现的,并发写或者读显然会可能出现问题的。
(4)Application会多次创建。ps:运行在同一个进程中的组件是属于同一个虚拟机和同一个Applicationde ,同理,运行在不同进程中的组件是属于两个不同虚拟机和Application的。
AIDL使用的具体操作:
参考:https://blog.csdn.net/iromkoear/article/details/59706441
Binder原理涉及的主要类分析:
MessageCenter.aidl编译后自动生成的java类。所在位置如图:
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: D:\\Android\\MyAndroid\\AIDLDemo\\aidlclient\\src\\main\\aidl\\com\\viii\\aidlclient\\MessageCenter.aidl
*/
package com.viii.aidlclient;
public interface MessageCenter extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements com.viii.aidlclient.MessageCenter {
//Binder的唯一标识,一般使用当前Binder的类名表示
private static final java.lang.String DESCRIPTOR = "com.viii.aidlclient.MessageCenter";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.viii.aidlclient.MessageCenter interface,
* generating a proxy if needed.
* 用于将服务端的Binder对象转换成客户端所需的AIDL接口类型的对象,这种转换过程是区分进程的,如果客户端和服务端位于同一个进程,
* 那么此方法返回的就是服务端的Stub对象本身,否则返回的就是系统封装后的Stub.proxy对象。
*/
public static com.viii.aidlclient.MessageCenter asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.viii.aidlclient.MessageCenter))) {
return ((com.viii.aidlclient.MessageCenter) iin);
}
return new com.viii.aidlclient.MessageCenter.Stub.Proxy(obj);
}
/**
* 此方法用于返回Binder对象
* @return
*/
@Override
public android.os.IBinder asBinder() {
return this;
}
/**
* 客户端和服务器位于不同的进程时,需要走此方法
* @param code
* @param data
* @param reply
* @param flags
* @return
* @throws android.os.RemoteException
* 此方法运行在服务端中的Bider线程池中,当客户端发起跨进程请求时,远程请求会通过系统底层封装后交由此方法来处理。服务器通过code
* 可以确定客户端所请求的目标方法是什么,当目标方法执行完毕后,就向reply中写入返回值(如果目标方法有返回值的话),onTransact方法
* 的执行过程就是这样的。需要注意的是,如果此方法返回了false,那么客户端的请求会失败,因此我们可以利用这个特性来做权限验证,毕竟我们
* 也不希望随便一个进程都能远程调用我们的服务。
*/
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_getInfo: {
data.enforceInterface(DESCRIPTOR);
java.util.List _result = this.getInfo();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addInfo: {
data.enforceInterface(DESCRIPTOR);
com.viii.aidlclient.Info _arg0;
if ((0 != data.readInt())) {
_arg0 = com.viii.aidlclient.Info.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.addInfo(_arg0);
reply.writeNoException();
if ((_arg0 != null)) {
reply.writeInt(1);
_arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
/**
* Stub的内部的一个代理类
*/
private static class Proxy implements com.viii.aidlclient.MessageCenter {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override
public android.os.IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
/**
* 这个方法运行在客户端,当客户端远程调用此方法时,它的内部实现是这样的:首先创建该方法所需要的输入型Parcel对象
* -data、输出型Parcel对象_reply和返回值对象_result;然后把方法的参数信息写入_data中(如果有参数的话);接着调用
* transact方法来发起RPC(远程过程调用)请求,同时当前线程挂起;然后服务器端的onTransact方法会被调用,直到RPC过程返回后,
* 当前线程继续执行,并从_reply中取出RPC过程的返回结果;最后返回_reply中的数据。
* @return
* @throws android.os.RemoteException
*/
@Override
public java.util.List getInfo() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getInfo, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.viii.aidlclient.Info.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
/**
* 这个方法运行在客户端,它的执行过程和getInfo是一样的,只是addInfo没有返回值,所以它不需要从_reply中取出返回值。
* @param info
* @throws android.os.RemoteException
*/
@Override
public void addInfo(com.viii.aidlclient.Info info) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((info != null)) {
_data.writeInt(1);
info.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addInfo, _data, _reply, 0);
_reply.readException();
if ((0 != _reply.readInt())) {
info.readFromParcel(_reply);
}
} finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_getInfo = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addInfo = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
public java.util.List getInfo() throws android.os.RemoteException;
public void addInfo(com.viii.aidlclient.Info info) throws android.os.RemoteException;
}
演示demo:https://download.csdn.net/download/yhy123456q/10553551