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配合起来使用,先来看看它的两个构造方法:
1
2
3
4
5
6
7public 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接口只可以声明方法,不能声明静态常量的这一点是和传统的接口有区别的。
1
2
3
4
5interface IBookManager { List<Book> getBookList(); void addBook(in Book book); }
1
2
3package 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 文件中声明所需权限
1
2
3<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 中添加权限:
1
2<uses-permission android:name="com.example.xxx.permission.ACCESS_COMPUTE_SERVICE" />
在 Service 中的 onBind 方法中处理代码如下:
1
2
3
4
5
6
7
8
9
10public 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来做包名验证。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26// 权限校验 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就是了。实现如下:
1
2
3
4
5
6
7
8public 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方法)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65//第二步:实现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的完整代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99public 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的死亡,死亡重连,代码示例如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27private 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接口:加解密接口和求和接口
1
2
3
4
5
6interface ISecurityCenter { String encrypt(String content); String decrypt(String password); }
1
2
3
4interface ICompute { int add(int a, int b); }
6.2.接着是这两个aidl接口的实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19public 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); } }
1
2
3
4
5
6
7public class ComputeImpl extends ICompute.Stub { @Override public int add(int a, int b) throws RemoteException { return a + b; } }
6.3.aidl接口定义和实现完成之后,准备服务端和binder连接池,创建binder连接池aidl接口与实现
1
2
3
4interface IBinderPool { IBinder queryBinder(int binderCode); }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96public 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的实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21public 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连接池
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23private 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内容请搜索靠谱客的其他文章。
发表评论 取消回复