如何让Android Service与Activity通信


251

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

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

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

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

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

编辑:

对于以后对此感兴趣的任何人,都可从Google的示例代码中找到示例代码,该示例代码通过AIDL在以下示例目录中进行处理:/apis/app/RemoteService.java

Answers:


94

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

  1. 使用意图
  2. 使用AIDL
  3. 使用服务对象本身(作为单例)

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

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

创建一个静态函数MyService getInstance(),该函数返回静态值sInstance

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

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

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


2
busy-wait该如何活动?你能解释一下吗?
iTurki 2011年

1
因此,在服务类中的onCreate或onStartCommand之后设置公共静态变量后,将其称为isConnected或true。然后在您的活动中执行以下操作:Intent intent = new Intent(act,YourService.class); startService(intent); while(!YourService.isConnected){睡眠(300); }在该循环之后,您的服务正在运行,您可以与它进行通信。根据我的经验,与这样的服务进行交互肯定存在一些限制。
马特·沃尔夫

为什么只能在其中启动Activity.onCreate()
2012年

关于您为什么不想Intents通过Parcellable它们之间的消息发送数据来使用的任何说法?
阿曼·阿拉姆

2
这可以工作,但是@MaximumGoat的答案更加简洁,并且不涉及任何繁忙的等待。
马特

262

询问者可能早已超越了这个范围,但是如果其他人搜索了这个…

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

将a添加BroadcastReceiver到您的活动中。注册它以接收一些自定义意图,onResume然后在中注销onPause。然后,当您要发送状态更新或您拥有的内容时,请从服务中发送该意图。

确保如果有其他应用程序监听您Intent(不会有人恶意吗?),那么您不会感到不满意,但是除此之外,您应该还可以。

请求代码示例:

为我服务:

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

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

在听力活动中,我定义了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
        }
    }
}

我将我的接收者声明为班级的最高水平:

private DataUpdateReceiver dataUpdateReceiver;

我重写onResume添加:

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

我重写onPause添加:

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

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


5
怎么样?您所描述的正是我所需要的。但不仅如此,我还需要完整的示例代码。提前致谢。仅供参考,上一次我尝试编写小部件时,消息传递部分花了我2个月的时间。如果愿意,请笑,但由于IMHO Android比iOS更复杂,因此您的专家需要发布更多内容!

1
aaaaaaa,《高级Android开发繁忙的编码员指南》中有关小部件的一章非常出色。这是使我摆脱困境的唯一资源。作者CommonsWare在StackOverflow上享有11.5万的声誉,我强烈推荐这本书。 commonsware.com/AdvAndroid
MaximumGoat 2011年

64
Broadcasts别出心裁,此外,如果你不想你的广播超越自己的过程,然后考虑使用LocalBroadcastdeveloper.android.com/reference/android/support/v4/content/...
科迪Caughlan

2
谢谢科迪,我以前没看过。
MaximumGoat 2012年

3
这是很好的沟通方式。但是,当活动处于暂停状态时,如果交付了结果该怎么办?因此,在onResume活动之后可以等待很长时间才能获得结果。由于丢失了数据,因此不会接收到任何数据。
Anton-M


20

令我惊讶的是,没有人提到Otto事件总线库

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

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


7
另外,还有greenrobot的EventBus库。
Jazzer

15
但是请记住,EventBus和otto始终处于一个过程中。当您使用自己的服务(从设置开始时,android:process=":my_service"它们将无法通信。)
Ron

20

使用Messenger是在服务和活动之间进行通信的另一种简单方法。

在活动中,创建一个具有相应Messenger的处理程序。这将处理来自您的服务的消息。

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());

通过将Messenger附加到消息,可以将其传递给服务:

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

可以在API演示中找到完整的示例:MessengerService和MessengerServiceActivity。请参阅完整的示例,以了解MyService的工作方式。


1
非常感激!完美运作。只是代码中的一个细节:myService.send(message);应该messenger.send(message);代替。
费德里科·克里斯蒂娜


9

您也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
    }
}

然后从中添加一个观察者Activity

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

您可以从此博客中了解更多信息


2

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


我正在努力思考类似的问题。我有2个具有相同模型的活动和服务。可以在活动或任何服务中更新模型,所以我考虑过以某种方式使用观察者,但无法弄清楚。您可以发表例子来表达您的想法吗?
pzo 2012年

2

我的方法:

用于管理与服务/活动之间的消息收发的类:

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 **/

}

在活动示例中:

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 **/

在服务示例中:

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 */

从活动/服务发送消息:

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

工作原理:

在您启动或绑定服务的活动上。服务“ OnBind”方法将Binder返回到他的MessageManager,在“活动”中通过“服务连接”接口方法实现“ OnServiceConnected”,您将获得该IBinder并使用它来初始化MessageManager。在Activity初始化其MessageManager之后,MessageHandler将向该服务发送消息并进行握手,以便它可以设置其“ MessageHandler”发送者(在MessageManager中为“私有Messenger mMsgSender;”)。通过此服务,服务知道谁发送他的消息。

您还可以在MessageManager中使用Messenger“发件人”的列表/队列来实现此目的,以便可以将多个消息发送到不同的活动/服务,也可以在MessageManager中使用Messenger“收件人”的列表/队列来接收多个消息。来自不同活动/服务的消息。

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

如您所见,使用“ MessageManager”实例在“活动的Messenger”和“ Service Messenger”之间的连接是自动的,它是通过“ OnServiceConnected”方法和“握手”完成的。

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


2

要跟踪@MrSnowflake答案并提供代码示例。 这是XABBER现在开源的Application。该Application课程正在集中和协调Listeners以及ManagerInterfaces等。各种管理器都是动态加载的。Activity´s在Xabber中启动的报表将报告Listener它们的类型。并在Service启动时向Application班级报告。现在,要将消息发送给Activity您,您要做的就是使您Activity成为所需的listener类型。在OnStart() OnPause()注册/取消注册中。该Service可以要求Application类只是listener它需要说,如果它的存在,那么活动已准备好接收。

Application全班学习中,您会发现还有很多事情要做。


现在,提供的链接会生成github404。它移动了吗?
JE卡特二世(

是的,现在似乎可以正常工作。一定是github上的临时性问题。
JE Carter II

1

绑定是另一种交流方式

创建一个回调

public interface MyCallBack{

   public void getResult(String result);

}

活动方面:

  1. 在活动中实现接口

  2. 提供该方法的实现

  3. 将活动绑定到服务

  4. 当服务与活动绑定和解除绑定时,注册和注销回调。

    public class YourActivity extends AppCompatActivity implements MyCallBack{
    
          private Intent notifyMeIntent;
          private GPSService gpsService;
          private boolean bound = false;
    
          @Override
          public void onCreate(Bundle sis){
    
              // activity code ...
    
              startGPSService();
    
          }
    
          @Override
          public void getResult(String result){
           // show in textView textView.setText(result);
          }
    
          @Override
          protected void onStart()
          {
              super.onStart();
              bindService();
          }
    
          @Override
          protected void onStop() {
              super.onStop();
              unbindService();
          }
    
          private ServiceConnection serviceConnection = new ServiceConnection() {
    
                @Override
                public void onServiceConnected(ComponentName className, IBinder service) {
    
                      GPSService.GPSBinder binder = (GPSService.GPSBinder) service;
                      gpsService= binder.getService();
                      bound = true;
                      gpsService.registerCallBack(YourActivity.this); // register
    
               }
    
               @Override
               public void onServiceDisconnected(ComponentName arg0) {
                      bound = false;
               }
          };
    
          private void bindService() {
    
               bindService(notifyMeIntent, serviceConnection, Context.BIND_AUTO_CREATE);
          }
    
          private void unbindService(){
               if (bound) {
                     gpsService.registerCallBack(null); // unregister            
                     unbindService(serviceConnection);
                     bound = false;
                }
          }
    
          // Call this method somewhere to start Your GPSService
          private void startGPSService(){
               notifyMeIntent = new Intent(this, GPSService.class);
               startService(myIntent );
          }
    
     }

服务端:

  1. 初始化回调

  2. 必要时调用回调方法

     public class GPSService extends Service{
    
         private MyCallBack myCallback;
         private IBinder serviceBinder = new GPSBinder();
    
         public void registerCallBack(MyCallBack myCallback){
              this.myCallback= myCallback;
         }
    
         public class GPSBinder extends Binder{
    
             public GPSService getService(){
                  return GPSService.this;
             }
        }
    
        @Nullable
        @Override
        public IBinder onBind(Intent intent){
             return serviceBinder;
        }
     }

serviceConnection没钱了onCreate。请编辑它。
阿德·扎法

它不必放在onCreate中
Rohit Singh,


0

除了LocalBroadcastManager之外 ,Event Bus和Messenger也已在该问题中得到解答,我们可以使用Pending Intent从服务进行通信。

如前所述这里在我的博客文章

服务和活动之间的通信可以使用PendingIntent完成。为此,我们可以使用 createPendingResult()。createPendingResult()创建一个新的PendingIntent对象,您可以将其提供给服务使用,并将结果数据发送回onActivityResult(int, int,Intent)回调。由于PendingIntent是Parcelable的,因此可以放入Intent Extra中,因此您的活动可以将此PendingIntent传递给服务。该服务进而可以在PendingIntent上调用send()方法以通知通过事件的onActivityResult活动。

活动

public class PendingIntentActivity extends AppCompatActivity
{
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

PendingIntent pendingResult = createPendingResult(
100, new Intent(), 0);
Intent intent = new Intent(getApplicationContext(), PendingIntentService.class);
intent.putExtra("pendingIntent", pendingResult);
startService(intent);

}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == 100 && resultCode==200) {
Toast.makeText(this,data.getStringExtra("name"),Toast.LENGTH_LONG).show();
}
super.onActivityResult(requestCode, resultCode, data);
}
}

服务

public class PendingIntentService extends Service {

    private static final String[] items= { "lorem", "ipsum", "dolor",
            "sit", "amet", "consectetuer", "adipiscing", "elit", "morbi",
            "vel", "ligula", "vitae", "arcu", "aliquet", "mollis", "etiam",
            "vel", "erat", "placerat", "ante", "porttitor", "sodales",
            "pellentesque", "augue", "purus" };
    private PendingIntent data;

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        data = intent.getParcelableExtra("pendingIntent");

        new LoadWordsThread().start();
        return START_NOT_STICKY;
    }

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

    @Override
    public void onDestroy() {
        super.onDestroy();
    }

    class LoadWordsThread extends Thread {
        @Override
        public void run() {
            for (String item : items) {
                if (!isInterrupted()) {

                    Intent result = new Intent();
                    result.putExtra("name", item);
                    try {
                        data.send(PendingIntentService.this,200,result);
                    } catch (PendingIntent.CanceledException e) {

                        e.printStackTrace();
                    }
                    SystemClock.sleep(400);

                }
            }
        }
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.