本文作者:CodingBlock 文章链接:https://cloud.tencent.com/developer/article/1351741
在Android中进程间通信是比较难的一部分,同时又非常重要,针对进程间通信,博主会用四篇文章来介绍,本篇文章为IPC系列的开篇,主要介绍一些IPC中用到的一些概念、基础等,目的是让读者朋友们在学习IPC之前对一些必要的知识有一个大体的把握。在Android中进程间通讯的方式有很多种,在后续的三篇中会分别介绍每一种方式的实现过程已经各自的优缺点。
IPC(全称:Inter-Process Communication)为进程间通讯,指至少两个进程间传递数据或信号的一些技术活方法。
注:进程间通讯是至少两个进程之间发生的事情,我们通常习惯性的会把一方称为客户端,一方称为服务端,在后续的文章也会多次出现客户端和服务端,没接触过进程间通信的童鞋可能一开始会不太习惯,这里要注意一下。
无论是在计算机系统还是Android系统中每个进程都有自己一部分独立的系统资源,彼此是隔离的,为了能是不同的进程互相访问资源并协同工作,就需要用到进程间通讯。
RPC(Remote Procedure Call)—远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。(-来自百度百科)
在后面介绍AIDL时会用到RPC的概念,在这里简要说明一下RPC在Android的进程间通讯所扮演的角色,以博主本人的理解,简单来说RPC机制就是指在本地即可调用远程进程中的方法,而不需要关心其底层实现。
在四大组件的AndroidManifest配置中配置process属性
比如这个:
<service
android:name=".messager.MessengerService"
android:exported="true"
android:process=":remote" />
“:”开头和不带“:”的有什么区别:
“:”开头的进程属于当前应用的私有进程,其他应用的组件不能和它跑在同一进程下。
不带“:”的进程属于全局进程,其他应用可以通过ShareUID和它跑在同一进程下。
Android系统会为每一个应用分配一个UID,具有相同的UID才能共享数据。
通过ShareUID跑在同一进程中需要两个应用有相同的ShareUID并且有相同的签名才可以。
Android系统为每一个进程分配一个独立的虚拟机,不同的虚拟机在内存分配上有不同的地址空间,这就导致不同的虚拟机访问同一个类的对象会产生多个副本。
使用多进程会导致如下问题:
使用Serializable进行序列化很简单,只需要实现Serializable接口,然后为类指定一个serialVersionUID即可。
Serializable中的serialVersionUID工作机制:
使用Parcelable进行序列化比Serializable要麻烦一些,需要实现Parcelable接口,并实现一些必要方法,其通常形式如下:
public class Contact implements Parcelable {
public int phoneNumber;
public String name;
public String address;
public Contact(int phoneNumber, String name, String address) {
this.phoneNumber = phoneNumber;
this.name = name;
this.address = address;
}
public Contact() {
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(phoneNumber);
dest.writeString(name);
dest.writeString(address);
}
public void readFromParcel(Parcel parcel) {
phoneNumber = parcel.readInt();
name = parcel.readString();
address = parcel.readString();
}
public final static Creator<Contact> CREATOR = new Creator<Contact>() {
@Override
public Contact createFromParcel(Parcel source) {
return new Contact(source);
}
@Override
public Contact[] newArray(int size) {
return new Contact[size];
}
};
public Contact(Parcel parcel) {
phoneNumber = parcel.readInt();
name = parcel.readString();
address = parcel.readString();
}
}
一个类只要实现了Parcelable接口,其对象就可以实现序列化并可以通过Intent和Binder传递。
AIDL中自动生成的Binder接口类的一些方法:
Binder运行在服务端进程,如果服务端进程被异常终止,Binder链接就会断裂,导致我们远程调用失败。但是此时我们并不知道Binder链接已经中断,为了解决这个问题,Binder中提供了两个配对的方法:
首先声明一个DeathRecipeint对象,然后通过binder.linkToDeath()方法将其绑定到binder上。在DeathRecipeint内部有一个方法binderDied,当Binder死亡后,系统就会回调binderDied方法。
示例代码如下:
private ServiceConnection serviceConnection = new ServiceConnection(){
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mIContactsManager = IContactsManager.Stub.asInterface(service);
Log.i(TAG, "onServiceConnected: mIContactsManager=" + mIContactsManager);
try {
// 给service设置死亡代理
service.linkToDeath(mDeathRecipient, 0);
} catch (RemoteException e) {
e.printStackTrace();
}
}
...
};
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
// 当binder挂掉后就会执行此方法
if (mIContactsManager == null) {
return;
}
// 首先移除之前绑定的死亡代理
mIContactsManager.asBinder().unlinkToDeath(mDeathRecipient, 0);
mIContactsManager = null;
// 然后重新绑定远程服务
bindService(intent, serviceConnection, BIND_AUTO_CREATE);
}
};
进程间通讯的基础知识就先介绍到这里,接下来将开始针对每种进程间通讯方式作出详细的介绍。
最后想说的是,本系列文章为博主对Android知识进行再次梳理,查缺补漏的学习过程,一方面是对自己遗忘的东西加以复习重新掌握,另一方面相信在重新学习的过程中定会有巨大的新收获,如果你也有跟我同样的想法,不妨关注我一起学习,互相探讨,共同进步!
参考文献:
本文作者:CodingBlock 文章链接:https://cloud.tencent.com/developer/article/1351741