2012-12-06 2 views
9

J'ai écrit deux applications (Gingerbread cible). Disons app1 et app2. App1 a deux services démarrés avec "BOOT_COMPLETED" et ils sont démarrés avec la valeur de retour START_STICKY. Ils fonctionnent dans des fils séparés. Pour faire court. L'un des services surveille les données entrantes sur un port série (une sorte de proxy pour l'application qui communique avec les interfaces à l'autre extrémité du port série). L'autre a un auditeur regardant l'état du système et attendant quelques "instructions" d'autres applications. Je sais qu'ils fonctionnent bien car ils sont listés dans les services courants et j'ai ajouté du code qui les oblige à faire certaines choses quand des données spécifiques viennent du port série.Liaison à un service depuis une autre application

Maintenant le problème: j'ai écrit app2. Il essaie de se lier à l'un des services dans app1. J'ai utilisé la documentation android-developper et mis en œuvre une communication bidirectionnelle entre le service dans app1 et app2. Depuis que j'ai juste une petite quantité de données très simples à envoyer, j'ai utilisé un messager, comme suggéré. J'utilise simplement le "what, arg1 et arg2" Je n'ai pas utilisé l'interface AIDL comme la documentation le suggérait.

Voici la section du androidmanifest déclarant le service dans l'application 1 que j'essaye de lier aussi.

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

Alors, voici quelques-uns méthode traitant de cette question dans app1:

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

Pour App2, voici le code pertinent pour établir la liaison avec la communication bidirectionnelle:

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

En regardant les services en cours d'exécution, je peux voir que le service que j'ai créé dans app2 est en cours d'exécution. Logcat me montre que j'essaye de lier le ModemWatcherService mais il n'est pas trouvé. Voici la partie intéressante de 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 

Ma première pensée était que je manque une autorisation, mais bindService() peut Trow exceptions de sécurité et dans ce cas, il ne le fait pas, je vérifié et il retourne faux pour une raison inconnue. Aussi, je sais que dans app1, onBind n'est jamais appelé prouvant que la liaison n'a jamais eu lieu. Donc le message de logcat "non trouvé" a du sens mais j'ai déclaré ce service public dans son manifeste. C'est probablement une simple erreur mais je suis sur cette émission depuis un moment maintenant et je n'ai pas trouvé pourquoi. Une idée de pourquoi app2 ne trouve pas le service dans app1? Je ne faisais que couper et coller des noms, donc je ne ferais pas de fautes stupides dans les noms. Ai-je des autorisations manquantes? Ai-je besoin de faire un pas de plus pour publier le service pour l'ensemble du système? C'est la première fois que j'essaie d'accéder à quelque chose dans une application d'une autre application, alors j'ai peut-être manqué quelque chose.

Répondre

15

Votre ComponentName est mal construit. Lors du passage au nom de la classe est doit être entièrement qualifié comme ceci:

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

Une autre chose, si vous faites référence à un Service en dehors des limites de l'application, il est probablement préférable de ne pas utiliser ComponentName pour faire référence, même si cela fonctionne correctement. Une approche plus courante serait de créer une chaîne ACTION personnalisée pour votre Intent et que le Service filtre cette action.

+1

Merci cela fonctionne. C'était le problème. Et merci pour la meilleure note de pratique aussi. –

+3

Depuis lollipop, il est interdit de se lier au service en utilisant l'intention implicite. Donc ComponentName semble être la seule option. – Ov3r1oad

Questions connexes