How to call a method in activity from a service
Asked Answered
C

4

32

There is a service that listens for some voice. If voice matches a string a certain method is invoked in the service object.

public class SpeechActivationService extends Service {

     public static Intent makeStartServiceIntent(Context pContext){    

         return new Intent(pContext, SpeechActivationService.class);
     }

     //...

     public void onMatch(){
         Log.d(TAG, "voice matches word");
     }

     //...
}

This is how I start the service in my activity:

Intent i = SpeechActivationService.makeStartServiceIntent(this);
startService(i);

From this service method, how can I invoke a method that resides in the activity object? I don't want access from activity to service, but from service to activity. I already read about handlers and broadcasters but could not find/understand any example. Any ideas?

Cloak answered 15/2, 2013 at 14:12 Comment(2)
Does the method have to be in the Activity class, or can you move it into a utility class?Subscribe
I want to update the user interface so I think it has to be in activity classCloak
S
11

I would register a BroadcastReceiver in the Activity and send an Intent to it from the service. See this tutorial: http://www.vogella.com/articles/AndroidBroadcastReceiver/article.html It might look a bit long but you'll want to learn how to use those anyway ;)

Sassanid answered 15/2, 2013 at 14:24 Comment(0)
T
68

Assuming your Service and Activity are in the same package (i.e. the same app), you can use LocalBroadcastManager as follows:

In your Service:

// Send an Intent with an action named "my-event". 
private void sendMessage() {
  Intent intent = new Intent("my-event");
  // add data
  intent.putExtra("message", "data");
  LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}

In your Activity:

@Override
public void onResume() {
  super.onResume();

  // Register mMessageReceiver to receive messages.
  LocalBroadcastManager.getInstance(this).registerReceiver(mMessageReceiver,
      new IntentFilter("my-event"));
}

// handler for received Intents for the "my-event" event 
private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
  @Override
  public void onReceive(Context context, Intent intent) {
    // Extract data included in the Intent
    String message = intent.getStringExtra("message");
    Log.d("receiver", "Got message: " + message);
  }
};

@Override
protected void onPause() {
  // Unregister since the activity is not visible
  LocalBroadcastManager.getInstance(this).unregisterReceiver(mMessageReceiver);
  super.onPause();
}

From section 7.3 of @Ascorbin's link: http://www.vogella.com/tutorials/AndroidBroadcastReceiver/article.html#ownreceiver_localbroadcastmanager

Thermograph answered 7/8, 2014 at 0:42 Comment(2)
Simple and direct answer. Thank you.Marquismarquisate
I'm using the same in my project. It was working fine. Now I'm getting null exception in service class while LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent); java.lang.NullPointerException: Attempt to invoke virtual method 'void android.content.BroadcastReceiver.onReceive(android.content.Context, android.content.Intent)' on a null object referenceCementum
S
11

I would register a BroadcastReceiver in the Activity and send an Intent to it from the service. See this tutorial: http://www.vogella.com/articles/AndroidBroadcastReceiver/article.html It might look a bit long but you'll want to learn how to use those anyway ;)

Sassanid answered 15/2, 2013 at 14:24 Comment(0)
A
2

There are many different ways to achive this. One of them to use Handler and Messanger classes. The idea of the method is to pass Handler object from Activity to Service. Every time Service wants to call some method of the Activity it just sends a Message and Activity handles it somehow.

Activity:

public class MyActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        final Handler handler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                showToast(msg.what);
            }
        };

        final Intent intent = new Intent(this, MyService.class);
        final Messenger messenger = new Messenger(handler);

        intent.putExtra("messenger", messenger);
        startService(intent);
    }

    private void showToast(int messageId) {
        Toast.makeText(this, "Message  " + messageId, Toast.LENGTH_SHORT).show();
    }
}

Service:

public class MyService extends Service {
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (intent != null) {
            final Messenger messenger = (Messenger) intent.getParcelableExtra("messenger");
            final Message message = Message.obtain(null, 1234);

            try {
                messenger.send(message);
            } catch (RemoteException exception) {
                exception.printStackTrace();
            }
        }

        return START_NOT_STICKY;
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}
Already answered 15/2, 2013 at 14:24 Comment(4)
My service extends from service and not IntentServiceCloak
You can use Service as well. This code just shows the idea. I updated my answer and now it uses Service instead of IntentServiceAlready
+1 Thank you, I already managed it with broadcast receiver inchoo.net/mobile-development/android-development/…Cloak
is there any performance difference in this two approach (messanger vs broadcasting) in case we are sending commands/values from service to activity often, let's say 20 times per second? thanksGogetter
S
0

After some research I found the following timings in my case for sending and receiving the broadcast. I have service in the same process.

sendBroadcast (Not recommended if both components are in same process) 34 sec

LocalBroadcastManager.getInstance(this).sendBroadcast(intent); close to 30 sec

Implementing using AIDL and RemoteCallbackList Will work for same process or different process

In your service

public final RemoteCallbackList<ICallBackAidl> mDMCallbacks = new RemoteCallbackList<ICallBackAidl>();

public void registerDMCallback(ICallBackAidl cb) {
    Logger.d(LOG_TAG, "registerDMCallback " + cb);
    if (cb != null)
        mDMCallbacks.register(cb);
}

When you need call methods in Application/Acitvity from service

public void callMehodsInApplication() {
    final int N = mDMCallbacks.beginBroadcast();
    for (int i = 0; i < N; i++) {
        try {
            mDMCallbacks.getBroadcastItem(i).method1();
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }
    mDMCallbacks.finishBroadcast();
}

In your class extending Application or Activity.

private ISyncmlServiceDMCallback mCallback = new ISyncmlServiceDMCallback.Stub() {
 // Implement callback methods here
  public void method1() {
       // Service can call this method
  }
}

 public void onServiceConnected(ComponentName name, IBinder service) {   
        svc.LocalBinder binder = (svc.LocalBinder) service;
        mSvc = binder.getService();
        mSvc.registerDMCallback(mCallback);
 }

Calling this way is almost instantaneous from broadcasting and receiving from the same process

Swarm answered 29/4, 2016 at 22:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.