2012-12-06 10 views
9

Ich schrieb zwei Apps (Ziel Gingerbread). Sagen wir App1 und App2. App1 hat zwei Dienste, die mit "BOOT_COMPLETED" gestartet wurden und sie werden mit dem Rückgabewert START_STICKY gestartet. Sie laufen in separaten Threads. Um es kurz zu machen. Einer der Dienste überwacht eingehende Daten auf einer seriellen Schnittstelle (eine Art Proxy für eine App, die mit Schnittstellen am anderen Ende der seriellen Schnittstelle kommuniziert). Der andere hat einen Zuhörer, der einen Systemstatus beobachtet und auf einige "Anweisungen" von anderen Apps wartet. Ich weiß, dass sie gut laufen, weil sie in den laufenden Diensten aufgelistet sind, und ich habe etwas Code hinzugefügt, der sie dazu zwingt, etwas zu tun, wenn bestimmte Daten von der seriellen Schnittstelle kommen.Bindung an einen Dienst von einer anderen App

Jetzt das Problem: Ich schrieb app2. Es versucht, an einen Dienst in app1 zu binden. Ich habe android-developper Dokumentation verwendet und eine bidirektionale Kommunikation zwischen dem Dienst in app1 und app2 implementiert. Da ich nur eine kleine Menge sehr einfacher Daten zu senden habe, habe ich wie vorgeschlagen einen Messenger benutzt. Ich benutze im Grunde nur die "Was, Arg1 und Arg2" Ich habe nicht die AIDL-Schnittstelle verwendet, wie die Dokumentation vorgeschlagen hat.

Hier ist der Abschnitt der Androidmanifest deklariert den Dienst in app1 Ich versuche zu binden.

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

Dann hier sind die wenigen Verfahren mit diesem Thema in app1 Umgang:

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

Für app2, hier ist der entsprechende Code die Bindung mit der bidirektionalen Kommunikation herzustellen:

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

Mit Blick auf die laufenden Dienste kann ich sehen, dass der Dienst, den ich in app2 erstellt habe, läuft. Logcat zeigt mir, dass ich versuche, den ModemWatcherService zu binden, aber es wird nicht gefunden. Hier ist der interessante Abschnitt 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 

Mein erster Gedanke war, dass ich eine Erlaubnis fehlte aber bindService() Sicherheits Ausnahmen trow und in diesem Fall ist es nicht so, ich überprüft und es gibt false zurück, für eine unbekannter Grund. Außerdem weiß ich, dass der onBind in app1 nie aufgerufen wird, um zu beweisen, dass die Bindung niemals aufgetreten ist. So macht die Logcat-Nachricht "not found" Sinn, aber ich habe diesen Dienst in seinem Manifest public deklariert. Es ist wahrscheinlich ein einfacher Fehler, aber ich bin seit einiger Zeit auf diesem Thema und habe den Grund nicht gefunden. Irgendeine Idee, warum app2 den Dienst in app1 nicht finden kann? Ich habe einfach Namen ausgeschnitten und eingefügt, damit ich keine dämlichen Fehler in den Namen machen würde. Fehle ich irgendwelche Berechtigungen? Muss ich einen zusätzlichen Schritt ausführen, um den Service für das gesamte System zu veröffentlichen? Das ist das erste Mal, dass ich versuche, in einer App von einer anderen App aus auf etwas zuzugreifen, sodass ich etwas übersehen habe.

Antwort

15

Ihre ComponentName ist falsch konstruiert.

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

Eine andere Sache, wenn Sie eine Service außerhalb der Grenzen der Anwendung verweisen, ist es wahrscheinlich am besten nicht ComponentName zu verwenden, um es zu verweisen, auch wenn: Wenn der Klassenname vorbei ist, muss vollständig wie so qualifiziert sein es funktioniert richtig. Ein üblicherer Ansatz wäre es, eine benutzerdefinierte ACTION-Zeichenfolge für Ihre Intent zu erstellen und diese Aktion mit dem Filter Service zu filtern.

+1

Danke, es funktioniert. Das war das Problem. Und danke für den besseren Übungshinweis. –

+3

Seit Lollipop ist es verboten, mit impliziter Absicht an den Dienst zu binden. So scheint ComponentName die einzige Option zu sein. – Ov3r1oad

Verwandte Themen