在这篇文章中,我们想谈谈Android Bound Service 。 绑定服务是一种允许其他android组件(如活动)绑定到它并发送和接收数据的服务。 在上一篇文章中 ,我们讨论了本地服务,并了解了如何创建,启动和停止服务。 绑定服务是一种服务,不仅可以由与本地服务在同一进程中运行的组件使用,而且可以在不同进程中运行的活动和服务绑定到该服务并发送和接收数据。 当我们实现绑定服务时,我们总是必须扩展Service类,但是我们也必须重写onBind
方法。 此方法返回一个实现IBinder
的对象,该对象可用于与服务进行交互。
我们可以通过三种方式创建绑定服务:
- 扩展IBinder接口
- 使用Messenger
- 使用AIDL
在本文中,我们要分析如何使用Messenger来创建Android服务 。 使用此方法,我们可以创建可供不同流程中的组件使用的服务。 在这种情况下,我们使用Handler和Message在服务和其他组件之间交换数据。
使用Messenger实施绑定服务
基于Messenger的服务可以与不同进程中的其他组件进行通信,称为进程间通信(IPC) ,而无需使用AIDL。 要实现这样的服务,我们需要:
- 服务处理程序 :此组件处理来自与服务本身交互的客户端的传入请求。
- Messenger :此类用于创建实现
IBinder
接口的对象,以便客户端可以与服务进行交互。
因此,让我们实现服务。 作为示例,我们可以假设我们想创建一个服务,该服务接收一个字符串并将其转换为大写形式并将结果返回给客户端。
首先,我们创建一个实现Service
的类:
public class ConvertService extends Service {
..
}
如前所述,我们需要一个Handler来实现来自客户端的传入请求,因此,我们可以创建一个内部类,如下所示:
class ConvertHanlder extends Handler {
@Override
public void handleMessage(Message msg) {
// This is the action
int msgType = msg.what;
switch(msgType) {
case TO_UPPER_CASE: {
try {
// Incoming data
String data = msg.getData().getString("data");
Message resp = Message.obtain(null, TO_UPPER_CASE_RESPONSE);
Bundle bResp = new Bundle();
bResp.putString("respData", data.toUpperCase());
resp.setData(bResp);
msg.replyTo.send(resp);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
}
default:
super.handleMessage(msg);
}
}
在handleMessage中,我们开始处理传入的请求。 我们要做的第一件事就是“解码”我们正在处理的请求类型。 为此,我们可以使用Message类的what
属性。 根据其值,我们执行不同的操作:在本例中,我们只是将大写的字符串转换为字符串。 我们将字符串值Bundle传递给Message,以便在第12行获得该值。 我们必须将响应发送给客户端,因此我们创建另一个包含响应的消息(第13行),并附加一个包含转换后的字符串的新Bundle(第14-15行)。 在第16行,我们将消息发送回客户端(稍后我们将看到)。
因此,通过这种方式,我们创建了请求处理程序,但必须创建IBinder实例,以便客户端可以使用我们的服务。 为此,我们需要一个Messenger:
public class ConvertService extends Service {
...
private Messenger msg = new Messenger(new ConvertHanlder());;
...
@Override
public IBinder onBind(Intent arg0) {
return msg.getBinder();
}
}
在第3行,通过我们之前讨论的Handler,创建了Messenger类的新实例。 在第6行,我们重写onBind
方法并返回IBinder接口的实例。 我们的服务已准备就绪。 最后,我们在Manifest.xml中定义它:
<service android:name=".ConvertService" android:process=":convertprc"/>
注意,我们使用了android:process
以便该Service在与客户端不同的进程中运行。
Android服务客户端
现在,我们必须实现一个绑定到服务并向其发送数据的客户端。 我们可以假设客户端是一个Activity,它允许用户插入必须以大写形式转换的字符串。 该活动调用bindService
方法绑定到之前创建的服务。 当使用bindService方法绑定到“远程”服务时,我们需要提供一个回调方法,以便在绑定过程完成时得到通知,并且可以“使用”该服务。 我们必须创建一个实现ServiceConnection的类以接收此通知:
..
private ServiceConnection sConn;
private Messenger messenger;
..
@Override
protected void onCreate(Bundle savedInstanceState) {
// Service Connection to handle system callbacks
sConn = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
messenger = null;
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// We are conntected to the service
messenger = new Messenger(service);
}
};
...
// We bind to the service
bindService(new Intent(this, ConvertService.class), sConn,
Context.BIND_AUTO_CREATE);
..
}
在第8行,我们创建了ServiceConnection的实例,该实例将覆盖其方法。 在第18行,我们创建一个Messanger,稍后将其用于获取IBinder实例,以便我们可以将消息发送到我们的服务。 最后,在第24行,我们绑定到服务,指定服务类和回调接口
现在,我们需要一个“接收”处理程序来管理服务响应:
// This class handles the Service response
class ResponseHandler extends Handler {
@Override
public void handleMessage(Message msg) {
int respCode = msg.what;
switch (respCode) {
case ConvertService.TO_UPPER_CASE_RESPONSE: {
result = msg.getData().getString("respData");
txtResult.setText(result);
}
}
}
}
该处理程序的行为类似于服务实现中的处理程序,它从附加到Message的Bundle中提取响应,并在第11行向用户显示结果。
我们要讨论的最后一件事是从Activity向服务发送必须转换的字符串。 在这种情况下,我们可以假设我们的界面中有一个Button,当用户单击它时,Activity将发送数据:
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String val = edt.getText().toString();
Message msg = Message
.obtain(null, ConvertService.TO_UPPER_CASE);
msg.replyTo = new Messenger(new ResponseHandler());
// We pass the value
Bundle b = new Bundle();
b.putString("data", val);
msg.setData(b);
try {
messenger.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
请注意,在第9行,我们设置了答复Messenger,该服务将在必须发送回响应时用于该服务。
翻译自: https://www.javacodegeeks.com/2014/01/android-bound-service-ipc-with-messenger.html