如何让 Android Service 与 Activity 通信

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2463175/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 05:56:27  来源:igfitidea点击:

How to have Android Service communicate with Activity

androidandroid-activityandroid-service

提问by Scott Saunders

I'm writing my first Android application and trying to get my head around communication between services and activities. I have a Service that will run in the background and do some gps and time based logging. I will have an Activity that will be used to start and stop the Service.

我正在编写我的第一个 Android 应用程序,并试图了解服务和活动之间的通信。我有一个服务,它将在后台运行并执行一些基于 GPS 和时间的日志记录。我将有一个用于启动和停止服务的活动。

So first, I need to be able to figure out if the Service is running when the Activity is started. There are some other questions here about that, so I think I can figure that out (but feel free to offer advice).

因此,首先,我需要能够确定在 Activity 启动时服务是否正在运行。这里还有一些其他问题,所以我想我可以弄清楚(但请随时提供建议)。

My real problem: if the Activity is running and the Service is started, I need a way for the Service to send messages to the Activity. Simple Strings and integers at this point - status messages mostly. The messages will not happen regularly, so I don't think polling the service is a good way to go if there is another way. I only want this communication when the Activity has been started by the user - I don't want to start the Activity from the Service. In other words, if you start the Activity and the Service is running, you will see some status messages in the Activity UI when something interesting happens. If you don't start the Activity, you will not see these messages (they're not that interesting).

我真正的问题是:如果 Activity 正在运行并且 Service 已启动,我需要一种让 Service 向 Activity 发送消息的方法。此时的简单字符串和整数 - 主要是状态消息。这些消息不会定期发生,所以如果有另一种方式,我认为轮询服务不是一个好方法。我只希望在用户启动 Activity 时进行这种通信 - 我不想从服务启动 Activity。换句话说,如果您启动 Activity 并且 Service 正在运行,当发生有趣的事情时,您将在 Activity UI 中看到一些状态消息。如果您不启动 Activity,您将看不到这些消息(它们不是那么有趣)。

It seems like I should be able to determine if the Service is running, and if so, add the Activity as a listener. Then remove the Activity as a listener when the Activity pauses or stops. Is that actually possible? The only way I can figure out to do it is to have the Activity implement Parcelable and build an AIDL file so I can pass it through the Service's remote interface. That seems like overkill though, and I have no idea how the Activity should implement writeToParcel() / readFromParcel().

似乎我应该能够确定服务是否正在运行,如果是,则将 Activity 添加为侦听器。然后在 Activity 暂停或停止时将 Activity 作为侦听器移除。这真的可能吗?我能想到的唯一方法是让活动实现 Parcelable 并构建一个 AIDL 文件,以便我可以通过服务的远程接口传递它。但这似乎有点矫枉过正,我不知道 Activity 应该如何实现 writeToParcel() / readFromParcel()。

Is there an easier or better way? Thanks for any help.

有没有更简单或更好的方法?谢谢你的帮助。

EDIT:

编辑:

For anyone who's interested in this later on, there is sample code from Google for handling this via AIDL in the samples directory: /apis/app/RemoteService.java

对于稍后对此感兴趣的任何人,Google 提供的示例代码用于通过示例目录中的 AIDL 处理此问题:/apis/app/RemoteService.java

采纳答案by MrSnowflake

There are three obvious ways to communicate with services:

有三种明显的方式与服务通信:

  1. Using Intents
  2. Using AIDL
  3. Using the service object itself (as singleton)
  1. 使用意图
  2. 使用 AIDL
  3. 使用服务对象本身(作为单例)

In your case, I'd go with option 3. Make a static reference to the service it self and populate it in onCreate():

在您的情况下,我会选择选项 3。对它自己的服务进行静态引用并将其填充到 onCreate() 中:

void onCreate(Intent i) {
  sInstance = this;
}

Make a static function MyService getInstance(), which returns the static sInstance.

制作一个静态函数MyService getInstance(),它返回静态sInstance

Then in Activity.onCreate()you start the service, asynchronously wait until the service is actually started (you could have your service notify your app it's ready by sending an intent to the activity.) and get its instance. When you have the instance, register your service listener object to you service and you are set. NOTE: when editing Views inside the Activity you should modify them in the UI thread, the service will probably run its own Thread, so you need to call Activity.runOnUiThread().

然后在Activity.onCreate()您启动服务时,异步等待服务实际启动(您可以让您的服务通过向活动发送意图来通知您的应用程序已准备就绪。)并获取其实例。当您拥有实例时,将您的服务侦听器对象注册到您的服务并进行设置。注意:在 Activity 中编辑视图时,您应该在 UI 线程中修改它们,该服务可能会运行自己的线程,因此您需要调用Activity.runOnUiThread().

The last thing you need to do is to remove the reference to you listener object in Activity.onPause(), otherwise an instance of your activity context will leak, not good.

您需要做的最后一件事是在 中删除对您的侦听器对象的引用Activity.onPause(),否则您的活动上下文实例将泄漏,这不好。

NOTE: This method is only useful when your application/Activity/task is the only process that will access your service. If this is not the case you have to use option 1. or 2.

注意:此方法仅在您的应用程序/活动/任务是访问您的服务的唯一进程时才有用。如果不是这种情况,您必须使用选项 1. 或 2。

回答by MaximumGoat

The asker has probably long since moved past this, but in case someone else searches for this...

提问者可能早已超越了这个,但万一其他人搜索这个......

There's another way to handle this, which I think might be the simplest.

还有另一种方法来处理这个问题,我认为这可能是最简单的。

Add a BroadcastReceiverto your activity. Register it to receive some custom intent in onResumeand unregister it in onPause. Then send out that intent from your service when you want to send out your status updates or what have you.

BroadcastReceiver为您的活动添加一个。注册它以接收一些自定义意图onResume并在onPause. 然后在您想发送状态更新或您有什么时从您的服务发送该意图。

Make sure you wouldn't be unhappy if some other app listened for your Intent(could anyone do anything malicious?), but beyond that, you should be alright.

确保如果其他应用程序监听您的Intent(有人会做任何恶意的事情吗?),您不会不高兴,但除此之外,您应该没事。

Code sample was requested:

请求代码示例:

In my service, I have this:

在我的服务中,我有这个:

// Do stuff that alters the content of my local SQLite Database
sendBroadcast(new Intent(RefreshTask.REFRESH_DATA_INTENT));

(RefreshTask.REFRESH_DATA_INTENTis just a constant string.)

RefreshTask.REFRESH_DATA_INTENT只是一个常量字符串。)

In my listening activity, I define my BroadcastReceiver:

在我的听力活动中,我定义了我的BroadcastReceiver

private class DataUpdateReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction().equals(RefreshTask.REFRESH_DATA_INTENT)) {
          // Do stuff - maybe update my view based on the changed DB contents
        }
    }
}

I declare my receiver at the top of the class:

我在班级的顶部声明我的接收器:

private DataUpdateReceiver dataUpdateReceiver;

I override onResumeto add this:

我重写onResume添加这个:

if (dataUpdateReceiver == null) dataUpdateReceiver = new DataUpdateReceiver();
IntentFilter intentFilter = new IntentFilter(RefreshTask.REFRESH_DATA_INTENT);
registerReceiver(dataUpdateReceiver, intentFilter);

And I override onPauseto add:

我覆盖onPause添加:

if (dataUpdateReceiver != null) unregisterReceiver(dataUpdateReceiver);

Now my activity is listening for my service to say "Hey, go update yourself." I could pass data in the Intentinstead of updating database tables and then going back to find the changes within my activity, but since I want the changes to persist anyway, it makes sense to pass the data via DB.

现在,我的活动正在监听我的服务说“嘿,去更新一下自己”。我可以在Intent而不是更新数据库表中传递数据,然后返回在我的活动中查找更改,但由于我希望更改仍然存在,因此通过 DB 传递数据是有意义的。

回答by Hyman Gao

Use LocalBroadcastManagerto register a receiver to listen for a broadcast sent from local service inside your app, reference goes here:

使用LocalBroadcastManager注册一个接收器监听来自应用内的本地服务发送一个广播,引用放在这里:

http://developer.android.com/reference/android/support/v4/content/LocalBroadcastManager.html

http://developer.android.com/reference/android/support/v4/content/LocalBroadcastManager.html

回答by Kjetil

Using a Messenger is another simple way to communicate between a Service and an Activity.

使用 Messenger 是另一种在 Service 和 Activity 之间进行通信的简单方法。

In the Activity, create a Handler with a corresponding Messenger. This will handle messages from your Service.

在Activity中,创建一个带有对应Messenger的Handler。这将处理来自您的服务的消息。

class ResponseHandler extends Handler {
    @Override public void handleMessage(Message message) {
            Toast.makeText(this, "message from service",
                    Toast.LENGTH_SHORT).show();
    }
}
Messenger messenger = new Messenger(new ResponseHandler());

The Messenger can be passed to the service by attaching it to a Message:

Messenger 可以通过将其附加到 Message 来传递给服务:

Message message = Message.obtain(null, MyService.ADD_RESPONSE_HANDLER);
message.replyTo = messenger;
try {
    myService.send(message);
catch (RemoteException e) {
    e.printStackTrace();
}

A full example can be found in the API demos: MessengerService and MessengerServiceActivity. Refer to the full example for how MyService works.

可以在 API 演示中找到完整示例:MessengerService 和 MessengerServiceActivity。有关 MyService 的工作原理,请参阅完整示例。

回答by Madhur Ahuja

I am surprised that no one has given reference to Otto event Bus library

我很惊讶没有人提到 Otto 事件总线库

http://square.github.io/otto/

http://square.github.io/otto/

I have been using this in my android apps and it works seamlessly.

我一直在我的 android 应用程序中使用它,它可以无缝运行。

回答by miguel

The other method that's not mentioned in the other comments is to bind to the service from the activity using bindService() and get an instance of the service in the ServiceConnection callback. As described here http://developer.android.com/guide/components/bound-services.html

其他注释中未提及的另一种方法是使用 bindService() 从活动绑定到服务,并在 ServiceConnection 回调中获取服务的实例。如此处所述http://developer.android.com/guide/components/bound-services.html

回答by zeenosaur

You may also use LiveDatathat works like an EventBus.

您也LiveData可以像使用EventBus.

class MyService : LifecycleService() {
    companion object {
        var BUS = MutableLiveData<Object>()
    }

    override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
        super.onStartCommand(intent, flags, startId)

        val testItem : Object

        // expose your data
        if (BUS.hasActiveObservers()) {
            BUS.postValue(testItem)
        }

        return START_NOT_STICKY
    }
}

Then add an observer from your Activity.

然后从您的Activity.

MyService.BUS.observe(this, Observer {
    it?.let {
        // Do what you need to do here
    }
})

You can read more from this blog.

您可以从此博客中阅读更多内容

回答by Z3R0

My method:

我的方法:

Class to manage send and receive message from/to service/activity:

管理从/向服务/活动发送和接收消息的类:

import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;

import java.util.ArrayList;
import java.util.List;

public class MessageManager {

    public interface IOnHandleMessage{
        // Messages
        int MSG_HANDSHAKE = 0x1;

        void onHandleMessage(Message msg);
    }

    private static final String LOGCAT = MessageManager.class.getSimpleName();

    private Messenger mMsgSender;
    private Messenger mMsgReceiver;
    private List<Message> mMessages;

    public MessageManager(IOnHandleMessage callback, IBinder target){
        mMsgReceiver = new Messenger(new MessageHandler(callback, MessageHandler.TYPE_ACTIVITY));
        mMsgSender = new Messenger(target);
        mMessages = new ArrayList<>();
    }

    public MessageManager(IOnHandleMessage callback){
        mMsgReceiver = new Messenger(new MessageHandler(callback, MessageHandler.TYPE_SERVICE));
        mMsgSender = null;
        mMessages = new ArrayList<>();
    }

    /* START Getter & Setter Methods */
    public Messenger getMsgSender() {
        return mMsgSender;
    }

    public void setMsgSender(Messenger sender) {
        this.mMsgSender = sender;
    }

    public Messenger getMsgReceiver() {
        return mMsgReceiver;
    }

    public void setMsgReceiver(Messenger receiver) {
        this.mMsgReceiver = receiver;
    }

    public List<Message> getLastMessages() {
        return mMessages;
    }

    public void addMessage(Message message) {
        this.mMessages.add(message);
    }
    /* END Getter & Setter Methods */

    /* START Public Methods */
    public void sendMessage(int what, int arg1, int arg2, Bundle msgData){
        if(mMsgSender != null && mMsgReceiver != null) {
            try {
                Message msg = Message.obtain(null, what, arg1, arg2);
                msg.replyTo = mMsgReceiver;
                if(msgData != null){
                    msg.setData(msgData);
                }
                mMsgSender.send(msg);
            } catch (RemoteException rE) {
                onException(rE);
            }
        }
    }

    public void sendHandshake(){
        if(mMsgSender != null && mMsgReceiver != null){
            sendMessage(IOnHandleMessage.MSG_HANDSHAKE, 0, 0, null);
        }
    }
    /* END Public Methods */

    /* START Private Methods */
    private void onException(Exception e){
        Log.e(LOGCAT, e.getMessage());
        e.printStackTrace();
    }
    /* END Private Methods */

    /** START Private Classes **/
    private class MessageHandler extends Handler {

        // Types
        final static int TYPE_SERVICE = 0x1;
        final static int TYPE_ACTIVITY = 0x2;

        private IOnHandleMessage mCallback;
        private int mType;

        public MessageHandler(IOnHandleMessage callback, int type){
            mCallback = callback;
            mType = type;
        }

        @Override
        public void handleMessage(Message msg){
            addMessage(msg);
            switch(msg.what){
                case IOnHandleMessage.MSG_HANDSHAKE:
                    switch(mType){
                        case TYPE_SERVICE:
                            setMsgSender(msg.replyTo);
                            sendHandshake();
                            break;
                        case TYPE_ACTIVITY:
                            Log.v(LOGCAT, "HERE");
                            break;
                    }
                    break;
                default:
                    if(mCallback != null){
                        mCallback.onHandleMessage(msg);
                    }
                    break;
            }
        }

    }
    /** END Private Classes **/

}

In Activity Example:

在活动示例中:

public class activity extends AppCompatActivity
      implements     ServiceConnection,
                     MessageManager.IOnHandleMessage { 

    [....]

    private MessageManager mMessenger;

    private void initMyMessenger(IBinder iBinder){
        mMessenger = new MessageManager(this, iBinder);
        mMessenger.sendHandshake();
    }

    private void bindToService(){
        Intent intent = new Intent(this, TagScanService.class);
        bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
        /* START THE SERVICE IF NEEDED */
    }

    private void unbindToService(){
    /* UNBIND when you want (onDestroy, after operation...)
        if(mBound) {
            unbindService(mServiceConnection);
            mBound = false;
        }
    }

    /* START Override MessageManager.IOnHandleMessage Methods */
    @Override
    public void onHandleMessage(Message msg) {
        switch(msg.what){
            case Constants.MSG_SYNC_PROGRESS:
                Bundle data = msg.getData();
                String text = data.getString(Constants.KEY_MSG_TEXT);
                setMessageProgress(text);
                break;
            case Constants.MSG_START_SYNC:
                onStartSync();
                break;
            case Constants.MSG_END_SYNC:
                onEndSync(msg.arg1 == Constants.ARG1_SUCCESS);
                mBound = false;
                break;
        }
    }
    /* END Override MessageManager.IOnHandleMessage Methods */

    /** START Override ServiceConnection Methods **/
    private class BLEScanServiceConnection implements ServiceConnection {

        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            initMyMessenger(iBinder);
            mBound = true;
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            mMessenger = null;
            mBound = false;
        }
    }
    /** END Override ServiceConnection Methods **/

In Service Example:

在服务示例:

public class Blablabla extends Service
    implements     MessageManager.IOnHandleMessage {

    [...]

    private MessageManager mMessenger;

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        super.onBind(intent);
        initMessageManager();
        return mMessenger.getMsgReceiver().getBinder();
    }

    private void initMessageManager(){
        mMessenger = new MessageManager(this);
    }

    /* START Override IOnHandleMessage Methods */
    @Override
    public void onHandleMessage(Message msg) {
    /* Do what you want when u get a message looking the "what" attribute */
    }
    /* END Override IOnHandleMessage Methods */

Send a message from Activity / Service:

从活动/服务发送消息:

mMessenger.sendMessage(what, arg1, arg2, dataBundle);

How this works:

这是如何工作的:

on the activity you start or bind the service. The service "OnBind" methods return the Binder to his MessageManager, the in the Activity through the "Service Connection" interface methods implementation "OnServiceConnected" you get this IBinder and init you MessageManager using it. After the Activity has init his MessageManager the MessageHandler send and Handshake to the service so it can set his "MessageHandler" sender ( the "private Messenger mMsgSender;" in MessageManager ). Doing this the service know to who send his messages.

在您启动或绑定服务的活动上。服务“OnBind”方法将Binder返回给他的MessageManager,在Activity中通过“Service Connection”接口方法实现“OnServiceConnected”你得到这个IBinder并使用它初始化你的MessageManager。在 Activity 初始化了他的 MessageManager 之后,MessageHandler 向服务发送和握手,以便它可以设置他的“MessageHandler”发送者(MessageManager 中的“私人 Messenger mMsgSender;”)。这样做,服务知道向谁发送消息。

You can also implement this using a List/Queue of Messenger "sender" in the MessageManager so you can send multiple messages to different Activities/Services or you can use a List/Queue of Messenger "receiver" in the MessageManager so you can receive multiple message from different Activities/Services.

您还可以使用 MessageManager 中的 Messenger“发送者”列表/队列来实现这一点,以便您可以向不同的活动/服务发送多条消息,或者您可以使用 MessageManager 中的 Messenger“接收者”列表/队列,以便您可以接收多个来自不同活动/服务的消息。

In the "MessageManager" instance you have a list of all messages received.

在“MessageManager”实例中,您有收到的所有消息的列表。

As you can see the connection between "Activity's Messenger" and "Service Messenger" using this "MessageManager" instance is automatic, it is done through the "OnServiceConnected" method and through the use of the "Handshake".

如您所见,使用此“MessageManager”实例的“Activity's Messenger”和“Service Messenger”之间的连接是自动的,它是通过“OnServiceConnected”方法和“Handshake”完成的。

Hope this is helpful for you :) Thank you very much! Bye :D

希望这对您有帮助:) 非常感谢!再见 :D

回答by ReDirEct__

Another way could be using observers with a fake model class through the activity and the service itself, implementing an MVC pattern variation. I don't know if it's the best way to accomplish this, but it's the way that worked for me. If you need some example ask for it and i'll post something.

另一种方法是通过活动和服务本身使用带有假模型类的观察者,实现 MVC 模式变体。我不知道这是否是实现这一目标的最佳方式,但这是对我有用的方式。如果您需要一些示例,请索取,我会发布一些内容。

回答by Erik

To follow up on @MrSnowflake answer with a code example. This is the XABBER now open source Applicationclass. The Applicationclass is centralising and coordinating Listenersand ManagerInterfaces and more. Managers of all sorts are dynamically loaded. Activity′sstarted in the Xabber will report in what type of Listenerthey are. And when a Servicestart it report in to the Applicationclass as started. Now to send a message to an Activityall you have to do is make your Activitybecome a listenerof what type you need. In the OnStart()OnPause()register/unreg. The Servicecan ask the Applicationclass for just that listenerit need to speak to and if it's there then the Activity is ready to receive.

用代码示例跟进@MrSnowflake 的回答。 这就是 XABBER 现在开源的Application。在Application类集中,协调Listeners和ManagerInterfaces多。各种管理器都是动态加载的。Activity′s在 Xabber 中启动时会报告Listener它们是什么类型。并在Service开始时向Application班级报告开始。现在发送消息给Activity你所要做的就是让你Activity成为listener你需要的类型。在OnStart()OnPause()注册/取消注册中。该Service可以要求Application类只是listener它需要说,如果它的存在,那么活动已准备好接收。

Going through the Applicationclass you'll see there's a loot more going on then this.

通过这Application门课,你会看到还有更多的战利品在发生。