概述
1.序列化与反序列化及android常见IPC方式:
1.1Serializable:
Serializable是java提供的序列化接口,为对象提供标准的序列化与反序列化操作。serialVersionUID是需要声明的,一般情况下赋值为1L,正常情况下不声明也没关系。但是在某些情况下可能会反序列化失败,这是因为在序列化对象的时候会把ID写入序列化文件中,在反序列化的时候会将这个ID和当前类的serialVersionUID进行比较,如果不一致就会反序列化失败。常见的情况如版本升级类的成员变量发生了改变,这时候如果没有声明serialVersionUID程序便会计算当前类的hash值把它赋值给serialVersionUID,那么和序列化文件中的ID比较肯定不一致,程序就会出现崩溃。但是如果类名或者成员变量的类型发生了改变,serialVersionUID虽然可以校验通过但是还是会序列化失败。
不参与序列化的成员:静态成员变量和transient关键字修饰的成员变量
父类与子类序列化:1.如果父类实现了Serializable接口,子类是的成员也是可以序列化的 2.如果子类实现了Serializable接口,父类的的成员不会被序列化赋值的 3.外部类实现了Serializable接口,内部类同样也是要实现Serializable接口,否则内部类不会被序列化
1.2.Parcelable:
序列化过程由writeToParcel方法来完成,反序列化由createFromParcel方法来完成。
Serializable与Parcelable的选择:Serializable使用简单但是开销比较大涉及比较多的IO操作,Parcelable是android平台推荐的序列化方法效率高但是使用稍麻烦。如果将对象序列化到存储设备或者网络传输建议使用Serializable,其他情况建议使用Parcelable。
1.3常用的IPC方式:
1.3.1.bundle
这个比较常见,Activity、Service和Receiver都是支持在intent中传递bundle的,这是一种最简单的IPC方式。
1.3.2.文件共享:
文件共享最大的问题就是并发,并发读写读出的内容可能不是最新的,并发写就更严重了,需要开发人员自己加线程同步来限制多个线程同时写操作。常见的文件共享有xml等文件以及sharedprefrence,sharedprefrence不支持进程间通信(不可靠),这是由它的缓存策略导致的,在内存中会有一份sharedprefrence文件的缓存。
1.3.3.Messenger:
这种底层是Binder,messenger实际上是对aidl做了封装,它需要和Handler配合起来使用,先来看看它的两个构造方法:
public Messenger(Handler target){
mTarget = target.getIMessenger();
}
public Messenger(IBinder target){
mTarget = IMessenger.Stub.asInterface(target);
}
首先服务端进程:创建service处理客户端连接请求,然后创建一个handler同时使用handler创建messenger(上图的第一个构造函数),最后在onBind方法中返回messenger底层的binder即可。
客户端进程:首先要绑定远程服务,绑定成功后根据回调回来的binder对象创建messenger,然后就可以使用该messenger对象向服务端进程发送message了
服务端回复客户端消息:首先客户端需要new一个replyMessenger和replyhHandler,然后客户端还需要messenger发送消息的时候需要带上一个replyTo,将replyMessenger赋值给replyTo。最后在服务端收到消息时,取出replyTo中的replyMessenger就可以回复消息给客户端了。
1.3.4.ContentProvider:
这个底层也是Binder实现的,是比较常规的操作,实现一个例子即可,如果app中有多个进程推荐使用contentprovider代替数据库,可以解决数据库不能进程间共享的问题。还有就是contentprovider不是线程安全的,会存在并发问题,需要自己在icud方法内做好线程同步。
1.3.5.Aidl:
aidl也是Binder实现的对其进行了封装,后面会有详细描述使用起来稍微复杂一点。
1.3.6.Socket管道
这个需要建立在三次握手成功的基础上,这种IPC方式在android中使用的不是很多。
1.3.7.RemoteViews:
android里面ui方面跨进程通信的方式,使用场景只有notification和桌面小部件。
2.Binber的基本概念
Binder驱动:如上图binder驱动在android系统中运行在内核空间的,负责各个用户进程通过binder进行通信的模块。binder驱动在程序运行时被链接到内核作为内核模块的一部分运行,它起的作用就是各个用户进程之间的桥梁,通过这个桥梁完成进程间通信。
Binder:通常情况下binder指的是一种机制,一种进程间通信的机制。就进程而言:对于server进程来说binder指的是binder本地对象,对于client进程来说binder指的是binderProxy对象,它只是server进程binder对象的远程代理。
IBinder:是一个接口,代表着跨进程传输的能力,是Binder的抽象。只要实现了IBinder接口,底层的binder驱动会自动对binder对象做处理,自动完成本地对象和代理对象之间的转换。
IInterface:也是一个接口,代表的是远程server对象具有什么样的能力,具体来说就是aidl里面的接口.
BinderProxy:Binderproxy是Binder的一个内部类,代表的是远程进程的Binder对象的本地代理,binderproxy也是实现了IBinder同样的也是具有跨进程传输能力。
Stub:在使用aidl的时候android自动生成的抽象静态内部类,它继承了Binder说明它是一个Binder本地对象。同时它又实现了IInterface接口,说明具有server端向client端传输数据的能力。stub是个抽象类,具体的实现需要我们自己完成。
StubProxy:stubproxy是stub的代理,持有Binder对象,同时实现了IInterface接口,说明也是具有server端向client端传输数据的能力。
asInterface:将服务端的Binder对象转换成客户端需要的aidl接口类型对象,在这里检查客户端和服务端是否处于同一进程,同一进程直接返回Binder本地对象(也就是stub),不同进程则返回Binder代理对象(也就是stubproxy)。
asBinder:返回当前的Binder对象,获取IIterface接口所关联的BinderProxy。
3.AIDL支持的数据类型:
1.基本数据类型:int, long, char, boolean, double
2.String和CharSequence类型
3.List,只支持Arraylist,并且list里面每一个元素都必须能被aidl支持
4.Map,只支持HaspMap,并且map里面每一个元素都必须能被aidl支持,包括key和value
5.Parcelable,所有实现了Parcelable接口的对象
6.AIDL,所有的aidl接口本身也是可以在aidl中使用的
4.AIDL过程需要注意的地方:
4.1.如果aidl文件中使用了自定义的parcelable对象
那么必须要创建一个和它同名的aidl文件,并在其中声明为Parcelable类型。并且aidl中除了基本类型,其他类型的参数必须要标明方向int、out或者inout,in输入型参数,out输出型参数,inout输入输出型参数。此外aidl接口只可以声明方法,不能声明静态常量的这一点是和传统的接口有区别的。
interface IBookManager {
List<Book> getBookList();
void addBook(in Book book);
}
package com.ryg.chapter_2.aidl;
parcelable Book;
4.2.aidl服务端的线程同步
由于aidl服务端方法是在binder线程池中执行的,当多个客户端来访问的时候会存在并发读写问题。比较常见的可以使用CopyOnWriteArrayList来支持并发读写自动实现线程同步,类似的还有ConcurrentHashMap。
4.3.跨进程解注册监听器RemoteCallbackList
aidl跨进程unRegisterListener解注册监听器的时候,由于跨进程客户端和服务端不是同一个对象所以会解注册失败。可以使用RemoteCallbackList处理,它是系统专门提供的用来删除跨进程listener的接口。它的实现原理:内部有一个map来保存所有的callback回调,map的key值是IBinder类型,value是callback类型。RemoteCallbackList本质上不是一个list,因此不可以有类似list的操作,使用的时候必须beginBroadcast和finishBroadcast配套使用。
4.4.aidl服务端耗时可能引起的ANR问题
如果确认服务端方法耗时的话客户端调用的时候放在非ui线程即可。
4.5.在aidl中使用权限验证功能
4.5.1一种方法是在onBind中做permission验证,验证不通过返回null使得客户端无法连接服务
首先需要在服务端的 AndroidMenifest 文件中声明所需权限
<permission android:name="com.example.xxx.permission.ACCESS_COMPUTE_SERVICE" android:protectionLevel="normal" />
<uses-permission android:name="com.example.xxx.permission.ACCESS_COMPUTE_SERVICE" />
客户端 AndroidManifest.xml 中添加权限:
<uses-permission android:name="com.example.xxx.permission.ACCESS_COMPUTE_SERVICE" />
在 Service 中的 onBind 方法中处理代码如下:
public IBinder onBind(Intent t) {
// 远程调用的验证方式
int check = checkCallingPermission("com.example.xxx.permission.ACCESS_COMPUTE_SERVICE");
if (check == PackageManager.PERMISSION_DENIED) {
// 权限校验失败
return null;
}
return mBinder;
}
4.5.2一种是在服务端的onTransact方法中来做验证,同样也可以做permission验证。最后还可以做包名验证,拿到客户端应用的uid和pid来做包名验证。
// 权限校验
private boolean checkPermission(Context context, String permName, String pkgName) {
PackageManager pm = context.getPackageManager();
if (PackageManager.PERMISSION_GRANTED == pm.checkPermission(permName, pkgName)) {
return true;
} else {
return false;
}
}
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
// 权限校验
String packageName = null;
String[] packages = getPackageManager().getPackagesForUid(getCallingUid());
if (packages != null && packages.length > 0) {
packageName = packages[0];
}
if (packageName == null) {
return false;
}
boolean checkPermission = checkPermission(getApplication(),
"com.example.xxx.permission.ACCESS_COMPUTE_SERVICE", packageName);
return checkPermission && super.onTransact(code, data, reply, flags);
}
5.AIDL过程(手动实现):
5.1首先声明一个aidl性质的接口,并且继承IInterface。
接口有一个方法,声明一个binder描述和id,如果接口中不止一个方法那就再多声明几个id就是了。实现如下:
public interface IHWCompute extends IInterface {
//第一步:声明binder描述,id和add方法
//DESCRIPTOR:binder的唯一标识,一般使用类名+aidl接口名字
static final String DESCRIPTOR = "com.thh.ipcdemo2binderpool.IHWCompute";
static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
public int add(int a, int b) throws android.os.RemoteException;
}
5.2实现stub类ComputeImpl
ComputeImpl继承了Binder说明它是一个Binder本地对象,又实现了IHWCompute接口因此它携带了某种客户端需要的能力(在这里就表示add方法)
//第二步:实现ComputeImpl(Stub类),该内部类里面的方法都是运行在server端的
public class HWComputeImpl extends Binder implements IHWCompute {
/**
* 构造函数
*/
public HWComputeImpl() {
this.attachInterface(this, DESCRIPTOR);
}
//add方法运行在客户端,流程如下:
//创建输入对象_data和返回对象_reply,接着调用transact发起RPC请求同时当前线程挂起。
//然后服务端的onTransact方法会被调起执行,RPC返回结果后当前线程继续执行,并获取RPC的结果
@Override
public int add(int a, int b) throws RemoteException {
//暂不实现
return 0;
}
//返回当前的Binder对象
@Override
public IBinder asBinder() {
return null;
}
//将服务端的Binder对象转换成客户端需要的aidl接口类型对象,在这里检查客户端和服务端是否处于同一进程,
//同一进程直接返回Binder本地对象,不同进程则返回Binder代理对象。
public static IHWCompute asInterface(IBinder obj) {
if (obj == null) {
return null;
}
IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if ((iin != null) && (iin instanceof IHWCompute)) {
return (IHWCompute) iin;
}
//待实现,返回Binder代理对象
return new Proxy(obj);
}
//该方法位于服务端的Binder线程池中,当客户端发起跨进程请求时,远程请求通过系统底层封装之后交给该方法来
//处理。onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)
//几个参数解释:code标识客户端请求的方法是什么,data带有方法所需的目标参数(如果有),
//然后方法开始执行执行完毕后向relply中写入返回值(如果有)。此方法返回true标识客户端请求成功,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_add: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.add(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
}
5.3实现stubProxy代理内部类,它是Binder(ComputeImpl)代理对象
这里直接贴下IHWCompute的完整代码
public interface IHWCompute extends IInterface {
//第一步:声明binder描述,id和add方法
static final String DESCRIPTOR = "com.thh.ipcdemo2binderpool.IHWCompute";
static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
public int add(int a, int b) throws android.os.RemoteException;
//第二步:实现ComputeImpl(Stub类),该内部类里面的方法都是运行在server端的
public class HWComputeImpl extends Binder implements IHWCompute {
/**
* 构造函数
*/
public HWComputeImpl() {
this.attachInterface(this, DESCRIPTOR);
}
@Override
public int add(int a, int b) throws RemoteException {
//暂不实现
return 0;
}
@Override
public IBinder asBinder() {
return null;
}
public static IHWCompute asInterface(IBinder obj) {
if (obj == null) {
return null;
}
IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if ((iin != null) && (iin instanceof IHWCompute)) {
return (IHWCompute) iin;
}
//待实现,返回Binder代理对象
return new Proxy(obj);
}
@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_add: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.add(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
/**
* ComputeImpl的代理
*/
private static class Proxy implements IHWCompute {
private IBinder mRemote;
public Proxy(IBinder remote) {
mRemote = remote;
}
@Override
public IBinder asBinder() {
return null;
}
@Override
public int add(int a, int b) throws RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(a);
_data.writeInt(b);
mRemote.transact(HWComputeImpl.TRANSACTION_add, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
}
}
5.4linkToDeath和unlinkToDeath,监听远程服务端Binder的死亡,死亡重连,代码示例如下:
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
IHWCompute mIHWCompute = IHWCompute.HWComputeImpl.asInterface(service);
try {
int count = mIHWCompute.add(3, 2);
Log.i("MainAcivity", "doWork1 count:" + count);
//失败重连
mIHWCompute.asBinder().linkToDeath(mBinderPoolDeathRecipient, 0);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
private IBinder.DeathRecipient mBinderPoolDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
doWork1();
}
};
5.5AIDL总结
aidl比较固定的模式:
1先定义一个接口标明你需要哪些能力,该接口必须要实现IInterface
2.接口定义完了我们需要一个跨进程传递的对象也就是Stub,它肯定继承自Binder。在这儿作区分,如果是Binder本地对象那么继承Binder且实现你刚刚定义的接口,如果是代理对象Proxy那么就持有IBinder(挟天子以令诸侯)且实现你刚刚定义的接口。
3.在客户端绑定服务onServiceConnected回调里调用asInterface将IBinder转化为客户端需要的接口类型的对象,就可以很方便的。
6.Binber连接池
线程池的背景:如果项目中有很多个业务模块都需要进程间通信,按照常规做法就需要创建很多个aidl文件以及很多个service,但是service创建过多会消耗系统资源会很重量级。binder连接池做的就是减少service的数量,将所有的aidl放到一个service中去管理。具体:客户端每个业务模块创建自己的aidl接口并实现,然后想服务端提供自己的唯一标识和对应binder对象。服务端提供一个queryBinder接口,根据业务模块id返回对应的binder对象给客户端,客户端不同模块拿到binder对象之后便可以进行远程调用了。binder链接池的作用就是讲各个业务模块的binder请求统一转发到远程service中去执行,避免了重复创建service的过程。按照我自己的理解就是binder连接池是一个容器,它将所有的aidl接口对应的binder装在一起,再另外提供一个查询aidl接口根据模块id返回不同的binder对象给你。
现在用一个例子来详细描述:
6.1.首先定义两个aidl接口:加解密接口和求和接口
interface ISecurityCenter {
String encrypt(String content);
String decrypt(String password);
}
interface ICompute {
int add(int a, int b);
}
6.2.接着是这两个aidl接口的实现
public class SecurityCenterImpl extends ISecurityCenter.Stub {
private static final char SECRET_CODE = '^';
@Override
public String encrypt(String content) throws RemoteException {
char[] chars = content.toCharArray();
for (int i = 0; i < chars.length; i++) {
chars[i] ^= SECRET_CODE;
}
return new String(chars);
}
@Override
public String decrypt(String password) throws RemoteException {
return encrypt(password);
}
}
public class ComputeImpl extends ICompute.Stub {
@Override
public int add(int a, int b) throws RemoteException {
return a + b;
}
}
6.3.aidl接口定义和实现完成之后,准备服务端和binder连接池,创建binder连接池aidl接口与实现
interface IBinderPool {
IBinder queryBinder(int binderCode);
}
public class BinderPool {
public static final int BINDER_SUCURITY_CENTER = 1000;
public static final int BINDER_COMPUTE = 1001;
private static BinderPool sInstance = null;
private final Context mContext;
private CountDownLatch mConnectBinderPoolCountDownLatch;
private IBinderPool mBinderPool;
public static BinderPool getInstance(Context context){
if (sInstance == null){
synchronized (BinderPool.class){
if (sInstance == null){
sInstance = new BinderPool(context);
}
}
}
return sInstance;
}
public BinderPool(Context context){
this.mContext = context.getApplicationContext();
connectBinderPoolService();
}
private synchronized void connectBinderPoolService() {
mConnectBinderPoolCountDownLatch = new CountDownLatch(1);
Intent intent = new Intent(mContext, BinderPoolService.class);
mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
try {
mConnectBinderPoolCountDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private ServiceConnection mServiceConnection = new ServiceConnection() {
@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();
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
private IBinder.DeathRecipient mBinderPoolDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
connectBinderPoolService();
}
};
public IBinder queryBinder(int binderCode){
IBinder binder = null;
try {
if (mBinderPool != null) {
binder = mBinderPool.queryBinder(binderCode);
}
} catch (RemoteException e) {
e.printStackTrace();
}
return binder;
}
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_SUCURITY_CENTER:
binder = new SecurityCenterImpl();
break;
case BINDER_COMPUTE:
binder = new ComputeImpl();
break;
}
return binder;
}
}
}
6.4.服务端service的实现
public class BinderPoolService extends Service {
private Binder mBinderPool = new BinderPool.BinderPoolImpl();
@Override
public void onCreate() {
super.onCreate();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mBinderPool;
}
@Override
public void onDestroy() {
super.onDestroy();
}
}
6.5客户端调用binder连接池
private void doWork() {
BinderPool binderPool = BinderPool.getInstance(this);
IBinder iBinder = binderPool.queryBinder(BinderPool.BINDER_COMPUTE);
ICompute iCompute = ICompute.Stub.asInterface(iBinder);
try {
int count = iCompute.add(3, 6);
Log.i("thhi", "[Cp3MainActivity doWork] count:" + count);
} catch (RemoteException e) {
e.printStackTrace();
}
IBinder iBinder1 = binderPool.queryBinder(BinderPool.BINDER_SUCURITY_CENTER);
ISecurityCenter iSecurityCenter = ISecurityCenter.Stub.asInterface(iBinder1);
try {
String encrypt = iSecurityCenter.encrypt("abcdefghyjklmn");
Log.i("thhi", "[Cp3MainActivity doWork] encrypt:" + encrypt);
String decrypt = iSecurityCenter.decrypt(encrypt);
Log.i("thhi", "[Cp3MainActivity doWork] decrypt:" + decrypt);
} catch (RemoteException e) {
e.printStackTrace();
}
}
最后
以上就是阔达羊为你收集整理的Android IPC机制:Binder与Aidl1.序列化与反序列化及android常见IPC方式:2.Binber的基本概念3.AIDL支持的数据类型:4.AIDL过程需要注意的地方:5.AIDL过程(手动实现):6.Binber连接池的全部内容,希望文章能够帮你解决Android IPC机制:Binder与Aidl1.序列化与反序列化及android常见IPC方式:2.Binber的基本概念3.AIDL支持的数据类型:4.AIDL过程需要注意的地方:5.AIDL过程(手动实现):6.Binber连接池所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复