如何从一个服务中调用活动中的方法?

32

有一个服务正在监听某些声音,如果声音匹配某个字符串,则会在服务对象中调用特定的方法。

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");
     }

     //...
}

这是我在活动中启动服务的方式:

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

通过这种服务方法,我该如何调用活动对象中的方法? 我不想从活动访问服务,而是从服务访问活动。 我已经阅读了有关处理程序和广播接收器的信息,但找不到/理解任何示例。 有任何想法吗?


这个方法必须在Activity类中吗?还是可以将其移动到实用程序类中? - Raghav Sood
我想要更新用户界面,所以我认为它必须在活动类中。 - DarkLeafyGreen
4个回答

69

假设您的服务和活动在同一个包中(即同一个应用程序),则可以使用LocalBroadcastManager如下所示:

在您的服务中:

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

在你的活动中:

@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();
}

从@Ascorbin的链接的7.3节中得知:http://www.vogella.com/tutorials/AndroidBroadcastReceiver/article.html#ownreceiver_localbroadcastmanager


1
谢谢Tony!非常好的答案。 - Nickmccomb
简单明了的回答。谢谢。 - Alexandre Prazeres
我在我的项目中也使用了同样的代码,之前一直运行良好。但现在,在服务类中执行LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent)时出现了空指针异常。 java.lang.NullPointerException: 尝试在一个空对象引用上调用虚拟方法'void android.content.BroadcastReceiver.onReceive(android.content.Context, android.content.Intent)' - Dnyaneshwar

11

2
有许多不同的方法来实现这个。其中一种方法是使用HandlerMessenger类。该方法的思路是将Handler对象从Activity传递到Service中。每次Service想要调用Activity的某个方法时,它只需发送一个Message,而Activity会以某种方式处理它。

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();
    }
}

服务:

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

我的服务扩展自Service而不是IntentService。 - DarkLeafyGreen
你也可以使用 Service。这段代码只是展示了这个想法。我更新了我的回答,现在它使用 Service 而不是 IntentService - Vladimir Mironov
+1 谢谢,我已经使用广播接收器解决了它 http://inchoo.net/mobile-development/android-development/broadcast-receiver-from-activity/ - DarkLeafyGreen
3
如果我们需要经常从服务向活动发送命令/值,比如每秒20次,那么使用这两种方法(信使与广播)是否有性能差异?谢谢。 - Ewoks

0

经过一些研究,我在我的情况下找到了发送和接收广播的以下时间。我在同一个进程中有服务。

sendBroadcast(如果两个组件在同一个进程中,则不建议使用) 34秒

LocalBroadcastManager.getInstance(this).sendBroadcast(intent); 接近30秒

使用AIDL和RemoteCallbackList实现 适用于同一进程或不同进程

在您的服务中

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);
}

当你需要从服务中调用应用程序/活动中的方法时

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();
}

在扩展Application或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);
 }

从同一进程广播和接收的调用方式几乎是瞬间完成的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接