Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Communication between Activity and Service

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.

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.

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

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

like image 930
Dejan Avatar asked Dec 15 '13 13:12

Dejan


People also ask

How do you communicate between services and activities?

This example demonstrates how do I communicate between Activity and Service in android. Step 1 − Create a new project in Android Studio, go to File ⇒ New Project and fill all required details to create a new project. Step 2 − Add the following code to res/layout/activity_main. xml.

What is the difference between activity and service?

Services are a unique component in Android that allows an application to run in the background to execute long-running operation activities, on the other hand, an activity, like a window or a frame in Java, represents a single screen with a user interface.

How do I send a callback from service to activity?

You need to create a BroadcastReceiver in your activity (be sure to register it in onResume() and unregister it in onPause() ) and notify it via a broadcast, providing an Intent .


Video Answer


2 Answers

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

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.

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.

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

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);     } } 
like image 199
Moti Bartov Avatar answered Sep 23 '22 05:09

Moti Bartov


Update: July 10 2016

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.

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

  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 Service with 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 Service you receive this object from the intent and initialize the Messenger variable in Service as

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

    And then write a method sendMessage to 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(); } } 

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

like image 29
Rachit Mishra Avatar answered Sep 19 '22 05:09

Rachit Mishra