Android Binder

在Android中,Binder用于完成进程间通信(IPC),即把多个进程关联在一起。
Binder工作在Linux层面,属于一个驱动,只是这个驱动不需要硬件,或者说其操作的硬件是基于一小段内存。从线程的角度来讲,Binder驱动代码运行在内核态,客户端程序调用Binder是通过系统调用完成的。

Binder是一种架构,这种架构提供了服务端接口、Binder驱动、客户端接口三个模块。

  • 服务端:一个Binder服务端实际上就是一个Binder类的对象,该对象一旦创建,内部就启动一个隐藏线程。该线程接下来会接收Binder驱动发送的消息,收到消息后,会执行到Binder对象中的onTransact()函数,并按照该函数的参数执行不同的服务代码。因此,要实现一个Binder服务,就必须重载onTransact()方法。重载onTransact()函数的主要内容是把onTransact()函数的参数转换为服务函数的参数,而onTransact()函数的参数来源是客户端调用transact()函数时输入的,如果transact()有固定格式的输入,那么onTransact()就会有固定格式的输出。
  • Binder驱动:任意一个服务端Binder对象被创建时,同时会在Binder驱动中创建一个mRemote对象,该对象的类型也是Binder类。客户端要访问远程服务时,都是通过mRemote对象来进行调用。
  • 客户端:客户端要想访问远程服务,必须获取远程服务在Binder对象中对应的mRemote引用,至于如何获取,下面将会介绍。获得该mRemote对象后,就可以调用其transact()方法,而在Binder驱动中,mRemote对象也重载了transact()方法,重载的内容主要包括以下几项。

    1. 以线程间消息通信的模式,向服务端发送客户端传递过来的参数。
    2. 挂起当前线程,当前线程正是客户端线程,并等待服务端线程执行完指定服务函数后通知(notify)。
    3. 接收到服务端线程的通知,然后继续执行客户端线程,并返回到客户端代码区。

下面通过一个bindServiceunBindService来记录一下流程。

Android中Binder机制的应用,在bindServiceunBindService中流程分解。

1
2
3
4
5
6
7
8
9
10
11
12
Activity.java
bindService(new Intent(MainActivity.this, MyService.class), conn, Service.BIND_AUTO_CREATE);
ContextWrapper.java
@Override
public boolean bindService(Intent service, ServiceConnection conn,
int flags) {
// mBase为ContextImpl实例对象
return mBase.bindService(service, conn, flags);
}

ContextImpl.java

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
@Override
public boolean bindService(Intent service, ServiceConnection conn,
int flags) {
warnIfCallingFromSystemProcess();
return bindServiceCommon(service, conn, flags, mMainThread.getHandler(),
Process.myUserHandle());
}
private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler
handler, UserHandle user) {
IServiceConnection sd;
if (mPackageInfo != null) {
// ccf: => Binder#attachInterface(...);
sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
} else {
throw new RuntimeException("Not supported in system context");
}
validateServiceIntent(service);
try {
IBinder token = getActivityToken();
if (token == null && (flags&BIND_AUTO_CREATE) == 0 &&
mPackageInfo != null
&& mPackageInfo.getApplicationInfo().targetSdkVersion
< android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
flags |= BIND_WAIVE_PRIORITY;
}
service.prepareToLeaveProcess(this);
// ccf: => ActivityManagerNative#bindService(...);
int res = ActivityManagerNative.getDefault().bindService(
mMainThread.getApplicationThread(), getActivityToken(), service,
service.resolveTypeIfNeeded(getContentResolver()),
sd, flags, getOpPackageName(), user.getIdentifier());
return res != 0;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
Binder.java
public void attachInterface(IInterface owner, String descriptor) {
mOwner = owner;
mDescriptor = descriptor;
}

ActivityManagerNative.java

1
2
3
public abstract class ActivityManagerNative extends Binder implements IActivityManager
{
}
  • bindService
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
public int bindService(IApplicationThread caller, IBinder token,
Intent service, String resolvedType, IServiceConnection connection,
int flags, String callingPackage, int userId) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(caller != null ? caller.asBinder() : null);
data.writeStrongBinder(token);
service.writeToParcel(data, 0);
data.writeString(resolvedType);
data.writeStrongBinder(connection.asBinder());
data.writeInt(flags);
data.writeString(callingPackage);
data.writeInt(userId);
// ccf: mRemote(BinderProxy)
// ccf: => BinderProxy#transact(...);
mRemote.transact(BIND_SERVICE_TRANSACTION, data, reply, 0);
reply.readException();
int res = reply.readInt();
data.recycle();
reply.recycle();
return res;
}
Binder$BinderProxy.java
// 上一步中的mRemote即为BinderProxy对象!
public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");
if (Binder.isTracingEnabled()) { Binder.getTransactionTracker().addTrace(); }
// ccf: => Binder#execTransact(...);
return transactNative(code, data, reply, flags);
}
Binder.java
(LoadedApk$ServiceDispatcher$InnerConnection)
private boolean execTransact(int code, long dataObj, long replyObj,
int flags) {
Parcel data = Parcel.obtain(dataObj);
Parcel reply = Parcel.obtain(replyObj);
// theoretically, we should call transact, which will call onTransact,
// but all that does is rewind it, and we just got these from an IPC,
// so we'll just call it directly.
boolean res;
// Log any exceptions as warnings, don't silently suppress them.
// If the call was FLAG_ONEWAY then these exceptions disappear into the ether.
try {
// ccf: => Binder$BinderProxy#BinderProxy(...);
// 绑定->创建Service的时候,code为1;此时会生成一个BinderProxy对象
res = onTransact(code, data, reply, flags);
} catch (RemoteException|RuntimeException e) {
// ...
res = true;
} catch (OutOfMemoryError e) {
// ...
res = true;
}
checkParcel(this, code, reply, "Unreasonably large binder reply buffer");
reply.recycle();
data.recycle();
// Just in case -- we are done with the IPC, so there should be no more strict
// mode violations that have gathered for this thread. Either they have been
// parceled and are now in transport off to the caller, or we are returning back
// to the main transaction loop to wait for another incoming transaction. Either
// way, strict mode begone!
StrictMode.clearGatheredViolations();
return res;
}
Binder$BinderProxy.java
BinderProxy() {
mSelf = new WeakReference(this);
}
// 生成Binder中间驱动,onServiceConnected中的第二个参数,实际上就是该对象!
Activity.java # onServiceConnected
ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.w(TAG, "onServiceConnected: " + service.hashCode() + " - " + Thread.currentThread().getName());
try {
// ccf: => Binder$BinderProxy#transact(...);
service.transact(101010, Parcel.obtain(), Parcel.obtain(), 0);
} catch (RemoteException e) {
}
}
// ...
}
Binder$BinderProxy.java
public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");
if (Binder.isTracingEnabled()) { Binder.getTransactionTracker().addTrace(); }
return transactNative(code, data, reply, flags);
}

unBindService

  • unBindService(conn);
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
ContextWrapper.java
@Override
public void unbindService(ServiceConnection conn) {
mBase.unbindService(conn);
}
ContextImpl.java
@Override
public void unbindService(ServiceConnection conn) {
if (mPackageInfo != null) {
IServiceConnection sd = mPackageInfo.forgetServiceDispatcher(
getOuterContext(), conn);
try {
// ccf: => ActivityManagerNative#unbindService(...);
ActivityManagerNative.getDefault().unbindService(sd);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
} else {
throw new RuntimeException("Not supported in system context");
}
}
ActivityManagerNative.java
public boolean unbindService(IServiceConnection connection) throws RemoteException
{
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(connection.asBinder());
// ccf: => Binder$BinderProxy#transact(...);
mRemote.transact(UNBIND_SERVICE_TRANSACTION, data, reply, 0);
reply.readException();
boolean res = reply.readInt() != 0;
data.recycle();
reply.recycle();
return res;
}
Binder$BinderProxy.java
public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");
if (Binder.isTracingEnabled()) { Binder.getTransactionTracker().addTrace(); }
// ccf: => Binder#execTransact(...);
return transactNative(code, data, reply, flags);
}