IPC—进程间通信的基础概念
一、Android中的多进程模式
1、Android中多进程是指一个应用中存在多个进程的情况,因此这里不讨论两个应用之间的情况,首先在Android中使用多进程只有一种方法,那就是给四大组件指定android:process。默认进程名是包名。
<activity
android:name=".MainActivity"
android:configChanges="orientation|screenSize"
android:launchMode="standard">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".SecondActivity"
android:process=":remote" />
<activity
android:name=".ThirdActivity"
android:process="com.liuguilin.multiprocesssample.remote" />
上面示例分别为SecondActivity和ThirdActivity指定了process属性,并且属性值不同,当SecondActivity启动时,系统会为它创建一个单独的进程,进程名为con.liuguilin.multiprocesssample:remote;当ThridActivity启动的时候,系统也会为他创建一个单独的进程,进程名com.liuguilin.multiprocesssample.remote,同时,入口Activity是MainActivity,没有为他指定process属性,那么他运行在默认进程中,当我们运行的时候就可以看到,进程列表末尾存在三个进程。查看进程方法如下:
C:\Users\hWX393093>adb shell ps | find "hfy"
u0_a128 27860 501 2278864 81132 0 0000000000 S demo.hfy.com.viewtest
SecondActivity和ThirdActivity的进程名分别为“:remote”和包名.remote,区别在两面:
a、”:“的含义,是指在当前的进程名前附加上包名,这是一种简写的方法,对于SecondActivity来说,他完整的进程名为 com.liuguilin.multiprocesssample:remote,而对于ThirdActivity中的申明方式,它是一种完整的命名方式,不会附加包名信息;
b、进程以”:“开头的属于当前应用的私有进程,其他应用的组件不可以和它跑在同一个进程中,而进程名不以”:“开头的进程属于全局进程,其他应用通过ShareUID方式可以和它跑在同一进程中。
一般使用私有进程。即“:”的形式。
2、Android为每个进程都分配一个独立的虚拟机,不同的虚拟机在内存上有不同的地址空间,这导致在不同的虚拟机中访问同一个类的对象会产生多份副本,就我们这个例子来说,在两个进程中都存在一个UserManager类,并且这两个类是互不干扰的,在一个进程中修改mUseld的值只会影响当前进程,对其他进程不会造成任何影响,这样我们就可以理解为什么在MainActivitv中修改了mUserld的值,但是在 SecondActivity 中这个值却 没有发生改变这个现象。
所有运行在不同进程中的四大组件,只要它们之间需要通过内存来共享数据,都会共享失败这也是多进程带来的主要影响,正常情况下四大组件中间不可能不通过一些中间层来共享数据,那么通过简单地指定进程名来开启多进程都会无法正确运行。
一般来说,使用多进程会造成如下几方面的问题:
a.静态成员和单例模式完全失效。----因为不同内存空间,是互不干扰的。
b.线程同步机制完全失效。----因为不是同一块内存,即锁对象不是同一个。
c.SharedPreferences的可靠性下降。----底层是xml,并发读写可能有问题的。
d.Application会多次创建。----运行在不同进程的组件是属于不同虚拟机和Application。
二、IPC基础概念 — Serializable接口、Parcelable接口、Binder
1、Parcelable和Serializable的区别
a、Serializable是Java中的序列化接口,其使用起来简单但是开销很大,序列化和反序列化过程需要大量I/O操作。
b、Parcelable是Andrord中的序列化方式,因此更适合用在Android平台上,它的缺点就是使用起来稍微麻烦点,但是它的效率很高,这是Android推荐的序列化方式,因此我们要首选Parcelatle。
c、Parcelable主要用在内存序列化上,通过Parcelable将对象序列化到存储设备中或者将对象序列化后通过网络传输也都是可以的,但是这个过程会稍显复杂,因此在这两种情况下建议大家使用Serializabie。
2、Binder是什么?
a、Binder是Android中的一个类,它实现了IBinder接口。
b、从IPC角度来说,Binder是Android中的一种跨进程通信方式。
c、Binder还可以理解为一种虚拟的物理设备,它的设备驱动是/dev/binder,该通信方式在Linux中没有;
d、从AndroidFramework角度来说,Binder是ServiceManager连接各种Manager(ActivityManager、WindowManager,等等)和相应Managerservice的桥梁:
e、从Android应用层来说,Binder是客户端和服务端进行通信的媒介,当bindService的时候,服务端会返回一个包含了服务端业务调用的Binder对象,通过这个Binder对象,客户端就可以获取服务端提供的服务或者数据,这里的服务包括普通服务和基于AIDL的服务。
Android开发中,Binder主要用在Service中,包括AIDL和Messenger,其中普通Service中的Binder不涉及进程间通信,所以较为简单,无法触及Binder的核心,而Messenger的的底层其实是AIDL。
3、Binder详解
Binder工作机制:
a、当客户端发起远程请求时,由于当前线程会被挂起直至服务器进程返回数据,所以如果一个远程方法是很耗时的,那么不能再UI线程中发起此远程请求。
b、由于服务器的Binder方法运行在Binder的线程池中,所以Binder方法不管是否耗时都应该采用同步的方式去实现,因为他已经运行在一个线程中了。
Binder死亡:Binder运行在服务端进程,如果服务端进程由于某种原因异常终止,这个时候我们到服务端的Binder连接断裂(称之为Binder死亡),会导致我们的远程调用失败。更为关键的是,如果我们不知道Binder连接已经断裂,那么客户端的功能就会受到影响。为了解决这个问题,Binder中提供了两个配对的方法linkTopeath和unlinkTopeath,通过 linkToDeath我们可以给Binder设置一个死亡代理,当Binder死亡时,我们就会收到通知,这个时候我们就可以重新发起连接请求从而恢复连接。那么到底如何给Binder设置死亡代理成?也很简单:
首先,生命一个Deathleciient对象、Deathleciient是一个接口,其内部只有一个方法binderDied,我们需要实现这个方法,当Binder死亡的时候,系统就会回调binderDied方法,然后我们就可以移出之前绑定的binder代理并重新绑定远程服务;
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
if(mBookManager == null){
return;
}
mBookManager.asBinder().unlinkToDeath(mDeathRecipient,0);
mBookManager = null;
//这个可以重新绑定远程service
}
};
其次,在客户端绑定远程服务成功之后,给binder设置死亡代理;
mService= IMessageBoxManager.Stub.asInterface(binder);
binder.linkToDeath(mDeathRecipient,0);
其次linkToDeath的第二个参数为标记位,我们直接设置为0即可,经过上述的步骤,我们就可以给Binder设置死亡代理了,当Binder死亡之后我们就可以收到通知了,另外。通过Binder的方法isBinderAlive也可以判断Binder是否死亡。
到这里,IPC的基础知识就介绍完毕了。