首页 文章

如何让Android服务与Activity通信

提问于
浏览
224

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

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

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

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

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

EDIT:

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

11 回答

  • 7

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

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

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

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

  • 79

    使用代码示例跟进@MrSnowflake答案 . This is the XABBER now open source Application class . Application 类正在集中和协调 Listeners 和ManagerInterfaces等 . 各种管理器都是动态加载的 . Activity´s 在Xabber中开始报告它们是什么类型的 Listener . 当 Service 开始时,它会报告 Application 类 . 现在要发送消息到 Activity 所有你需要做的就是让你的 Activity 成为你需要的类型 . 在 OnStart() OnPause() register / unreg . Service 可以向 Application 类询问它需要说话的 listener ,如果它在那里,则活动已准备好接收 .

    通过 Application 课程,你会发现更多的战利品 .

  • 2

    除了 LocalBroadcastManager , Event Bus and Messenger 已在这个问题中回答,我们可以使用 Pending Intent 从服务进行通信 .

    正如我在博客文章中提到的here

    可以使用PendingIntent来完成服务和Activity之间的通信 . 我们可以使用createPendingResult() . createPendingResult()创建一个新的PendingIntent对象,您可以将该服务交给服务使用,并将结果数据发送回onActivityResult内的活动(int ,int,Intent)callback . 由于PendingIntent是Parcelable,因此可以放入Intent extra,你的活动可以将这个PendingIntent传递给服务 . 服务反过来可以调用PendingIntent上的send()方法来通知活动的onActivityResult活动 . 活动公共类PendingIntentActivity扩展了AppCompatActivity
    {
    @覆盖
    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(意向);

    }

    @覆盖
    protected void onActivityResult(int requestCode,int resultCode,Intent data){
    if(requestCode == 100 && resultCode == 200){
    Toast.makeText(这一点,data.getStringExtra( “名称”),Toast.LENGTH_LONG).show();
    }
    super.onActivityResult(requestCode,resultCode,data);
    }
    }
    服务公共类PendingIntentService扩展Service {

    private static final String [] items = {“lorem”,“ipsum”,“dolor”,
    “坐”,“amet”,“consectetuer”,“adipiscing”,“elit”,“morbi”,
    “vel”,“ligula”,“简历”,“arcu”,“aliquet”,“mollis”,“etiam”,
    “vel”,“erat”,“placerat”,“ante”,“porttitor”,“sodales”,
    “pellentesque”“augue”,“purus”};
    私人PendingIntent数据;

    @覆盖
    public void onCreate(){
    super.onCreate();
    }

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

    data = intent.getParcelableExtra(“pendingIntent”);

    new LoadWordsThread() . start();
    返回START_NOT_STICKY;
    }

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

    @覆盖
    public void onDestroy(){
    super.onDestroy();
    }

    class LoadWordsThread扩展Thread {
    @覆盖
    public void run(){
    for(String item:items){
    if(!isInterrupted()){

    Intent result = new Intent();
    result.putExtra(“name”,item);
    尝试{
    data.send(PendingIntentService.this,200,结果);
    } catch(PendingIntent.CanceledException e){

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

    }
    }
    }
    }
    }

  • 0

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

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

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

  • 0

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

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

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

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

    代码样本被要求:

    在我的服务中,我有:

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

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

    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传递数据是有意义的 .

  • 37

    另一种方法是通过活动和服务本身使用具有假模型类的观察者,实现MVC模式变体 . 我不知道这是否是实现这一目标的最佳方式,但这是对我有用的方式 . 如果你需要一些例子请求它,我会发布一些东西 .

  • 242

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

    • 使用意图

    • 使用AIDL

    • 使用服务对象本身(作为单例)

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

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

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

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

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

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

  • 0

    您也可以使用 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
        }
    })
    

    你可以从这里了解更多blog.

  • 18

    如Madhur所述,您可以使用 Bus 车进行通信 .

    如果使用总线,您有一些选择:

    Otto事件总线库(不赞成使用RxJava)

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

    绿色机器人的EventBus

    http://greenrobot.org/eventbus/

    NYBus(RxBus,使用RxJava实现 . 非常类似于EventBus)

    https://github.com/MindorksOpenSource/NYBus

  • 2

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

  • 18

    使用 LocalBroadcastManager 注册接收器以侦听从应用内部的本地服务发送的广播,参考文献在此处:

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

相关问题