Android 中的绑定服务

前言

什么是绑定服务?

绑定服务是指服务与其他绑定在一起。比如说,我们可以将服务绑定在 Activity 上,来发送请求,接受响应,甚至是执行进程间的通讯(IPC)。我们需要知道的是,绑定服务的生命周期通常与被绑定者的生命周期相同,不会无限期的运行在后台。

关于绑定服务

想要服务绑定于某个组件,你必须实现 onBind() 回调方法。该方法返回的 IBinder 对线定义了启动者与 Service 交互的接口。

通过调用 bindService() 来绑定服务。前提是,你必须提供 ServiceConnection 的实现,ServiceConnection 能够监控调用者与 Serviced 的链接。创建后,系统会调用 ServiceConnnection 接口中的 onServiceConnected() 回调,并先调用者传递 IBinder.这样,二者就可以通信了。

多个组件可以同时与 Service 绑定,不过,需要注意的是,只有在第一个组件绑定时,系统才会调用 onBind() 方法返回检索到的 IBinder.随后的绑定,系统将不在调用 onBind(),而是将同一个 IBinder 传递给绑定的组件。

何时销毁?

当最后一个与之绑定的组件解绑时,系统才会将 Service 销毁(排除 startService() 也启动了该服务)。

创建绑定服务

上面已经说到,绑定服务需要提供一个 IBinder,用来调用者和 Service 的交互,你可以通过以下三种方式来定义接口:

  • 扩展 IBinder 类

如果这个服务只是提供给自身应用使用,并且与客户端运行在同一进程中,则应通过扩展 Binder 类,并从 onBind() 返回它的一个示例来创建接口。调用者在接受到 Binder 后,就可以通过它来访问 Binder 中的实现,甚至是 Service 中公有的方法。

也就是说,如果服务仅仅是自身应用的后台工作线程,则优先采用这种方法。

不使用这种方式创建接口的唯一原因是:被启动的服务需要提供给其他应用使用,或者不同进程使用。
  • Messenger

如果你需要让接口跨进程工作,那么你可以使用 Messenger 为服务创建接口。我们可以通过这种方式定义不同类型的 Message 对线的 Handler。这样我就可以和调用者同用一个 IBinder,调用者通过利用 Message 向服务发送命令。调用者也可以自定义 Message,以便服务回传消息。

这种方法是 进程间通信(IPC) 最简单的方法,Message 会在单一线程中创建所有的请求队列,这样就可以避免线程不安全的所带来的复杂性。

  • 使用 AIDL

什么是 AIDL?

ALDL 是 Android 接口定义语言,用来将所有对象分解成原语的工作,操作系统可以识别这些原语并将它们编组进各进程中来执行进程间的通讯(IPC).而上面说过的 Messenger 的底层实际上就是通过 AIDL 来实现的。

如果你想要服务同时处理多个请求,则可直接使用 AIDL。前提是,你的服务必须具备多线程处理能力,同时采用线程安全的设计。

使用 AIDL,你需要创建一个定义编程接口的 .aidl 的文件。Android SDK 的工具利用该文件生成一个实现接口并处理 IPC 的抽象类,你可以在服务内对其扩展。

注意:基本上大部分应用都不会采用 AIDL 来创建绑定服务,因为它需要具备多线程的处理能力,这样导致实现功能的复杂性增加。所以说,并不适用于大多数应用。具体 AIDL 文档,请参阅 这里

拓展 Binder 类

如果你的服务仅供本地使用,不需要跨进程工作,那你只要实现自有 Binder 类即可,通过此类来访问服务中的方法。

注意:此方法只有在客户端和服务在同一应用和进程中才有效。

具体步骤:

1.在你的服务中,创建一个满足下列任一要求的 Binder 示例:

  • 包含调用者可调用的 public 方法;
  • 返回当前 Service 示例,其中包含调用者可调用的 public 方法;
  • 亦或是调用已有的 Binder 子类,其中包含调用者可调用的 public 方法;

2.在 onBind() 回调方法中返回此 Binder 示例。

3.在调用者中,在 onServiceConnected() 回调方法中接口 Binder,并通过 bindService() 来绑定服务。

Note: 一直强调服务和调用者在同一应用内,是为了便于调用者转换返回的对象和正确的调用其 API。服务和调用者还必须在同一个进程内,因为此方法不支持跨进程。

示例代码:

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
public class LocalService extends Service {
// Binder given to clients
private final IBinder mBinder = new LocalBinder();
// Random number generator
private final Random mGenerator = new Random();

/**
* Class used for the client Binder. Because we know this service always
* runs in the same process as its clients, we don't need to deal with IPC.
*/

public class LocalBinder extends Binder {
LocalService getService() {
// Return this instance of LocalService so clients can call public methods
return LocalService.this;
}
}

@Override
public IBinder onBind(Intent intent) {
return mBinder;
}

/** method for clients */
public int getRandomNumber() {
return mGenerator.nextInt(100);
}
}

上述代码中,LocalBinder 为调用者提供了一个 getService() 方法,以便调用者能够轻松获取 LocalService 的实例。同时,也能够调用服务内部的 public 方法。

再来看看调用者是如何启动 LocalService 的:

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
public class BindingActivity extends Activity {
LocalService mService;
boolean mBound = false;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}

@Override
protected void onStart() {
super.onStart();
// Bind to LocalService
Intent intent = new Intent(this, LocalService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}

@Override
protected void onStop() {
super.onStop();
// Unbind from the service
if (mBound) {
unbindService(mConnection);
mBound = false;
}
}

/** Called when a button is clicked (the button in the layout file attaches to
* this method with the android:onClick attribute) */

public void onButtonClick(View v) {
if (mBound) {
// Call a method from the LocalService.
// However, if this call were something that might hang, then this request should
// occur in a separate thread to avoid slowing down the activity performance.
int num = mService.getRandomNumber();
Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show();
}
}

/** Defines callbacks for service binding, passed to bindService() */
private ServiceConnection mConnection = new ServiceConnection() {

@Override
public void onServiceConnected(ComponentName className,
IBinder service)
{

// We've bound to LocalService, cast the IBinder and get LocalService instance
LocalBinder binder = (LocalBinder) service;
mService = binder.getService();
mBound = true;
}

@Override
public void onServiceDisconnected(ComponentName arg0) {
mBound = false;
}
};
}

上面代码贴出了调用者是如何通过 ServiceConnection 的实现和 onServiceConnected() 的回调来绑定到服务的。

注意:上例并没有显式取消与服务的绑定,但是我们在实际编码时都应在适当的时间(如 Activity 暂停时)取消绑定。

Messenger 如何使用?

无需通过 AIDL,我们通过 Messager 来实现进程间的通信。

如何使用?

  • 服务中实现一个 Handler,来接收每个调用者中调用的回调;
  • 利用 Handler 来创建 Messenger 对象;
  • 通过 Messenger 创建一个 IBinder,服务通过 onBind() 来返回给调用者;
  • 调用者使用 IBinderMessenger 实例化,然后通过 MessengerMessage 对象发送给服务;
  • 服务在 handleMessage() 方法中接收每个 Message;

下面是示例代码:

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
public class MessengerService extends Service {
/** Command to the service to display a message */
static final int MSG_SAY_HELLO = 1;

/**
* Handler of incoming messages from clients.
*/

class IncomingHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_SAY_HELLO:
Toast.makeText(getApplicationContext(), "hello!", Toast.LENGTH_SHORT).show();
break;
default:
super.handleMessage(msg);
}
}
}

/**
* Target we publish for clients to send messages to IncomingHandler.
*/

final Messenger mMessenger = new Messenger(new IncomingHandler());

/**
* When binding to the service, we return an interface to our messenger
* for sending messages to the service.
*/

@Override
public IBinder onBind(Intent intent) {
Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();
return mMessenger.getBinder();
}
}

接下来,调用者只需根据服务返回的 IBinder 创建一个 Messenger,然后利用 send() 发送消息,下面是示例代码:

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
public class ActivityMessenger extends Activity {
/** Messenger for communicating with the service. */
Messenger mService = null;

/** Flag indicating whether we have called bind on the service. */
boolean mBound;

/**
* Class for interacting with the main interface of the service.
*/

private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
// This is called when the connection with the service has been
// established, giving us the object we can use to
// interact with the service. We are communicating with the
// service using a Messenger, so here we get a client-side
// representation of that from the raw IBinder object.
mService = new Messenger(service);
mBound = true;
}

public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been
// unexpectedly disconnected -- that is, its process crashed.
mService = null;
mBound = false;
}
};

public void sayHello(View v) {
if (!mBound) return;
// Create and send a message to the service, using a supported 'what' value
Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0);
try {
mService.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}

@Override
protected void onStart() {
super.onStart();
// Bind to the service
bindService(new Intent(this, MessengerService.class), mConnection,
Context.BIND_AUTO_CREATE);
}

@Override
protected void onStop() {
super.onStop();
// Unbind from the service
if (mBound) {
unbindService(mConnection);
mBound = false;
}
}
}

上面的代码只是发消息让 Service 执行某段操作,并未有任何反馈,如果你需要让服务给出响应,你还需要在客户端中创建一个 Messenger。然后,但调用者接受到 onServiceConnected() 回调时,会向服务发送一条 Message,并在 send() 方法中附带一个包含调用者 MessengerreplyTo 参数。

绑定服务

调用者通过调用 bindService() 方法绑定到服务。系统随后会调用 onBind() 方法,该方法返回的 IBinder 用于两者之间的交互。

注意,绑定时异步的,服务不会立即返回 IBinder 对象。我们需要创建 ServiceConnection 示例,并将其传递给 bindService()。通过 ServiceConnection 中实现的回调方法来接收 IBinder

注意:四大组件中,只有 Activity, Service, ContentProvider 才能绑定到服务,广播接收者是无法绑定到服务的。

绑定具体步骤:

1.实现 ServiceConnection:

你必须重写两个回调方法:

  • onServiceConnected() : 这个不用多说,放回 IBinder;
  • onServiceDisconnected() : 当 Android 系统与服务的链接意外中断时(如服务奔溃或被终止时)调用此方法。取消绑定,系统是不会调用此方法的。

2.调用 bindService(),并传递 ServiceConnection
3.当系统调用 onServiceConnected() 回调时,我们开始调用服务中的方法。
4.通过调用 unbindService() 来断开与服务的连接。

下面是一段 ServiceConnection 的使用示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
LocalService mService;
private ServiceConnection mConnection = new ServiceConnection() {
// Called when the connection with the service is established
public void onServiceConnected(ComponentName className, IBinder service) {
// Because we have bound to an explicit
// service that is running in our own process, we can
// cast its IBinder to a concrete class and directly access it.
LocalBinder binder = (LocalBinder) service;
mService = binder.getService();
mBound = true;
}

// Called when the connection with the service disconnects unexpectedly
public void onServiceDisconnected(ComponentName className) {
Log.e(TAG, "onServiceDisconnected");
mBound = false;
}
};

通过 bindServcie() 来传递 ServiceConnection

1
2
Intent intent = new Intent(this, LocalService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);

再来说说 bindService() 方法中的参数:

  • param 1: Intent - 可以是显式的,也可以是隐式的;
  • param 2: ServiceConnection;
  • param 3: 指定绑定选项的标志。通常都是 BIND_AUTO_CREATE,以便创建尚未激活的服务。其他可能的值为 BIND_DEBUG_UNBINDBIND_NOT_FOREGROUND,或 0(表示无)。

还有一些重要的事情

  • 你应该捕获 DeadObjectException 异常,它在连接中断时引发。也是远程方法引发的唯一异常;
  • 对象是进程计数的引用;
  • 你应该在客户端生命周期的合理阶段来绑定和取消绑定,例如:

1.如果你只需在 Activity 可见时与服务交互,则应在 onStart() 期间绑定,在 onStop() 期间取消绑定。
2.如果你希望 Activity 在后台运行状态仍在工作,则可在 onCreate() 期间绑定,在 onDestory() 期间取消绑定。

注意:通常情况下,你不应该在 ActivityonResume()onPause() 期间绑定和取消绑定,这样做性能会很差。另外,如果你的应用中多个 Activity 绑定了同一个 Service,并且两个 Activity 之间发生了转换,系统可能会销毁服务并重建服务。

绑定服务的生命周期

当服务与所有的绑定时,系统会销毁该服务。换句话说,如果你创建的服务只是单纯的绑定服务,则无需对其生命周期进行管理,系统会根据该 Service 是否绑定到任何组件来代替你管理。

需要注意,如果你选择实现 onStartCommand() 回调方法,这时,你必须显式停止服务(通过 stopSelf() 或者 stopService())。

另外,如果你的服务已经启动并且绑定了,则当系统调用 onUnbind() 方法时,如果你想在调用者下一次绑定到该服务时,调用 onRebind() 方法,而不是 onBind() 调用,则可以选择返回 true

下面是这种生命周期的逻辑:

本文学习自:

文章有帮助到您?不妨打赏博主一碗拉面或一杯咖啡的小费吧 :-D!