Android Activity 和 Service 之间的通信

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20594936/
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 03:07:10  来源:igfitidea点击:

Communication between Activity and Service

androidandroid-activityandroid-servicemessage-passing

提问by Dejan

I am trying to make my own MusicPlayer for android. Where i came to a problem is running some things in background. Main activity manages GUI and up to now all the songs are playing. I wanted to separate GUI and music playing classes. I want to put music managing part in Service and leave other things as they are now.

我正在尝试为 android 制作我自己的 MusicPlayer。我遇到问题的地方是在后台运行一些东西。主要活动管理 GUI,到目前为止所有歌曲都在播放。我想将 GUI 和音乐播放类分开。我想把音乐管理部分放在服务中,而其他东西保持现状。

My problem is that i can't organize communication between Activity and Service as lot of communication is happening between them including moving objects in both directions. I tried many techniques that I searched here on Stack Overflow but every time I had problems. I need Service to be able to send objects to Activity and vice versa. When I add widget i also want it to be able to communicate with Service.

我的问题是我无法组织 Activity 和 Service 之间的通信,因为它们之间发生了很多通信,包括双向移动对象。我尝试了许多在 Stack Overflow 上搜索过的技术,但每次都遇到问题。我需要 Service 才能将对象发送到 Activity,反之亦然。当我添加小部件时,我还希望它能够与服务通信。

Any tips are appreciated, if you need source code place comment bellow but now in this transition it became chaotic.

感谢您提供任何提示,如果您需要源代码在下面放置注释,但现在在此过渡中它变得混乱。

Is there any more advanced tutorial on this than calling one method that returns random number from service? :P

有没有比调用一种从服务返回随机数的方法更高级的教程?:P

EDIT: Possible solution is to use RoboGuice library and move objects with injection

编辑:可能的解决方案是使用 RoboGuice 库并通过注入移动对象

回答by Moti Bartov

I have implemented communication between Activity and Service using Bind and Callbacks interface.

我已经使用 Bind 和 Callbacks 接口实现了 Activity 和 Service 之间的通信。

For sending data to the service I used Binder which retruns the Service instace to the Activity, and then the Activity can access public methods in the Service.

为了将数据发送到服务,我使用了 Binder,它将服务实例重新运行到活动,然后活动可以访问服务中的公共方法。

To send data back to the Activity from the Service, I used Callbacks interface like you are using when you want to communicate between Fragment and Activity.

为了将数据从服务发送回活动,我使用了回调接口,就像您在片段和活动之间进行通信时所使用的那样。

Here is some code samples for each: The following example shows Activity and Service bidirectional relationship: The Activity has 2 buttons: The first button will start and stop the service. The second button will start a timer which runs in the service.

下面是每个的一些代码示例: 以下示例显示了 Activity 和 Service 双向关系: Activity 有 2 个按钮:第一个按钮将启动和停止服务。第二个按钮将启动在服务中运行的计时器。

The service will update the Activity through callback with the timer progress.

该服务将通过带有计时器进度的回调来更新 Activity。

My Activity:

我的活动:

    //Activity implements the Callbacks interface which defined in the Service  
    public class MainActivity extends ActionBarActivity implements MyService.Callbacks{

    ToggleButton toggleButton;
    ToggleButton tbStartTask;
    TextView tvServiceState;
    TextView tvServiceOutput;
    Intent serviceIntent;
    MyService myService;
    int seconds;
    int minutes;
    int hours;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
         serviceIntent = new Intent(MainActivity.this, MyService.class);
        setViewsWidgets();
    }

    private void setViewsWidgets() {
        toggleButton = (ToggleButton)findViewById(R.id.toggleButton);
        toggleButton.setOnClickListener(btListener);
        tbStartTask = (ToggleButton)findViewById(R.id.tbStartServiceTask);
        tbStartTask.setOnClickListener(btListener);
        tvServiceState = (TextView)findViewById(R.id.tvServiceState);
        tvServiceOutput = (TextView)findViewById(R.id.tvServiceOutput);

    }

    private ServiceConnection mConnection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName className,
                                       IBinder service) {
            Toast.makeText(MainActivity.this, "onServiceConnected called", Toast.LENGTH_SHORT).show();
            // We've binded to LocalService, cast the IBinder and get LocalService instance
            MyService.LocalBinder binder = (MyService.LocalBinder) service; 
            myService = binder.getServiceInstance(); //Get instance of your service! 
            myService.registerClient(MainActivity.this); //Activity register in the service as client for callabcks! 
            tvServiceState.setText("Connected to service...");
            tbStartTask.setEnabled(true);
        }

        @Override
        public void onServiceDisconnected(ComponentName arg0) {
            Toast.makeText(MainActivity.this, "onServiceDisconnected called", Toast.LENGTH_SHORT).show();
            tvServiceState.setText("Service disconnected");
            tbStartTask.setEnabled(false);
        }
    };

    View.OnClickListener btListener =  new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if(v == toggleButton){
                if(toggleButton.isChecked()){
                    startService(serviceIntent); //Starting the service 
                    bindService(serviceIntent, mConnection,            Context.BIND_AUTO_CREATE); //Binding to the service! 
                    Toast.makeText(MainActivity.this, "Button checked", Toast.LENGTH_SHORT).show();
                }else{
                    unbindService(mConnection);
                    stopService(serviceIntent);
                    Toast.makeText(MainActivity.this, "Button unchecked", Toast.LENGTH_SHORT).show();
                    tvServiceState.setText("Service disconnected");
                    tbStartTask.setEnabled(false);
                }
            }

            if(v == tbStartTask){
                if(tbStartTask.isChecked()){
                      myService.startCounter();
                }else{
                    myService.stopCounter();
                }
            }
        }
    };

    @Override
    public void updateClient(long millis) {
        seconds = (int) (millis / 1000) % 60 ;
        minutes = (int) ((millis / (1000*60)) % 60);
        hours   = (int) ((millis / (1000*60*60)) % 24);

        tvServiceOutput.setText((hours>0 ? String.format("%d:", hours) : "") + ((this.minutes<10 && this.hours > 0)? "0" + String.format("%d:", minutes) :  String.format("%d:", minutes)) + (this.seconds<10 ? "0" + this.seconds: this.seconds));
    }
}

And here is the service:

这是服务:

 public class MyService extends Service {
    NotificationManager notificationManager;
    NotificationCompat.Builder mBuilder;
    Callbacks activity;
    private long startTime = 0;
    private long millis = 0;
    private final IBinder mBinder = new LocalBinder();
    Handler handler = new Handler();
    Runnable serviceRunnable = new Runnable() {
        @Override
        public void run() {
            millis = System.currentTimeMillis() - startTime;
            activity.updateClient(millis); //Update Activity (client) by the implementd callback
            handler.postDelayed(this, 1000);
        }
    };


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

        //Do what you need in onStartCommand when service has been started
        return START_NOT_STICKY;
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
    //returns the instance of the service
    public class LocalBinder extends Binder{
        public MyService getServiceInstance(){
            return MyService.this;
        }
    }

    //Here Activity register to the service as Callbacks client
    public void registerClient(Activity activity){
        this.activity = (Callbacks)activity;
    }

    public void startCounter(){
        startTime = System.currentTimeMillis();
        handler.postDelayed(serviceRunnable, 0);
        Toast.makeText(getApplicationContext(), "Counter started", Toast.LENGTH_SHORT).show();
    }

    public void stopCounter(){
        handler.removeCallbacks(serviceRunnable);
    }


    //callbacks interface for communication with service clients! 
    public interface Callbacks{
        public void updateClient(long data);
    }
}

回答by Rachit Mishra

Update: July 10 2016

更新:2016 年 7 月 10 日

IMO I think using BroadcastReceiver for custom events is better way as the Messengers mentioned don't handle activity recreation on device rotation as well as possible memory leaks.

IMO 我认为对自定义事件使用 BroadcastReceiver 是更好的方法,因为提到的 Messenger 不处理设备旋转时的活动重新创建以及可能的内存泄漏。

You may create custom BroadCast Receiver for events in the activity, Then you may also use Messengers.

您可以为活动中的事件创建自定义广播接收器,然后您也可以使用 Messenger。

  1. In your Activity

    create a MessageHandler class as

    public static class MessageHandler extends Handler {
        @Override
        public void handleMessage(Message message) {
            int state = message.arg1;
            switch (state) {
            case HIDE:
                progressBar.setVisibility(View.GONE);
                break;
            case SHOW:
                progressBar.setVisibility(View.VISIBLE);
                break;
            }
        }
    }
    

    Now you can have it's instance as

    public static Handler messageHandler = new MessageHandler();
    

    Start your Servicewith this Handler object as an extra data as

    Intent startService = new Intent(context, SERVICE.class)
    startService.putExtra("MESSENGER", new Messenger(messageHandler));
    context.startService(startService);
    
  2. In your Serviceyou receive this object from the intent and initialize the Messengervariable in Service as

    private Messenger messageHandler;
    Bundle extras = intent.getExtras();
    messageHandler = (Messenger) extras.get("MESSENGER");
    sendMessage(ProgressBarState.SHOW);
    

    And then write a method sendMessageto send messages to activity.

    public void sendMessage(ProgressBarState state) {
    Message message = Message.obtain();
    switch (state) {
        case SHOW :
            message.arg1 = Home.SHOW;
            break;
        case HIDE :
            message.arg1 = Home.HIDE;
            break;
    }
    try {
        messageHandler.send(message);
    } catch (RemoteException e) {
        e.printStackTrace();
    }
    }
    
  1. 在你的 Activity

    创建一个 MessageHandler 类作为

    public static class MessageHandler extends Handler {
        @Override
        public void handleMessage(Message message) {
            int state = message.arg1;
            switch (state) {
            case HIDE:
                progressBar.setVisibility(View.GONE);
                break;
            case SHOW:
                progressBar.setVisibility(View.VISIBLE);
                break;
            }
        }
    }
    

    现在你可以把它的实例作为

    public static Handler messageHandler = new MessageHandler();
    

    启动Service与该Handler对象作为一个额外的数据,

    Intent startService = new Intent(context, SERVICE.class)
    startService.putExtra("MESSENGER", new Messenger(messageHandler));
    context.startService(startService);
    
  2. 在您中,Service您从意图接收此对象并将MessengerService 中的变量初始化为

    private Messenger messageHandler;
    Bundle extras = intent.getExtras();
    messageHandler = (Messenger) extras.get("MESSENGER");
    sendMessage(ProgressBarState.SHOW);
    

    然后编写一个方法sendMessage来向活动发送消息。

    public void sendMessage(ProgressBarState state) {
    Message message = Message.obtain();
    switch (state) {
        case SHOW :
            message.arg1 = Home.SHOW;
            break;
        case HIDE :
            message.arg1 = Home.HIDE;
            break;
    }
    try {
        messageHandler.send(message);
    } catch (RemoteException e) {
        e.printStackTrace();
    }
    }
    

The sample code above shows and hides a ProgressBar in Activity as messages are received from Service.

当从 Service 接收消息时,上面的示例代码显示并隐藏了 Activity 中的 ProgressBar。

回答by mpolci

Intents are good solution for communication between Activitiy and Service.

Intent 是 Activitiy 和 Service 之间通信的良好解决方案。

A fast solution for receive intents in your service is subclassing IntentServiceclass. It handles asynchronous requests expressed as Intents using a queue and worker thread.

在您的服务中接收意图的快速解决方案是继承 IntentService类。它使用队列和工作线程处理表示为 Intent 的异步请求。

For communication from service to Activity you can broadcast the intent but instead of using normal sendBroadcast() from Context, a more efficent way is to use LocalBroadcastManagerfrom support library.

对于从服务到活动的通信,您可以广播意图,但不是使用来自 Context 的普通 sendBroadcast(),更有效的方法是使用来自支持库的LocalBroadcastManager

Example service.

示例服务。

public class MyIntentService extends IntentService {
    private static final String ACTION_FOO = "com.myapp.action.FOO";
    private static final String EXTRA_PARAM_A = "com.myapp.extra.PARAM_A";

    public static final String BROADCAST_ACTION_BAZ = "com.myapp.broadcast_action.FOO";
    public static final String EXTRA_PARAM_B = "com.myapp.extra.PARAM_B";

    // called by activity to communicate to service
    public static void startActionFoo(Context context, String param1) {
        Intent intent = new Intent(context, MyIntentService.class);
        intent.setAction(ACTION_FOO);
        intent.putExtra(EXTRA_PARAM1, param1);
        context.startService(intent);
    }

    public MyIntentService() {
        super("MyIntentService");
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        if (intent != null) {
            final String action = intent.getAction();
            if (ACTION_FOO.equals(action)) {
                final String param1 = intent.getStringExtra(EXTRA_PARAM_A);
                // do something
            }
        }
    }

    // called to send data to Activity
    public static void broadcastActionBaz(String param) {
        Intent intent = new Intent(BROADCAST_ACTION_BAZ);
        intent.putExtra(EXTRA_PARAM_B, param);
        LocalBroadcastManager bm = LocalBroadcastManager.getInstance(this);
        bm.sendBroadcast(intent);
    }
}

Example Activity

示例活动

public class MainActivity extends ActionBarActivity {

    // handler for received data from service
    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent.getAction().equals(MyIntentService.BROADCAST_ACTION_BAZ)) {
                final String param = intent.getStringExtra(EXTRA_PARAM_B);
                // do something
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        IntentFilter filter = new IntentFilter();
        filter.addAction(MyIntentService.BROADCAST_ACTION_BAZ);
        LocalBroadcastManager bm = LocalBroadcastManager.getInstance(this);
        bm.registerReceiver(mBroadcastReceiver, filter);
    }

    @Override
    protected void onDestroy() {
        LocalBroadcastManager bm = LocalBroadcastManager.getInstance(this);
        bm.unregisterReceiver(mBroadcastReceiver);
        super.onDestroy();
    }

    // send data to MyService
    protected void communicateToService(String parameter) {
        MyIntentService.startActionFoo(this, parameter);
    }
}

回答by HungNM2

I think there is a problem with the correct answer. I have not enough reputation to comment on it.

我认为正确答案有问题。我没有足够的声誉来评论它。

Right in the answer:Activity call bindService() to get pointer to Service is ok. Because service context is maintained when connection is maintained.

就在答案中:Activity 调用 bindService() 来获取指向 Service 的指针是可以的。因为维护连接时维护服务上下文。

wrong in the answer:service pointer to Activity class to call back is bad way. Activity instance maybe not null during Activity context is being Release => exception here.

答案错误:服务指针指向 Activity 类进行回调是不好的方式。在 Activity 上下文被 Release => 异常期间,Activity 实例可能不为空。

solution for the wrong in the answer:service send intent to Activity. and Activity receiver intent via BroadcastReceiver.

答案中错误的解决方案:service send intent to Activity。和通过 BroadcastReceiver 的活动接收者意图。

Note:in this case, Service and Activity in the same Process, you should use LocalBroadcastManager to send intent. It make performance and security better

注意:在这种情况下,Service 和 Activity 在同一个 Process 中,您应该使用 LocalBroadcastManager 发送 Intent。它使性能和安全性更好

回答by Vikram Singh

The best way in this case is to communicate by doing broadcasting from your service for different actions and receiving it in your activity. You can create a custom broadcast and send some codes defining specific events like complete, change, prepare etc...

在这种情况下,最好的方法是通过从您的服务广播不同的操作并在您的活动中接收它来进行通信。您可以创建自定义广播并发送一些定义特定事件的代码,例如完成、更改、准备等...

回答by DarckBlezzer

This is a simple example of communication between activity and service

这是活动和服务之间通信的简单示例

Activity

活动

MyReceiver myReceiver; //my global var receiver
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.layourAwesomexD);
    registerReceiver();
}

//When the activity resume, the receiver is going to register...
@Override
protected void onResume() {
    super.onResume();
    checkStatusService(); // verficarStatusServicio(); <- name change
    registerReceiver();
}
//when the activity stop, the receiver is going to unregister...
@Override
protected void onStop() {
    unregisterReceiver(myReceiver); //unregister my receiver...
    super.onStop();
}
//function to register receiver :3
private void registerReceiver(){
    //Register BroadcastReceiver
    //to receive event from our service
    myReceiver = new MyReceiver();
    IntentFilter intentFilter = new IntentFilter();
    intentFilter.addAction(MyService.SENDMESAGGE);
    registerReceiver(myReceiver, intentFilter);
}

// class of receiver, the magic is here...
private class MyReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context arg0, Intent arg1) {
        //verify if the extra var exist
        System.out.println(arg1.hasExtra("message")); // true or false
        //another example...
        System.out.println(arg1.getExtras().containsKey("message")); // true or false
        //if var exist only print or do some stuff
        if (arg1.hasExtra("message")) {
            //do what you want to
            System.out.println(arg1.getStringExtra("message"));
        }    
    }
}

public void checkStatusService(){
    if(MyService.serviceStatus!=null){
        if(MyService.serviceStatus == true){
            //do something
            //textview.text("Service is running");
        }else{
            //do something
            //textview.text("Service is not running");
        }
    }
}


Service

服务

public class MyService extends Service {

final static String SENDMESAGGE = "passMessage";

public static Boolean serviceStatus = false;

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

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

@Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        //you service etc...             
        passMessageToActivity("hello my friend this an example of send a string...");
        return START_STICKY;
    }

@Override
    public void onDestroy() {
        super.onDestroy();
        passMessageToActivity("The service is finished, This is going to be more cooler than the heart of your ex...");
        System.out.println("onDestroy");
        serviceStatus=false;
   }

private void passMessageToActivity(String message){
        Intent intent = new Intent();
        intent.setAction(SENDMESAGGE);
        intent.putExtra("message",message);
        sendBroadcast(intent);
    }
}
  • if we don't unregister BroadcastReceiver we will have an error, you need to unregister when the activity go onPause, onStop, onDestroy...
  • if you don't register BroadcastReceiver when you back to activity, it will not listen anything from the service... the service will send information to BroadcastReceiver but it will not receive anything because it isn't registered.
  • When you create more than one service, the following services are going to begin in onStartCommand.
  • You can pass information to service with intent and you get it in onStartCommand
  • Difference about returnin onStartCommand: Difference between START_STICKY and START_REDELIVER_INTENT?and check the official website of google: Services
  • 如果我们不取消注册 BroadcastReceiver,我们将出现错误,您需要在活动继续时取消注册,例如暂停、停止、销毁...
  • 如果您在返回活动时未注册 BroadcastReceiver,它将不会从服务中侦听任何内容……该服务将向 BroadcastReceiver 发送信息,但它不会收到任何信息,因为它未注册。
  • 当您创建多个服务时,以下服务将在onStartCommand.
  • 您可以有意识地将信息传递给服务,然后将其获取 onStartCommand
  • 区别return在于onStartCommandSTART_STICKY 和 START_REDELIVER_INTENT 之间的区别?并查看谷歌的官方网站:服务

回答by Melad

Very easy yet powerful way is to use EventBusyou can add it to your gradle build and enjoy the easy publisher/subscriber pattern .

非常简单但功能强大的方法是使用EventBus,您可以将其添加到您的 gradle 构建中并享受简单的发布者/订阅者模式。

回答by Khemraj

Most easy and efficient way will be using EventBusfrom GreenRobot.

最简单有效的方法是使用GreenRobot 的EventBus

Use simple 3 steps:

使用简单的 3 步:

1 Define events

1 定义事件

public static class MessageEvent { /* Additional fields if needed */ }

2 Prepare subscribers: Declare and annotate your subscribing method, optionally specify a thread mode:

2 准备订阅者:声明并注释您的订阅方法,可选择指定线程模式:

@Subscribe(threadMode = ThreadMode.MAIN)  
public void onMessageEvent(MessageEvent event) {/* Do something */};

Register and unregister your subscriber. For example on Android, activities and fragments should usually register according to their life cycle:

注册和注销您的订阅者。例如在 Android 上,Activity 和 Fragment 通常应该根据它们的生命周期进行注册:

@Override
 public void onStart() {
     super.onStart();
     EventBus.getDefault().register(this);
 }

 @Override
 public void onStop() {
     super.onStop();
     EventBus.getDefault().unregister(this);
 }

3 Post events:

3 发布事件:

EventBus.getDefault().post(new MessageEvent());