2013-01-10 7 views
6

La mia app utilizza un servizio fornito da un'altra (delle mie) app. Sto usando uno spirito bound service con un Messenger per accedere e comunicare con esso (e poiché è un'app diversa, è anche un servizio remoto).bindService() restituisce false ma unbindService() deve essere chiamato?

Quando chiamo bindService con un intento corretta, e la chiamata restituisce false (come previsto quando l'APK che fornisce il servizio non è in giro), ho assunto dalla documentazione e il codice di esempio, che io non ho bisogno di unbind il ServiceConnection. Tuttavia, quando lo faccio sul mio dispositivo Galaxy Nexus (Jelly Bean), ricevo il noto messaggio ServiceConnectionLeaked al termine dello Activity.

ho recuperato che questo facendo

if (!ctxt.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)) { 
    try { 
     ctxt.unbindService(serviceConnection); 
    } catch (Throwable t) {} 
    // Clean up 
    return; 
} 
// Store serviceConnection for future use 

sono curioso: Ho appena perso qualcosa nella documentazione e se è dovuto lavorare in questo modo? Ho aggiunto try ... catch per assicurarmi che anche se questo comportamento è effettivamente diverso su altri dispositivi o versioni di Android, la mia app non ne è influenzata negativamente.

risposta

7

In generale, un ServiceConnection viene sempre assegnato e registrato dal framework, indipendentemente dal fatto che sia bindService() return di ritorno true o false. Vedere bindService() implementazione in android.app.ContextImpl:

public boolean bindService(Intent service, ServiceConnection conn, int flags, int userHandle) { 
    IServiceConnection sd; 
    if (conn == null) { 
     throw new IllegalArgumentException("connection is null"); 
    } 
    if (mPackageInfo != null) { 
     // A new ServiceDispatcher will be created and registered along with 
     // ServiceConnection in LoadedApk.mService for your application context. 
     sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), 
       mMainThread.getHandler(), flags); 
    } else { 
     throw new RuntimeException("Not supported in system context"); 
    } 
    try { 
     ... ... 
     return res != 0; 
    } catch (RemoteException e) { 
     return false; 
    } 
} 

Si deve sempre separare il servizio quando si è fatto con esso, come suggerito da the official dev guide, come un buon modo di programmazione:

  • Per scollegare dal servizio, chiama unbindService().

    Quando il cliente viene distrutto, non si separerà dal servizio, ma dovresti sempre separare quando hai finito di interagire con il servizio o quando l'attività si interrompe in modo che il servizio possa essere arrestato mentre non è in uso. (Tempi appropriati di legare e unbind è discusso più in basso.)

Il ServiceConnectionLeaked viene generato quando si avvia quadro l'esecuzione di una pulizia finale (per esempio, quando la vostra applicazione è uscire) e ha scoperto ci sono ServiceConnection non registrati, e il framework tenterà quindi di slegarlo per te. Vedere removeContextRegistrations() implementazione in android.app.LoadedApk:

public void removeContextRegistrations(Context context, 
     String who, String what) { 
    final boolean reportRegistrationLeaks = StrictMode.vmRegistrationLeaksEnabled(); 
    ... ... 
    //Slog.i(TAG, "Receiver registrations: " + mReceivers); 
    HashMap<ServiceConnection, LoadedApk.ServiceDispatcher> smap = 
     mServices.remove(context); 
    if (smap != null) { 
     Iterator<LoadedApk.ServiceDispatcher> it = smap.values().iterator(); 
     while (it.hasNext()) { 
      LoadedApk.ServiceDispatcher sd = it.next(); 
      ServiceConnectionLeaked leak = new ServiceConnectionLeaked(
        what + " " + who + " has leaked ServiceConnection " 
        + sd.getServiceConnection() + " that was originally bound here"); 
      leak.setStackTrace(sd.getLocation().getStackTrace()); 
      Slog.e(ActivityThread.TAG, leak.getMessage(), leak); 
      if (reportRegistrationLeaks) { 
       StrictMode.onServiceConnectionLeaked(leak); 
      } 
      try { 
       ActivityManagerNative.getDefault().unbindService(
         sd.getIServiceConnection()); 
      } catch (RemoteException e) { 
       // system crashed, nothing we can do 
      } 
      sd.doForget(); 
     } 
    } 
    mUnboundServices.remove(context); 
    //Slog.i(TAG, "Service registrations: " + mServices); 
} 
+0

Grazie per la risposta, ma non è ancora coerente con i campioni (ad esempio RemoteService in ApiDemos fa anche non associare solo se 'onServiceConnected()' è stato chiamato). Tuttavia, poiché con il tuo aiuto ora posso vedere che anche la descrizione di alto livello sembra consentire una tale interpretazione, accetterò la risposta. – Stephan

Problemi correlati