一、今日学习内容
模糊进程间调用
前边就是Binder的使用方式,但是至此还遗留了一个问题,我们的Service只有指定了新的进程名之后才会是远程调用,如果通过bindService 传递过来的IBinder对象是同进程的,那我们就不需要使用IBinder.transact进行内核通信了。我们知道同进程之间利用方法调用方式就可以做到通信。 我们在onServiceConnected打印IBinder类型,如果发现是远程调用,传给我们的 iBinder 是 BinderProxy 类型,BinderProxy是Binder的内部类一样实现了IBinder接口,他在native层会对应一个C++的BpBinder,BpBinder 最终会通过Binder驱动跟Server端通信。如果是本地调用,打印出的类型为Stub,说明本地调用时,onServiceConnected传过来的就是我们在Service的onBinde方法返回的Stub对象本身。基于这个原理,我们可以设计一个转换方法。
首先我们要怎么判断当前是远程调用还是同进程调用呢? 我们使用queryLocalInterface(DESCRIPTOR)方法,Binder中queryLocalInterface不会返回空,而在BinderProxy的实现中,queryLocalInterface返回为null。 Binder:
1
2
3
4
5
6
|
public IInterface queryLocalInterface(String descriptor) { if (mDescriptor != null && mDescriptor.equals(descriptor)) { return mOwner; } return null ; } |
mOwner是attachInterface方法传进来的接口本身,后面还会出现这个方法。 BinderProxy:
1
2
3
|
public IInterface queryLocalInterface(String descriptor) { return null ; } |
当发现是远程调用时我们创建上边的Proxy来代理跨进程通信过程。要是本地调用我们直接返回本地Stub对象。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
public static IMyInterface asInterface(IBinder iBinder){ if ((iBinder == null )) { return null ; } //获取本地interface IInterface iin = iBinder.queryLocalInterface(DESCRIPTOR); if (((iin != null ) && (iin instanceof IMyInterface))) { //不为空,说明是本地调用,直接强转后返回。 //IMyInterface封装了test0()、test1()两个方法,本地对象和Proxy都继承自接口 return ((IMyInterface)iin ); } //为null,远程调用,新建代理 return new Proxy(iBinder); } |
把上面相关代码完善之后
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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
|
public interface IBinderTest extends android.os.IInterface { /** * 本地Stub对象 */ public static abstract class Stub extends android.os.Binder implements IBinderTest { private static final java.lang.String DESCRIPTOR = "com.XXX.XXXX.IBinderTest" ; public Stub() { //绑定DESCRIPTOR,并设置queryLocalInterface方法返回的mOwner this .attachInterface( this , DESCRIPTOR); } /** * 本地远程转换 */ public static IBinderTest asInterface(android.os.IBinder obj) { if ((obj == null )) { return null ; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin != null ) && (iin instanceof IBinderTest))) { return ((IBinderTest) iin); } return new IBinderTest.Stub.Proxy(obj); } @Override public android.os.IBinder asBinder() { return this ; } /** * 处理Client调用请求 */ @Override public boolean onTransact( int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException { java.lang.String descriptor = DESCRIPTOR; switch (code) { case INTERFACE_TRANSACTION: { reply.writeString(descriptor); return true ; } case TRANSACTION_testVoidAidl: { data.enforceInterface(descriptor); this .testVoidAidl(); reply.writeNoException(); return true ; } case TRANSACTION_testStringAidl: { data.enforceInterface(descriptor); java.lang.String _result = this .testStringAidl(); reply.writeNoException(); reply.writeString(_result); return true ; } default : { return super .onTransact(code, data, reply, flags); } } } /** * 远程调用代理类 */ private static class Proxy implements IBinderTest { private android.os.IBinder mRemote; Proxy(android.os.IBinder remote) { mRemote = remote; } @Override public android.os.IBinder asBinder() { return mRemote; } public java.lang.String getInterfaceDescriptor() { return DESCRIPTOR; } @Override public void testVoidAidl() throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(Stub.TRANSACTION_testVoidAidl, _data, _reply, 0 ); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } } @Override public java.lang.String testStringAidl() throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); java.lang.String _result; try { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(Stub.TRANSACTION_testStringAidl, _data, _reply, 0 ); _reply.readException(); _result = _reply.readString(); } finally { _reply.recycle(); _data.recycle(); } return _result; } } /** * 调用函数code */ static final int TRANSACTION_testVoidAidl = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0 ); static final int TRANSACTION_testStringAidl = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1 ); } public void testVoidAidl() throws android.os.RemoteException; public java.lang.String testStringAidl() throws android.os.RemoteException; } |
如果你用过AIDL并且看过AIDL生成的代码,你就会发现上面代码就是AIDL生成的。 换掉Service的调用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
public class MyService extends Service { private String TAG = "MyService" ; @Override public IBinder onBind(Intent intent) { return new MyBinder(); } class MyBinder extends IBinderTest.Stub{ @Override public void testVoidAidl() throws RemoteException { Log.d(TAG, "testVoidAidl" ); } @Override public String testStringAidl() throws RemoteException { Log.d(TAG, "testStringAidl" ); return "hello" ; } } } |
以上就是Binder的使用方法以及原理的简单介绍。
Binder原理
之前介绍了Binder的基本使用,接下来我们来看下Binder的底层原理。
(图片来源gityuan.com/2015/10/31/…),安卓的应用内存是隔离的,但是内核空间是共享的,我们要实现IPC就要靠共享的内核空间来交换数据。
Binder通信模型:
ioctl
Binder的通信原理:
由于用户空间的隔离机制(沙盒模式),所以我们要利用内核空间进行IPC操作,用户空间与内核空间的交互使用的是linux内核的ioctl函数,接下来简单了解一下ioctl的使用。 ioctl可以控制I/O设备 (驱动设备),提供了一种获得设备信息和向设备发送控制参数的手段。简单来说就是使用ioctl可以对驱动设备进行操作。由于我们IPC的过程中内核空间使用虚拟驱动设备/dev/binder控制通信过程,我们要跟这个Binder驱动设备交互就要使用ioctl命令。 简单介绍下,Linux要实现驱动设备的使用需要三个角色
- 应用程序(调用方),使用ioctl来发送操作指令。
- 驱动程序,用来处理ioctl传来的指令,并完成对驱动设备的操作。驱动程序被注册进内核之后,就会等待应用程序的调用。
- 驱动设备,在Binder机制中,驱动设备是/dev/binder,这个文件被映射到每个系统Service的虚拟内存中(后边会讲到),驱动程序可以操作这个文件进行进程间数据交互。
下图是Linux中应用程序是怎么通过驱动来操作硬件设备的:
来个图来说一下应用层与驱动程序函数的ioctl之间的联系:
二、遇到的问题
没办法有效的验证所学知识
三、明日计划
继续binder的学习