在正式了解Android的IPC机制之前我们了解以下几个问题:
进程间通信(Inner-Process Comunication,简称IPC),就是指不同进程之间的信息传递。
进程是系统进行资源分配和调度的基本单位,是操作系统的结构的基础;一个应用至少有一个进程(当然也可以拥有多个进程),一个进程中有包含了多个线程(线程是CPU调度的最小单位),进程相当于是线程的容器,线程可以使用操作系统分配个进程的资源。
IPC机制是现代操作系统都存在的机制,而Android是基于Linux内核的OS,在Android中特有的方式Binder。
Android中在一个应用中创建多个进程的方式只有一种:在AndroidManifest.xml文件中申明四大组件的标签增加*“android:process=”""*属性即可。如下所示:
1.私有进程
<service
android:name=".service.MyAIDLService"
android:enabled="true"
android:exported="true"
android:process=":aidl_test" />
2.全局进程
<service
android:name=".service.MyAIDLService"
android:enabled="true"
android:exported="true"
android:process="com.kanlulu.aidl:aidl_test_test" />
通过第一种方式创建的多进程以":“开头的”:aidl_test“(省略了主进程它的全称为"com.kanlulu.aidl_test:aidl_test”) 是私有进程,其他应用组件不能和它运行在同一个进程中。
通过第二种写了完整名称的方式"com.kanlulu.aidl:aidl_test_test"是全局进程,可以使用相同ShareUID的方式运行在同一进程中(签名也需要一样)。
使用多进程可以带来的好处:
使用多进程也会带来一些问题和需要注意事项:
首先要说明的是同一个应用中创建的子进程在内存中的地址和主进程是不一样的,所以第1条中静态成员在不同进程间修改的其实是位于不同内存地址的副本,单例模式也是一样的。第2条原因和第一条是类似的;至于第3条,SharedPreferenced的并发访问会导致不可预料的错误。另外需要注意第四条,创建子进程时也会重新创建一次Application。
序列化是指将运行时的对象转换成二进制,然后保存到流、内存或者通过网络传输给其他端。
1、Serializable接口
Serializable接口是Java提供的序列化接口。
public interface Serializable {
}
下面是一个实现序列化接口的实体类:
package com.kanlulu.aidl_test.bean;
import java.io.Serializable;
public class Persion implements Serializable {
private String name;
private int age;
private String desc;
private static final long serialVersionUID = 8829975621220483374L;
public Persion() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
}
我们只需要实现Serializable
接口即可,另外还需要注意serialVersionUID属性;这个属性不是必须要创建的。
serialVersionUID:
简单来说它的作用就是用来序列化时的校验。只有当我们序列化前的serialVersionUID和我们反序列化是的serialVersionUID是一致的,反序列化才能成功,否则会报错:InvalidClassException
。
如果我们不自己创建serialVersionUID,但我们在反序列化时更改了对象属性名称或类型时就会导致反序列化失败。
serializable的序列化和反序列化分别通过ObjectOutputStream
和ObjectInputStream
来实现的,代码如下:
package com.kanlulu.aidl_test.utils;
import android.text.TextUtils;
import android.util.Log;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class SerialUtils {
private static final String TAG = "SerialUtils";
/**
* 序列化
*
* @param object 序列化对象
* @param path 序列化对象存储路径
* @return
*/
public synchronized static boolean saveObject(Object object, String path) {
if (object == null || TextUtils.isEmpty(path)) {
return false;
}
ObjectOutputStream outputStream = null;
try {
outputStream = new ObjectOutputStream(new FileOutputStream(path));
outputStream.writeObject(object);
outputStream.close();
return true;
} catch (IOException e) {
Log.e(TAG, e.getMessage());
} finally {
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return false;
}
/**
* @param path
* @param
* @return
*/
@SuppressWarnings("unchecked")
public synchronized static <T> T readObject(String path) {
if (TextUtils.isEmpty(path)) {
return null;
}
ObjectInputStream inputStream = null;
Object object = null;
try {
inputStream = new ObjectInputStream(new FileInputStream(path));
object = inputStream.readObject();
} catch (IOException | ClassNotFoundException e) {
Log.e(TAG, e.getMessage());
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return (T) object;
}
}
2、Parcelable接口
Parcelable接口是Android所特有的序列化接口,在序列化中原始对象会被转换成Parcel对象;Android对于Parcel描述:
Container for a message that can be sent through an IBinder.
可以通过IBinder发送的消息的容器。
package com.kanlulu.aidl_test.bean;
import android.os.Parcel;
import android.os.Parcelable;
/**
* Created by kanlulu
* DATE: 2018/11/30 15:54
*/
public class Animal implements Parcelable {
private String name;
private int age;
private String desc;
public Animal() {
}
/**
* 需要自己创建包含全部属性的构造方法
*/
public Animal(String name, int age, String desc) {
this.name = name;
this.age = age;
this.desc = desc;
}
/**
* 自动生成的
*/
protected Animal(Parcel in) {
name = in.readString();
age = in.readInt();
desc = in.readString();
}
/**
* 自动生成的
* 反序列化
*/
public static final Creator<Animal> CREATOR = new Creator<Animal>() {
@Override
public Animal createFromParcel(Parcel in) {
return new Animal(in);
}
@Override
public Animal[] newArray(int size) {
return new Animal[size];
}
};
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
@Override
public String toString() {
return "Animal{" +
"name='" + name + '\'' +
", age=" + age +
", desc='" + desc + '\'' +
'}';
}
@Override
public int describeContents() {
//几乎都返回 0,除非当前对象中存在文件描述符时为 1
return 0;
}
/**
* 序列化
*/
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeInt(age);
dest.writeString(desc);
}
}
Serializable和Parcelable的区别
1.在使用的便捷性上来说,Serializable只需要实现该接口后额外创建一个serialVersionUID属性即可,而使用Parcelable接口需要实现四个方法,比较繁琐。
2.通常如果我们需要保存数据到SD卡,或者需要进行网络传输数据,建议使用Serializable接口。
3.如果我们需要传递内存中的数据,建议使用Parcelable。
Android中有多种IPC机制,如AIDL、Messager、ContentProvider。而这些IPC机制的底层都是用Binder来实现的。
Binder的设计使用的是Client-Server结构,客户端进程通过获取服务端进程的代理,并向代理接口方法中读写数据来完成进程间的通信。它的传输过程只需要一次拷贝,为发送添加UID/PID身份,安全性更高。
对于Server而言,Binder可以看成是Server实现特定服务访问的接入地址,Client通过访问这个地址实现对Server的请求;对于Client而言,Binder可以看成是通向Server的管道入口,client要想和某个server通信首先必须建立管道和管道入口。
与其他IPC不同,Binder使用面向对象的思想描述作为访问接入点的Binder和它在Client中的入口:Binder是一个实体位于Server中的对象,Client通过Binder的引用访问Server。
Binder的通信框架定义了四个模型:Client、Server、ServiceManager和Binder驱动。其中Client、Server和ServiceManager运行在用户空间,Binder驱动运行在内核空间。他们的关系类似与互联网中:Server是服务器,Client是客户终端,ServiceManager是域名服务器(DNS),驱动是路由器。