我已阅读有关 Bound Services 的文档,其中显示您可以通过Messages从 Activity 轻松地与远程(即不在同一上下文中)Service 进行通信,但是有什么方法可以将消息从Service发送到绑定活动?例如,我的活动绑定到同一应用程序的正在运行的后台服务,向它发送一条消息,并在收到此消息后,服务回复一条消息给活动。我该如何实现呢?您能否指出一些解释该主题的文档?
5 回答
注意:这仅适用于进程中的服务和活动,而不是远程问题。
使用服务与活动通信涉及创建一个侦听器,您可以将其从活动传递给服务。
您需要制作绑定到活动的服务。
第一步是提供服务。在服务中确保你有一个 Binder 对象和返回一个 binder 对象的方法。以下是我在服务中用于检索活页夹的示例。另请注意,此绑定器具有设置侦听器的方法,该侦听器将作为 BoundServiceListener 类型字段保存在服务中。
/**
* 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 DownloadBgBinder extends Binder {
public DownloadBgService getService() {
// Return this instance of LocalService so clients can call public methods
return DownloadBgService.this;
}
public void setListener(BoundServiceListener listener) {
mListener = listener;
}
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
现在您需要创建某种接口,您可以将其传递给您的服务可以用来向其发送更新的 binder 对象。下面是我的 BoundServiceListener。
public interface BoundServiceListener {
public void sendProgress(double progress);
public void finishedDownloading();
}
现在在您的活动中,您需要创建一个用于绑定到服务的 ServiceConnection 对象。所以添加这样的东西。
/** Defines callbacks for service binding, passed to bindService() */
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName arg0) {
mBound = false;
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// We've bound to LocalService, cast the IBinder and get LocalService instance
DownloadBgBinder binder = (DownloadBgBinder) service;
mService = binder.getService();
binder.setListener(new BoundServiceListener() {
@Override
public void sendProgress(double progress) {
// Use this method to update our download progress
}
@Override
public void finishedDownloading() {
}
});
mBound = true;
}
现在要注意的关键是
binder.setListener(new BoundServiceListener() {
@Override
public void sendProgress(double progress) {
// Use this method to update our download progress
}
@Override
public void finishedDownloading() {
}
});
这部分是我实际将我的 BoundServiceListener 接口发送到服务类的地方。然后服务类在此处使用该侦听器对象
if (mListener!=null)
mListener.finishedDownloading();
if (mListener!=null)
mListener.sendProgress(percent);
现在您可以将它放在您的服务类中您需要的任何位置,您的活动将收到您的进度更新。
还要确保在您的活动中包含以下内容以实际绑定和启动服务。
Intent intent = new Intent(this, DownloadBgService.class);
startService(intent);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
请记住,即使您绑定到服务,它也不会真正启动,直到您调用启动服务。绑定到服务只是将服务连接到活动。startService() 方法调用服务
onStartCommand(Intent intent, int flags, int startId)
还要在清单中声明您的服务
<service android:name=".services.DownloadBgService" />
当活动离开时也取消绑定服务
@Override
protected void onStop() {
super.onStop();
// Unbind from the service
if (mBound) {
unbindService(mConnection);
mBound = false;
}
}
希望这可以帮助。
在Remote Messenger Service Sample的参考文档中找到示例。
简而言之,答案是将带有 ResponseHandler 的 Messenger 分配给msg.replyTo()。让我们在下面的示例中看看我们是如何做到的。
简要说明此示例的作用: 在此示例中,我们在 MainActivity 中有一个按钮,其 onClick() 链接到 sendMessage(View view)。单击按钮后,我们会向 RemoteService 发送自定义消息。一旦在远程服务中收到自定义消息,我们将 CurrentTime 附加到自定义消息并将其发送回 MainActivity。
MainActivity.java
public class MainActivity extends AppCompatActivity {
ServiceConnector serviceConnector;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
this.serviceConnector = new ServiceConnector();
Intent intent = new Intent(this,RemoteService.class);
bindService(intent,serviceConnector, Context.BIND_AUTO_CREATE);
}
public void sendMessage(View view) {
Message msg = Message.obtain();
msg.replyTo = new Messenger(new ResponseHandler(this));
Bundle bundle = new Bundle();
bundle.putString("MyString", "Time");
msg.setData(bundle);
try {
this.serviceConnector.getMessenger().send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
响应处理程序.java
public class ResponseHandler extends Handler {
MainActivity mainActivity;
public ResponseHandler(Context context){
this.mainActivity = (MainActivity) context;
}
@Override
public void handleMessage(@NonNull Message msg) {
Bundle data = msg.getData();
String dataString = data.getString("respData");
Toast.makeText(this.mainActivity,dataString,Toast.LENGTH_SHORT).show();
}
}
服务连接器.java
public class ServiceConnector implements ServiceConnection {
private Messenger messenger;
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder)
{
this.messenger = new Messenger(iBinder);
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
this.messenger = null;
}
public Messenger getMessenger(){
return this.messenger;
}
}
远程服务.java
public class RemoteService extends Service {
private final IBinder iBinder = new Messenger(new IncomingHandler(this)).getBinder();
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return iBinder;
}
}
传入处理程序.java
public class IncomingHandler extends Handler {
private RemoteService remoteService;
public IncomingHandler(Context context)
{
this.remoteService = (RemoteService)context;
}
public RemoteService getService()
{
return this.remoteService;
}
@Override
public void handleMessage(@NonNull Message msg) {
try {
msg.replyTo.send(getCurrentTime(msg));
} catch (RemoteException e) {
e.printStackTrace();
}
}
public Message getCurrentTime(Message msg){
SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss MM/dd/yyyy", Locale.US);
Message resp = Message.obtain();
Bundle bResp = new Bundle();
bResp.putString("respData", msg.getData().getString("MyString") + " : " +(dateFormat.format(new Date())).toString());
resp.setData(bResp);
return resp;
}
}
1) 在自己的 Binder.class 和实现 IInterface.class 对象(匿名或通过直接扩展类)中实现 transact/onTransact 方法,方法是使用传入的 Parcel.class 对象
2) 将本地接口附加到自己的 Binder 对象 3 ) 创建服务并从 onBind 方法返回绑定器代理实现 4) 使用 bindService(ServiceConnection) 创建绑定 5) 这将导致通过在接口实现中创建绑定返回代理绑定器
这是使用内核绑定器空间的 IPC 的 android 实现
简化代码示例:
class ServiceIPC extends Service {
@Override
public Binder onBind() {
return new IInterface() {
IInterface _local = this;
@Override
public IBinder asBinder() {
return new Binder()
{
//
// allow distinguish local/remote impl
// avoid overhead by ipc call
// see Binder.queryLocalInterface("descriptor");
//
attachLocalInterface(_local,"descriptor");
}
@Override
public boolean onTransact(int code,
Parcel in,
Parcel out,
int flags)
throws RemoteException {
//
// your talk between client & service goes here
//
return whatsoever // se super.onTransact();
}
}
}
}.asBinder();
}
}
*然后您可以在客户端和服务端使用 IBinder 的事务方法相互交谈(4 个使用奇数/前夕代码来恶心本地远程端的示例,因为我们对展位端使用相同的 onTransact 方法)
应该能够使用 . 像 android billing api 这样的AIDL 文件。它是一种进行 RPC 调用(跨远程进程通信)的方法。但是您必须声明要使用的每种方法。有点像上面已经提到的界面。