Android Binder机制分析

简介

Binder能干什么?Binder可以提供系统中任何程序都可以访问的全局服务。这个功能当然是任何系统都应该提供的,下面我们简单看一下Android的Binder的框架

Android Binder框架分为服务器接口、Binder驱动、以及客户端接口;简单想一下,需要提供一个全局服务,那么全局服务那端即是服务器接口,任何程序即客户端接口,它们之间通过一个Binder驱动访问。

服务器端接口:实际上是Binder类的对象,该对象一旦创建,内部则会启动一个隐藏线程,会接收Binder驱动发送的消息,收到消息后,会执行Binder对象中的onTransact()函数,并按照该函数的参数执行不同的服务器端代码。

Binder驱动:该对象也为Binder类的实例,客户端通过该对象访问远程服务。

客户端接口:获得Binder驱动,调用其transact()发送消息至服务器

AIDL的使用

如果对Android比较熟悉,那么一定使用过AIDL,如果你还不了解,那么也没关系,下面会使用一个例子展示AIDL的用法。

服务端

在Android Studio中,右键new 一个AIDL文件,名字为:IMessageAIDL

1
2
3
4
5
6
7
8
9
10
11
12
// IMessageAIDL.aidl
package com.chu.analyse.aidl;

// Declare any non-default types here with import statements

interface IMessageAIDL {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void sendMessage(String msg);
}

编译一下项目,会生成一个名字是IMessageAIDL.java的文件。

然后我们在项目中新建一个Service,代码如下:

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
public class MessageService extends Service {

private final IMessageAIDL.Stub binder = new IMessageAIDL.Stub() {
@Override
public void sendMessage(String msg) throws RemoteException {
Log.e("TAG", "received:" + msg);
}
};

@Override
public void onCreate() {
super.onCreate();
Log.e("TAG", "onCreate");
}

@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.e("TAG", "onBind");
return binder;
}

@Override
public void onRebind(Intent intent) {
Log.e("TAG", "onRebind");
super.onRebind(intent);
}

@Override
public boolean onUnbind(Intent intent) {
Log.e("TAG", "onUnbind");
return super.onUnbind(intent);
}

@Override
public void onDestroy() {
super.onDestroy();
Log.e("TAG", "onDestroy");
}
}

在此Service中,使用生成的IMessageAIDL创建了一个binder的对象,并在Service的onBind方法中返回
最后记得在AndroidManifest中注册,通过android:process指定一个新的进程。

1
2
3
4
5
6
7
8
<service
android:name="com.chu.analyse.service.MessageService"
android:process=":message">
<intent-filter>
<action android:name="com.chu.analyse.message"></action>
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>

客户端

客户端的代码比较简单,创建一个布局,里面包含3个按钮,分别为绑定服务,解除绑定,发送消息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<Button
android:id="@+id/bindBtn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="绑定服务" />

<Button
android:id="@+id/unBindBtn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="解绑服务" />

<Button
android:id="@+id/sendMsgBtn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="发送消息" />
</LinearLayout>

Activity的代码如下

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
public class TestMessageActivity extends AppCompatActivity implements View.OnClickListener {

private IMessageAIDL messageAIDL;
ServiceConnection serviceConn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.e("TAG", "onServiceConnected");
messageAIDL = IMessageAIDL.Stub.asInterface(service);
}

@Override
public void onServiceDisconnected(ComponentName name) {
Log.e("TAG", "onServiceDisconnected");
messageAIDL = null;
}
};

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_message);
findViewById(R.id.bindBtn).setOnClickListener(this);
findViewById(R.id.unBindBtn).setOnClickListener(this);
findViewById(R.id.sendMsgBtn).setOnClickListener(this);
}

public void onClick(View view) {
switch (view.getId()) {
case R.id.bindBtn:
Intent intent = new Intent();
intent.setAction("com.chu.analyse.message");
intent.setPackage(getPackageName());
bindService(intent, serviceConn, Context.BIND_AUTO_CREATE);
break;
case R.id.unBindBtn:
unbindService(serviceConn);
break;
case R.id.sendMsgBtn:
if (messageAIDL != null) {
try {
messageAIDL.sendMessage("hello aidl");
} catch (RemoteException e) {
e.printStackTrace();
}
}
break;
}
}
}

点击绑定按钮,服务端message进程的logcat打印日志为

1
2
com.chu.analyse E/TAG: onCreate
com.chu.analyse E/TAG: onBind

客户端进程logcat打印日志为

1
com.chu.analyse E/TAG: onServiceConnected

点击发送按钮,message进程的logcat打印日志为

1
com.chu.analyse E/TAG: received:hello aidl

点击解绑按钮,message进程的logcat打印日志为

1
2
com.chu.analyse E/TAG: onUnbind
com.chu.analyse E/TAG: onDestroy

分析AIDL生成的java代码

先看服务端的代码,可以看到我们服务端提供的服务是由IMessageAIDL.Stub来执行的。

服务端

1
2
3
4
5
6
private final IMessageAIDL.Stub binder = new IMessageAIDL.Stub() {
@Override
public void sendMessage(String msg) throws RemoteException {
Log.e("TAG", "received:" + msg);
}
};

我们来看看Stub这个类的声明:

1
public static abstract class Stub extends android.os.Binder implements com.chu.analyse.aidl.IMessageAIDL

接下来看它的onTransact()方法:

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
@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_sendMessage:
{
data.enforceInterface(descriptor);
java.lang.String _arg0;
_arg0 = data.readString();
this.sendMessage(_arg0);
reply.writeNoException();
return true;
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
}

文章开头也说到服务端的Binder实例会根据客户端依靠Binder驱动发来的消息,执行onTransact方法,然后由其参数决定执行服务端的代码。
可以看到onTransact有四个参数 code , data ,replay , flags

  • code 是一个整形的唯一标识,用于区分执行哪个方法,客户端会传递此参数,告诉服务端执行哪个方法
  • data客户端传递过来的参数
  • replay服务器返回回去的值
  • flags标明是否有返回值,0为有(双向),1为没有(单向)

我们仔细看case TRANSACTION_sendMessage:中的代码

  • data.enforceInterface(descriptor): 与客户端的writeInterfaceToken对用,标识远程服务的名称
    java.lang.String _arg0;
    _arg0 = data.readString();
    接下来分别读取了客户端传入的两个参数
    this.sendMessage(_arg0);
    然后执行sendMessage方法。

客户端

客户端主要通过ServiceConnected与服务端连接

1
2
3
4
5
6
7
8
9
10
11
12
13
ServiceConnection serviceConn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.e("TAG", "onServiceConnected");
messageAIDL = IMessageAIDL.Stub.asInterface(service);
}

@Override
public void onServiceDisconnected(ComponentName name) {
Log.e("TAG", "onServiceDisconnected");
messageAIDL = null;
}
};

如果你比较敏锐,应该会猜到这个onServiceConnected中的IBinder实例,其实就是我们文章开通所说的Binder驱动,也是一个Binder实例
在IMessageAIDL.Stub.asInterface中最终调用了:

1
2
3
4
5
6
7
8
9
10
11
public static com.chu.analyse.aidl.IMessageAIDL asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.chu.analyse.aidl.IMessageAIDL))) {
return ((com.chu.analyse.aidl.IMessageAIDL)iin);
}
return new com.chu.analyse.aidl.IMessageAIDL.Stub.Proxy(obj);
}

这个Proxy实例传入了我们的Binder驱动,并且封装了我们调用服务端的代码,文章开头说,客户端会通过Binder驱动的transact()方法调用服务端代码
直接看Proxy中的sendMessage方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Override public void sendMessage(java.lang.String msg) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(msg);
boolean _status = mRemote.transact(Stub.TRANSACTION_sendMessage, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
getDefaultImpl().sendMessage(msg);
return;
}
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}

首先声明两个Parcel对象,一个用于传递数据,一个用户接收返回的数据

  • _data.writeInterfaceToken(DESCRIPTOR);与服务器端的enforceInterfac对应
  • _data.writeString(msg);写入需要传递的参数
  • mRemote.transact(Stub.TRANSACTION_sendMessage, _data, _reply, 0);

    终于看到了我们的transact方法,第一个对应服务端的code,_data,_repay分别对应服务端的data,reply,0表示是双向的

到此,我们已经通过AIDL生成的代码解释了Android Binder框架的工作原理。Service的作用其实就是为我们创建Binder驱动,即服务端与客户端连接的桥梁。
AIDL其实通过我们写的aidl文件,帮助我们生成了一个接口,一个Stub类用于服务端,一个Proxy类用于客户端调用。

其他

服务端中做耗时操作是会影响主线程的。一般来说,会开启子线程去调用AIDL方法,然后处理完成可以回调到客户端的主线程中。

您的支持是我原创的动力