一、在Android中什么样的情况下会使用多进程模式,如何开启多进程
1、什么情况下使用多进程模式
分担主进程的内存压力
2、如何开启多进程
四大组件,在 Manifest中 指定 android:process 属性
二、Android为什么采用Binder做为IPC机制
1、 Binder
Binder 是 Android中一种跨进程方式。
2、Android 要采用 Binder 作为 IPC 机制
Linux 进程间IPC方式:
1、管道:在创建时分配一个page大小的内存,缓存区大小比较有限;
2、消息队列:信息复制两次,额外的CPU消耗;不合适频繁或信息量大的通信;
3、共享内存:无须复制,共享缓冲区直接付附加到进程虚拟地址空间,速度快;
但进程间的同步问题操作 系统无法实现,必须各进程利用同步工具解决;
4、套接字:作为更通用的接口,传输效率低,主要用于不通机器或跨网络的通信;
5、信号量:常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。
因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
6、信号: 不适用于信息交换,更适用于进程中断控制,比如非法内存访问,杀死某个进程等;
7、Binder
1)性能上:Binder数据拷贝只需要一次,而管道、消息队列、Socket都需要2次,
但共享内存方式一次内存拷贝都不需要;
从性能角度看,Binder性能仅次于共享内存
2)稳定性
Binder是基于C/S架构的,Server端与Client端相对独立,稳定性较好;
而共享内存实现方式复杂,没有客户与服务端之别,
需要充分考虑到访问临界资源的并发同步问题,否则可能会出现死锁等问题;
从这稳定性角度看,Binder架构优越于共享内存。
3)安全上
传统Linux IPC的接收方无法获得对方进程可靠的UID/PID,从而无法鉴别对方身份;
Binder 基于C/S架构,系统中对外只暴露Client端,Client端将任务发送给Server端,
Server端会对Client的请求做校验,判断UID/PID是否满足访问权限,Binder的安全性更高
3、 Binder死亡监听 DeathRecipient
public void onServiceConnected(ComponentName name, IBinder service) {
if (service != null) {
mBinderManager = IBinderManager.Stub.asInterface(service);
try {
mBinderManager.asBinder().linkToDeath(new IBinder.DeathRecipient() {
@Override
public void binderDied() {//子进程的主线程中监听binder的死亡通知
mBinderManager.asBinder().unlinkToDeath(this, 0);
mBinderManager = null;
}
}, 0);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
三、IPC进程间数据传输
使用Bundle、使用文件共享、使用Messenger、使用AIDL、使用ContentProvider、使用Socket
1、 使用Bundle
Bundle实现了Parcelable接口,可以传递【基本类型数据和实现了Parcelable接口的对象】
2、 使用文件共享
常规的,就是一个进程把数据写入文件,另一个进程再从同一个文件中去读取数据
3、 使用Messenger
Messenger 对 AIDL做了封装,一次处理一个请求,因此再Server端不用考虑线程同步的问题。
Server端、Client端,分别创建要给Handler对象用于处理消息。
消息传到一端后,都是塞入Handler 的 MessageQueue 中实现串行处理消息,一次只能处理一个消息。
4、 使用AIDL
AIDL文件中不能使用中文注释,否则会生成代码为空的java文件
AIDL支持的数据类型:
- 1)基本数据类型
- 2)String、CharSequence
- 3)List:只支持 ArrayList,里面的每个元素必须能够被AIDL支持
aidl使用List类型时的使用 - 4)Map: 只支持 HashMap,里面每个元素必须能够被AIDL支持,包括key和value
- 5)Parcelable:所有实现了Parcelable接口的对象
- 6)AIDL:所有AIDL接口本身也可以再AIDL文件中使用
(最终生成的代码里,会是一个Binder对象,Binder有实现Parcelable接口)
5、 使用ContentProvider
用于不同应用间进行数据共享,底层也是Binder 实现
-
1)Server端app。
创建自定义 MyContentProvider 类,继承 ContentProvider 类,并实现相关方法 -
2)Server端app。在Manifest中注册
<provider android:name=".provider.MyContentProvider" android:authorities="com.xxx.common.provider" android:permission="com.xxx.PROVIDER" android:process=":provider"> </provider>
-
3)Client端app。访问MyContentProvider
Uri uri = Uri.parse("content://com.xxx.common.provider"); Cursor cursor = context.getContentResolver().query(uri, null, null, null, null); ...
6、 使用Socket
-
1)权限
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
-
2)具体Socket服务端、客户端代码
// TODO …
四、AIDL的语义
AIDL文件,是系统为提供的一种快速实现Binder的工具
AIDL支持的数据类型:
- 1)基本数据类型
- 2)String、CharSequence
- 3)List:只支持 ArrayList,里面的每个元素必须能够被AIDL支持
- 4)Map: 只支持 HashMap,里面每个元素必须能够被AIDL支持,包括key和value
- 5)Parcelable:所有实现了Parcelable接口的对象
- 6)AIDL:所有AIDL接口本身也可以再AIDL文件中使用
(最终生成的代码里,会是一个Binder对象,Binder有实现Parcelable接口)
// IWebAidlInterface.aidl
package com.xxx.common.webview;
// Declare any non-default types here with import statements
import com.xxx.common.webview.IWebBinderCallback;
interface IWebBinder {
/**
* methodName: 方法名 jsonParams: 方法参数 callback跨进程回调函数
*/
void handleJsFunction(String actionName, String jsonParams, IWebBinderCallback callback);
/**
* type:消息类型 data:数据
*/
void sendEventBus(int type, boolean success, String data);
}
五、RemoteCallbackList
RemoteCallbackList 是系统提供的,用于删除跨进程 listener 的类
-
Client端请求Server端,需要回调时,是通过AIDL定义的接口回调的。
AIDL定义的接口,实际上转为Binder对象传到Server端的
-
Binder 会把客户端传递过来的对象重新转化并生成一个【新的对象】
虽然我们在注册和解注册过程中使用的是同一个客户端,但是通过 Binder 传递到服务端后,却会产生全新的对象。
因为对象的跨进程传输,本质上都是反序列化的过程。 -
如果Server端,用普通的容器管理Client端传的listener的化,
客户端每次调用,Server端都会反序列化创建新的listener,导致容器中出现多个listener
在反注册listener的时候,找不到匹配的listener所以Server端管理listener,要用 RemoteCallbackList 来管理,不能用普通的容器
-
原理
1、RemoteCallbackList,存入其中的所有 IBinder 类型跨进程接口,都会 linkToDeath(),接口Binder销毁时,会从 RemoteCallbackList 中删除该 接口
2、RemoteCallbackList,用 ArrayMap<IBinder, Callback> 存储传入的回调函数
客户端进程传入的 Callback,到服务端进程、反序列化后是一个新对象,但是,
Callback 底层的 Binder对象 是同一个,RemoteCallbackList 中使用key-value方式存储回调函数,key 是 Binder,可以根据Binder,找到对应的回调函数 -
Demo:Service中
private final RemoteCallbackList<IPersonArrivedListener> mListenerList = new RemoteCallbackList<>(); // AIDL中定义的注册接口的方法,在Service中的具体实现 @Override public void registerListener(IOnNewPersonArrivedListener listener) throws RemoteException { mListenerList.register(listener); } // AIDL中定义的反注册接口的方法,在Service中的具体实现 @SuppressLint("NewApi") @Override public void unregisterListener(IOnNewPersonArrivedListener listener) throws RemoteException { mListenerList.unregister(listener); } //遍历 mListenerList 并 回调到 Client 端 private void onNewPersonArrived(Person person) throws RemoteException { synchronized (mListenerList) { int n = mListenerList.beginBroadcast(); try { for (int i = 0; i < n; i++) { IOnNewPersonArrivedListener listener = mListenerList.getBroadcastItem(i); if (listener != null) { listener.onNewPersonArrived(person); } } } catch (RemoteException e) { e.printStackTrace(); } mListenerList.finishBroadcast(); } }
六、 AIDL 中使用权限验证功能
1、 AndroidMenifest 中定义 Service所需的权限
<permission
android:name="com.wuc.aidlservice.permission.ACCESS_SERVICE"
android:protectionLevel="normal"/>
2、 IRemoteService的 onBind() 中调用
mContext.checkCallingOrSelfPermission(…) 验证权限
@Nullable
@Override
public IBinder onBind(Intent intent) {
int check = mContext.checkCallingOrSelfPermission("com.wuc.aidlservice.permission.ACCESS_SERVICE");
if (check == PackageManager.PERMISSION_DENIED) {
return null;
}
return mIBinder;
}
3、 AndroidMenifest 中声明权限
<uses-permission android:name="com.wuc.aidlservice.permission.ACCESS_SERVICE"/>
七、AIDL生成Java文件详细分析
1、 DESCRIPTOR
Binder 的唯一标识,一般用当前Binder 的类名表示。
2、asInterface(android.os.IBinder obj)
将服务端的 Binder对象转换成【客户端所需的AIDL接口类型的对象】
如果C-S 同进程,返回 Server端的 Stub对象本身
如果C-S 不同进程,返回 系统封装后的 Stub.Proxy对象。
3、 asBinder
返回当前 Binder 对象
4、Service # onTransact (int code, Parcel data, Parcel reply, int flags)
-
1)该方法 运行在 Server端 的 Binder线程池 ,
客户端发起的跨进程请求,会通过系统底层封装后交由此方法处理public Boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)
-
2)Server端通过code,确定Client端所请求的目标方法
-
3)如果目标方法有 参数,会从data中 取出目标方法所需参数
-
4)执行目标方法
-
5)如果有 返回值,方法执行完毕后,向reply中写入返回值
-
6)如果 onTransact 方法 返回 false, 客户端的请求会失败
5、 Proxy#sendEventBus
AIDL中定义的方法,运行在客户端
客户端远程调用此方法时:
-
1)创建该方法需要的输入型Parcel对象_data、输出型Parcel对象_reply
如果有返回值,还会创建返回值对象
如果有参数,把该方法的参数写入_data 中 -
2)客户端调用 IBinder # transact(…) 方法,发起RPC (远程调用)请求,同时 将当前线程挂起
-
3)服务端的 onTransact() 方法会被调用,
直到 RPC过程返回后,Client端挂起的线程继续执行
并从_reply 中取出 RPC 过程返回的结果 -
4)如果有返回值,最后还会返回_reply中的数据
-
由于Client端发起请求时线程会挂起,所以如果请求的方法很耗时,则不能放到UI线程发起请求
-
由于 Server端的Binder 方法,运行在 Binder的线程池中,所以 Binder 方法应该同步的方式去实现,应该它已经运行在一个线程中了。
八、Binder连接池,方便业务解耦
public class BinderPool {
public static final int BINDER_SPEAK = 0;
public static final int BINDER_CALCULATE = 1;
private Context mContext;
private IBinderPool mBinderPool;
private static volatile BinderPool sInstance;
private CountDownLatch mConnectBinderPoolCountDownLatch;
private BinderPool(Context context) {
mContext = context.getApplicationContext();
connectBinderPoolService();
}
public static BinderPool getInstance(Context context) {
if (sInstance == null) {
synchronized (BinderPool.class) {
if (sInstance == null) {
sInstance = new BinderPool(context);
}
}
}
return sInstance;
}
private synchronized void connectBinderPoolService() {
mConnectBinderPoolCountDownLatch = new CountDownLatch(1);
Intent service = new Intent(mContext, BinderPoolService.class);
mContext.bindService(service, mBinderPoolConnection, Context.BIND_AUTO_CREATE);
try {
mConnectBinderPoolCountDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public IBinder queryBinder(int binderCode) {
IBinder binder = null;
try {
if (mBinderPool != null) {
binder = mBinderPool.queryBinder(binderCode);
}
} catch (RemoteException e) {
e.printStackTrace();
}
return binder;
}
private ServiceConnection mBinderPoolConnection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mBinderPool = IBinderPool.Stub.asInterface(service);
try {
mBinderPool.asBinder().linkToDeath(mBinderPoolDeathRecipient, 0);
} catch (RemoteException e) {
e.printStackTrace();
}
mConnectBinderPoolCountDownLatch.countDown();
}
};
private IBinder.DeathRecipient mBinderPoolDeathRecipient = new IBinder.DeathRecipient() { // 6
@Override
public void binderDied() {
mBinderPool.asBinder().unlinkToDeath(mBinderPoolDeathRecipient, 0);
mBinderPool = null;
connectBinderPoolService();
}
};
public static class BinderPoolImpl extends IBinderPool.Stub {
public BinderPoolImpl() {
super();
}
@Override
public IBinder queryBinder(int binderCode) throws RemoteException {
IBinder binder = null;
switch (binderCode) {
case BINDER_SPEAK: {
binder = new Speak();
break;
}
case BINDER_CALCULATE: {
binder = new Calculate();
break;
}
default:
break;
}
return binder;
}
}
}
推荐阅读:
《Android开发艺术探索》 第二章 IPC机制
《深如理解LINUX内核 第三版涵盖2.6版》 第三章 进程 3.2节进程描述符
写给 Android 应用工程师的 Binder 原理剖析
如果需要深入了解,推荐GitYuan大大的 Binder 系列文章