远程服务如何向绑定的活动发送消息?

10

我已阅读关于绑定服务的文档,其中显示你可以通过消息从一个Activity轻松地与远程(即不在同一上下文中)的服务进行通信。但是,是否有任何方法可以从服务发送消息到绑定的Activity?例如,我的Activity绑定到同一应用程序的正在运行的后台服务,将消息发送给它,接收到这个消息后服务回复给Activity一个消息.. 我该如何实现这个功能?你能指向一些解释这个主题的文档吗?

5个回答

29

注意:此方法仅适用于进程内服务和活动,不适用于远程服务。

使用服务与活动进行通信需要创建一个监听器,并将其从活动传递到服务中。

您需要创建一个绑定到活动的服务。

第一步是创建一个服务。在服务中确保您有一个Binder对象和返回Binder对象的方法。以下是我在我的服务中使用的示例,以检索我的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;
}

现在,您需要创建某种接口,可以将其传递给绑定器对象,以便您的服务可以使用它来发送更新。以下是我的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);

请注意,即使您绑定到服务,直到调用start service,它实际上并未启动。绑定服务只是将服务连接到活动中。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;
    }
}

希望这能有所帮助。

1
非常好的例子,感谢您花费了这么多时间来解释这个概念。 - Gianni Costanzi
@Danuofr,这个工作是在进程内部的。有没有办法将任意监听器跨过进程边界发送? - dcow
3
这个答案的唯一问题是:它只适用于本地服务。但问题明确要求如何使用远程服务。不管怎样,还是谢谢您撰写这篇文章。但也许需要注意的是,这并不是真正问题的答案 :) - Patrick Boos
@Danuofr,你能否给我一个实现类似文件上传的想法? - Hardik Mehta

7

有没有现实世界的情况需要使用远程服务绑定? - Parag Kadam

1
简而言之,答案是通过将带有ResponseHandler的Messenger分配给< strong>msg.replyTo()。让我们在下面的示例中查看如何实现。 关于此示例的简介: 在此示例中,MainActivity 中有一个按钮,其 onClick() 与 sendMessage(View view)相关联。一旦单击按钮,我们向 RemoteService 发送自定义消息。一旦在远程服务中收到自定义消息,我们将当前时间附加到自定义消息上,并将其发送回 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();
        }
    }
}

ResponseHandler.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();
    }
}

ServiceConnector.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;
    }
}

RemoteService.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;
    }

}

IncomingHandler.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;

    }
}

0

应该可以使用类似于Android计费API的AIDL文件来实现此操作。这是一种进行RPC调用(跨远程进程通信)的方法。但是您必须声明要使用的每个方法,有点像上面提到的接口。


0

1) 在自己的Binder.class和binder代理实现IInterface.class对象中实现transact/onTransact方法(通过直接扩展类或匿名方式),并使用传递给这些方法的Parcel.class对象
2) 将本地接口附加到自己的Binder对象上 3) 创建服务并从onBind方法返回一个binder代理实现 4) 与bindService(ServiceConnection)建立绑定 5) 这将通过创建的接口实现返回代理binder

这是在Android中使用内核binder空间实现IPC的方法。

代码示例:

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的transact方法相互通信(例如,使用奇偶代码表示本地远程方面,因为我们在双方都使用相同的onTransact方法)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接