Binder的学习

Binder是一个实现了IBinder接口的一个类。它是一种跨进程通信的方式,同时也是一种虚拟物理设备,同时也还是ServiceManager链接Manager和ManagerService的桥梁。当bindService的时候,服务端会返回一个包含了服务端业务调用的Binder对象,可以通过这个对象来获取相应的服务或者数据。

AIDL

AIDL (Android Interface Definition Language) 是一种IDL,在Android中,我们可以通过AIDL由系统自动生成我们所需要的Binder类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// IAddLife.aidl
package com.project.tangyifeng.ipctest;
import com.project.tangyifeng.ipctest.IAddLifeListener;
// Declare any non-default types here with import statements
interface IAddLife {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
void addASecond();
void registerListener(IAddLifeListener listener);
void unregisterListener(IAddLifeListener listener);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// IAddLifeListener.aidl
package com.project.tangyifeng.ipctest;
// Declare any non-default types here with import statements
interface IAddLifeListener {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
void onLifeLengthened();
}

这两个文件可以在Android Studio中通过右键->Add->AIDL,就可以创建。在AIDL文件中,可以使用的数据类型包括下面几种:

  • 基本数据类型;
  • String 和 CharSequence;
  • List,只支持ArrayList,同时要求内部元素都能被支持;
  • Map,只支持HashMap,同时要求内部元素都能被支持;
  • Parcelable;
  • AIDL

注:如果AIDL中用到了自定义类,必须创建一个同名的AIDL文件;所有的在一个package内的AIDL还是必须互相import之后,才能正常使用。

由AIDL生成的一个IInterface

位置如图所示

经过Android Studio的生成之后,我们就能在以上的位置找到AIDL所生成的IInterface文件。那么这个类里面到底包含了哪些内容呢?让我们来看一看。

AIDL定义的方法

1
2
3
4
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;
public void addASecond() throws android.os.RemoteException;
public void registerListener(com.project.tangyifeng.ipctest.IAddLifeListener listener) throws android.os.RemoteException;
public void unregisterListener(com.project.tangyifeng.ipctest.IAddLifeListener listener) throws android.os.RemoteException;

显然,以上的这些方法,就是我们刚才在AIDL接口里面所定义的所有方法。这些自然不是Binder的关键,下一个才是Binder的核心。

class Stub

可以看到Stub是个抽象类,并且它是要实现外部接口的。

DESCRIPTOR

1
private static final java.lang.String DESCRIPTOR = "com.project.tangyifeng.ipctest.IAddLife";

它是Binder的唯一标志,一般是以当前Binder的类的类名表示的。

asInterface(IBinder obj)

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

用于将服务端的Binder对象转换为客户端所需的AIDL类口类型的对象。其中的queryLocalInterface是用来区分这个Binder和obj是否在同一个进程中,如果是就直接返回;如果不是就返回代理类Proxy。

asBinder

1
2
3
android.os.IBinder asBinder(){
return this;
}

用于返回当前的Binder对象。

注:Interface用于功能操作,Binder用于进程间的通讯。asInterface和asBinder的功能是正好相反的,就像一对反函数。

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
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 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_basicTypes:{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
long _arg1;
_arg1 = data.readLong();
boolean _arg2;
_arg2 = (0!=data.readInt());
float _arg3;
_arg3 = data.readFloat();
double _arg4;
_arg4 = data.readDouble();
java.lang.String _arg5;
_arg5 = data.readString();
this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
reply.writeNoException();
return true;
}
case TRANSACTION_addASecond:{
data.enforceInterface(DESCRIPTOR);
this.addASecond();
reply.writeNoException();
return true;
}
case TRANSACTION_registerListener:{
data.enforceInterface(DESCRIPTOR);
com.project.tangyifeng.ipctest.IAddLifeListener _arg0;
_arg0 = com.project.tangyifeng.ipctest.IAddLifeListener.Stub.asInterface(data.readStrongBinder());
this.registerListener(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_unregisterListener:{
data.enforceInterface(DESCRIPTOR);
com.project.tangyifeng.ipctest.IAddLifeListener _arg0;
_arg0 = com.project.tangyifeng.ipctest.IAddLifeListener.Stub.asInterface(data.readStrongBinder());
this.unregisterListener(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}

这个方法将会在服务端的Binder线程池中运行。服务端将通过code的值来确定客户端请求的方法到底是什么,然后从data中取出相应的参数再执行相应的方法。当方法执行完之后就会把返回值写入reply中返回回去。这个方法的返回值如果是false的话,就说明客户端的请求失败了,这样也就可以控制进程的权限。

Proxy类

Proxy类和外部类的方法几乎一致,但是它的方法将会在客户端里面运行。当客户端调用方法的时候,先创建相应的data、reply和返回对象List,将参数写入之后调用transact方法向服务端发送请求,同时挂起当前线程,直到远端返回了数据,就从reply中获取数据然后再返回。

Binder的工作方式

如图所示

使用Binder来实现一个多进程服务

说了这么多,我们还是来实现一个简单的需求吧。我们来看这样一个很简单的小需求,有两个进程a与b,在一个处于b进程内的Service中存储着一个整型数据,然后通过a进程中的Activity来调用Service中的方法。当b中的整型数据到了100的时候,就在a中发出一个Toast。

创建AIDL文件

创建的文件就是上面所写的文件,包含了

1
2
3
void addASecond();
void registerListener(IAddLifeListener listener);
void unregisterListener(IAddLifeListener listener);

以及IAddLifeListener这个Interface中的方法

1
void onLifeLengthened();

IAddLifeListener是做为监听器来监听整型变量的变化的。

创建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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
public class AddLifeService extends Service {
private static final String TAG = "AddLifeService";
private Integer life;
private RemoteCallbackList<IAddLifeListener> listenerLists;
private Binder addLifeBinder = new IAddLife.Stub() {
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
// Do nothing...
}
@Override
public void addASecond() throws RemoteException {
if(life == null){
life = 1;
}else{
life ++;
}
if(life >= 100)
enoughLife();
Log.d(TAG, "Current life is :" + life);
}
@Override
public void registerListener(IAddLifeListener listener) throws RemoteException {
if(listenerLists == null){
listenerLists = new RemoteCallbackList<>();
}
listenerLists.register(listener);
Log.d(TAG, "Listener has been registered.");
}
@Override
public void unregisterListener(IAddLifeListener listener) throws RemoteException {
listenerLists.unregister(listener);
Log.d(TAG, "Listener has been unregistered.");
}
};
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "Service has been bound.");
return addLifeBinder;
}
private void enoughLife() throws RemoteException{
int N = listenerLists.beginBroadcast();
for(int i = 0; i < N; i++){
IAddLifeListener listener = listenerLists.getBroadcastItem(i);
if(listener != null){
listener.onLifeLengthened();
}
}
listenerLists.finishBroadcast();
}
}

让我们来详细分析一下这个Service中的内容。

RemoteCallBackList

这个类是为了实现通信而封装好的一个类,要注意的是,虽然它有一个list的名字,但是它不是一个List的子类。
它的存在是为了应对listener的注册以及解注册时发生的问题。因为在IPC过程中,我们从客户端传入的这个listener并不可能直接在内存中就传递到了服务端,它是会在服务端重新被创建一个新的来实现。当我们想要解注册这个listener的时候,如果是平常的list,就不能找到这个对象(因为这个对象是在服务端全新生成的)。所以我们就用这个类的register以及unregister就可以轻松实现注册和解注册了。
注:它的遍历方法和list完全不同,请参见官方文档

Binder addLifeBinder

这个Binder便是负责在两个进程之间传递的那个Binder,可以看到它是实现了AIDL创建的IInterface接口的Stub子类的。

enoughLife

这个方法就是当值到达了100之后,就会触发所有的listener的的方法。其中的RemoteCallBackList的使用方法是非常有趣的。。

创建操作的Acitity

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
@OnClick(R.id.bind_service_button)
void bindService(){
Intent intent = new Intent(this, AddLifeService.class);
bindService(intent, connection, Context.BIND_AUTO_CREATE);
}
@OnClick(R.id.add_life_button)
void addLife(){
try {
addLife.addASecond();
}catch (RemoteException re){
re.printStackTrace();
}
}
private IAddLifeListener lifeListener = new IAddLifeListener.Stub() {
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
@Override
public void onLifeLengthened() throws RemoteException {
new Handler().post(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, "Wow! Enough Life!", Toast.LENGTH_SHORT).show();
}
});
}
};
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
Log.d(TAG, "Success!");
addLife = IAddLife.Stub.asInterface(iBinder);
try {
addLife.registerListener(lifeListener);
}catch (RemoteException re){
re.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
Log.d(TAG, "Fail!");
addLife = null;
}
};

只粘贴了最核心的一段代码,让我们来看一看。

IAddLifeListener lifeListener

这是实现了IAddLifeListener接口的监听器,由于Binder是在Binder线程池中进行操作的,所以这里要用Handler机制将结果在UI线程中输出。

ServiceConnection connection

这里面就只看这一句

1
addLife = IAddLife.Stub.asInterface(iBinder);

这一句将返回来的iBinder这个Binder类转换为了Interface。我们在前面说了,Binder是处理通信的,Interface才是处理具体方法的,所以这样做才是对的。

运行结果

首先我们可以看到,未绑定服务之前,App只有一个进程。

只有一个进程

当我们绑定了服务之后,就多了个新的私有进程。

私有进程

我们跳到新出现的线程,然后点击按钮,就发现。

另一个进程

最后到了100的时候,Activity上弹出了Toast

Toast

说明我们的IPC成功了,真是可喜可贺啊,让人不禁想要念两句诗……

Powered by Hexo and Hexo-theme-hiker

Copyright © 2013 - 2018 Alex's Blog All Rights Reserved.

Yifeng Tang hält Urheberrechtsansprüche.