如何让Android服务与Activity通信

Sco*_*ers 245 android android-service android-activity

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

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

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

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

有更简单或更好的方法吗?谢谢你的帮助.

编辑:

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

Max*_*oat 255

提问者可能很久以来已经过了这个,但万一其他人搜索这个......

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

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

如果其他应用程序听取了你的意图(任何人都可以做恶意的事情吗?),请确保你不会不高兴.但除此之外,你应该没问题.

代码样本被要求:

在我的服务中,我有:

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

(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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我宣布我的接收器位于班级的顶端:

private DataUpdateReceiver dataUpdateReceiver;
Run Code Online (Sandbox Code Playgroud)

我重写onResume来添加:

if (dataUpdateReceiver == null) dataUpdateReceiver = new DataUpdateReceiver();
IntentFilter intentFilter = new IntentFilter(RefreshTask.REFRESH_DATA_INTENT);
registerReceiver(dataUpdateReceiver, intentFilter);
Run Code Online (Sandbox Code Playgroud)

我重写onPause添加:

if (dataUpdateReceiver != null) unregisterReceiver(dataUpdateReceiver);
Run Code Online (Sandbox Code Playgroud)

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

  • "广播"很棒,此外如果你不希望你的广播超出你自己的过程,那么考虑使用`LocalBroadcast`:http://developer.android.com/reference/android/support/v4/content/LocalBroadcastManager. HTML (63认同)
  • 怎么样?你所描述的正是我所需要的.但更重要的是,我需要完整的示例代码.提前致谢.仅供参考,我最后一次尝试编写小部件时,消息传递部分花了我2个月的时间.如果你愿意笑,但是你的专家需要发布更多,因为恕我直言Android比iOS更复杂! (4认同)
  • 这是非常好的沟通方式.但是,如果活动处于暂停状态,该怎么办?所以在onResume活动之后可以等待很长时间才能得到结果.并且不会收到任何数据,因为错过了数据. (3认同)
  • 我认为这是正确的方法! (2认同)
  • 谢谢科迪,我之前没见过. (2认同)

MrS*_*ake 88

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

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

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

void onCreate(Intent i) {
  sInstance = this;
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

  • 我如何"忙"等待活动?你能解释一下吗? (2认同)
  • 这是有效的,但@ MaximumGoat的答案更清晰,不涉及任何忙碌等待. (2认同)

Jac*_*Gao 39

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

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

  • `LocalBroadcastManager` 现已弃用,取而代之的是 `LiveData` 或其他观察者模式工具:https://developer.android.com/reference/androidx/localbroadcastmanager/content/LocalBroadcastManager (4认同)
  • 是的,这里有关于localbroadcast的非常好的讨论[链接](http://stackoverflow.com/questions/8802157/how-to-use-localbroadcastmanager) (3认同)

小智 20

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

在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());
Run Code Online (Sandbox Code Playgroud)

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

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

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


Mad*_*uja 20

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

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

我一直在我的Android应用程序中使用它,它无缝地工作.

  • 但请记住,EventBus和otto总是在一个进程中.当您使用自己的进程中的服务时(从设置`android:process ="开始:my_service"`他们无法进行通信. (15认同)
  • 另外,还有greenrobot的[EventBus](http://greenrobot.github.io/EventBus/)库. (7认同)

mig*_*uel 8

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

  • 这是获取服务实例引用的正确方法. (3认同)

zee*_*aur 6

您也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
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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


Roh*_*ngh 5

绑定是另一种沟通方式

创建回调

public interface MyCallBack{

   public void getResult(String result);

}
Run Code Online (Sandbox Code Playgroud)

活动方面:

  1. 在Activity中实现接口

  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 );
          }
    
     }
    
    Run Code Online (Sandbox Code Playgroud)

服务端:

  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;
        }
     }
    
    Run Code Online (Sandbox Code Playgroud)