Привязка к сервису из другого приложения

Я написал два приложения (целевое Gingerbread). Пусть говорят app1 и app2. В App1 есть две службы, начинающиеся с «BOOT_COMPLETED», и они начинаются с возвращаемого значения START_STICKY. Они работают в отдельных потоках. Короче говоря. Одна из услуг – просмотр входящих данных на последовательном порту (своего рода прокси для приложения, взаимодействующего с интерфейсами на другом конце последовательного порта). У другого есть слушатель, наблюдающий за некоторым статусом системы и ожидающий некоторых «инструкций» от других приложений. Я знаю, что они работают хорошо, потому что они перечислены в работающих службах, и я добавил код, который заставляет их делать некоторые вещи, когда некоторые конкретные данные поступают из последовательного порта.

Теперь проблема: я написал app2. Он пытается связать одну из сервисов в приложении 1. Я использовал документацию по разработке Android и реализовал двунаправленную связь между сервисом в приложениях1 и приложении2. Поскольку у меня просто небольшое количество очень простых данных для отправки, я использовал мессенджера, как было предложено. Я просто использую «что, arg1 и arg2», я не использовал интерфейс AIDL, как предлагала документация.

Вот раздел андроидального манифеста, объявляющий службу в app1, которую я тоже пытаюсь связать.

<service android:name=".ModemWatcherService" android:label="@string/app_name" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> <!-- Service name --> <action android:name="com.admetric.modemwatcher.Service" /> </intent-filter> </service> 

Затем, вот несколько способов решения этой проблемы в приложении 1:

  @Override public IBinder onBind(Intent intent) { Log.d(TAG, "entering onBind"); return mMessenger.getBinder(); } /** * Handler of incoming messages from clients. */ class IncomingHandler extends Handler { @Override public void handleMessage(Message msg) { String logMessage = "Received meaasge what= %d, arg1= %d, arg2= %d" + String.valueOf(msg.what) + String.valueOf(msg.arg1) + String.valueOf( msg.arg2); Log.d(TAG, logMessage); switch (msg.what) { case MSG_REGISTER_CLIENT: mClients.add(msg.replyTo); break; case MSG_UNREGISTER_CLIENT: mClients.remove(msg.replyTo); break; case ..... more code here for the application default: super.handleMessage(msg); } } } @Override public void onCreate() { mHandler = new Handler(); startSignalLevelListener(); Log.i(TAG, "Just did onCreated"); // Display a notification about us starting. We put an icon in the status bar. // showNotification(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.i(TAG, "Received start id " + startId + ": " + intent); // We want this service to continue running until it is explicitly // stopped, so return sticky. return START_STICKY; } 

Для приложения 2 вот соответствующий код для установления привязки с двунаправленной связью:

 public final class ComWithIoMcu extends Service { private static final String TAG = "ComWithIoMcu"; /** Messenger for communicating with service. */ static Messenger mServiceMcu = null; /** Flag indicating whether we have called bind on the service. */ boolean mIsBound; /** * Command to the service to register a client, receiving callbacks * from the service. The Message's replyTo field must be a Messenger of * the client where callbacks should be sent. */ static final int MSG_REGISTER_CLIENT = 1; /** * Command to the service to unregister a client, ot stop receiving callbacks * from the service. The Message's replyTo field must be a Messenger of * the client as previously given with MSG_REGISTER_CLIENT. */ static final int MSG_UNREGISTER_CLIENT = 2; /** * Command to forward a string command to the I/O MCU */ public static final int MSG_SEND_STRING_TO_IOMCU = 3; /** List of supported commands * */ ...... more code .... /** * Handler of incoming messages from service. */ class IncomingHandler extends Handler { @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_UNSOL_MESSAGE: Log.d(TAG, "Received from service: " + msg.arg1); break; default: super.handleMessage(msg); } } } /** * Target we publish for clients to send messages to IncomingHandler. */ final Messenger mMessenger = new Messenger(new IncomingHandler()); boolean mBound; /** * Class for interacting with the main interface of the service. */ private ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { // This is called when the connection with the service has been // established, giving us the service object we can use to // interact with the service. We are communicating with our // service through an IDL interface, so get a client-side // representation of that from the raw service object. mServiceMcu = new Messenger(service); Log.d(TAG, "Attached."); // We want to monitor the service for as long as we are // connected to it. try { Message msg = Message.obtain(null, MSG_REGISTER_CLIENT); msg.replyTo = mMessenger; mServiceMcu.send(msg); } catch (RemoteException e) { // In this case the service has crashed before we could even // do anything with it; we can count on soon being // disconnected (and then reconnected if it can be restarted) // so there is no need to do anything here. Log.e(TAG, "ModemWatcherService is not running"); } } public void onServiceDisconnected(ComponentName className) { // This is called when the connection with the service has been // unexpectedly disconnected -- that is, its process crashed. mServiceMcu = null; mBound = false; } }; void doBindService() { // Establish a connection with the service. We use an explicit // class name because there is no reason to be able to let other // applications replace our component. //bindService(new Intent(this, MessengerService.class), mConnection, Context.BIND_AUTO_CREATE); try { Intent intentForMcuService = new Intent(); Log.d(TAG, "Before init intent.componentName"); intentForMcuService.setComponent(new ComponentName("com.admetric.modemwatcher", "ModemWatcherService")); Log.d(TAG, "Before bindService"); if (bindService(intentForMcuService, mConnection, 0)){ Log.d(TAG, "Binding to Modem Watcher returned true"); } else { Log.d(TAG, "Binding to Modem Watcher returned false"); } } catch (SecurityException e) { Log.e(TAG, "can't bind to ModemWatcherService, check permission in Manifest"); } mIsBound = true; Log.d(TAG, "Binding."); } void doUnbindService() { if (mIsBound) { // If we have received the service, and hence registered with // it, then now is the time to unregister. if (mServiceMcu != null) { try { Message msg = Message.obtain(null, MSG_UNREGISTER_CLIENT); msg.replyTo = mMessenger; mServiceMcu.send(msg); } catch (RemoteException e) { // There is nothing special we need to do if the service // has crashed. } } // Detach our existing connection. unbindService(mConnection); mIsBound = false; Log.d(TAG, "Unbinding."); } } 

Посмотрев на запущенные службы, я вижу, что служба, созданная в приложении 2, запущена. Logcat показывает мне, что я пытаюсь связать ModemWatcherService, но он не найден. Вот интересный раздел logcat

 12-05 17:22:59.884 D/ComWithIoMcu( 547): Before init intent.componentName 12-05 17:22:59.884 D/ComWithIoMcu( 547): Before bindService 12-05 17:22:59.888 D/ComWithIoMcu( 547): Binding to Modem Watcher returned false 12-05 17:22:59.888 D/ComWithIoMcu( 547): Binding. 12-05 17:22:59.888 W/ActivityManager( 89): Unable to start service Intent { cmp=com.admetric.modemwatcher/ModemWatcherService }: not found 

Моя первая мысль заключалась в том, что у меня отсутствовало разрешение, но bindService () может троить исключения безопасности, и в этом случае это не так, я проверил и возвращает false по неизвестной причине. Кроме того, я знаю, что в app1 onBind никогда не называется, доказывая, что привязка никогда не происходила. Таким образом, сообщение logcat «не найдено» имеет смысл, но я объявил это публичное обслуживание в своем манифесте. Вероятно, это простая ошибка, но я уже давно занимаюсь этим выпуском, и я не нашел почему. Любая идея, почему app2 не может найти службу в приложении 1? Я использовал просто вырезать и вставлять имена, чтобы я не делал глупых ошибок в именах. Я пропускаю какие-то разрешения? Нужно ли мне сделать дополнительный шаг, чтобы опубликовать сервис для всей системы? Это первый случай, когда я пытаюсь получить доступ к чему-то в одном приложении из другого приложения, поэтому я мог что-то пропустить.

Solutions Collecting From Web of "Привязка к сервису из другого приложения"

Неправильное построение вашего ComponentName . При прохождении в имени класса необходимо иметь полную квалификацию:

 intentForMcuService.setComponent(new ComponentName("com.admetric.modemwatcher", "com.admetric.modemwatcher.ModemWatcherService")); 

Другое дело, если вы ссылаетесь на Service вне границ приложения, вероятно, лучше не использовать ComponentName для ссылки на нее, даже если она работает правильно. Более общим подходом было бы создание пользовательской строки ACTION для вашего Intent и фильтрация Service .