web-dev-qa-db-fra.com

L'activité <Nom de l'application> a détecté une fuite ServiceConnection <Nom de la connexion>> 438030a8 qui était liée à l'origine ici

Je travaille sur ma première Android. J'ai trois activités dans mon application, et l'utilisateur alterne assez fréquemment. J'ai aussi un service à distance, qui gère une connexion telnet. Les applications doivent se connecter à ce service pour pouvoir envoyer/recevoir des messages telnet.

Edit Merci à BDLS pour votre réponse informative. J'ai réécrit mon code à la lumière de votre clarification concernant la différence entre utiliser bindService() en tant que fonction autonome ou après startService(), et je ne reçois maintenant que le message d'erreur de fuite par intermittence. lorsque vous utilisez le bouton Précédent pour passer d’une activité à l’autre.

Mon activité de connexion a les fonctions onCreate() et onDestroy() suivantes:

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    /*
     * Initialize the ServiceConnection.  Note that this is the only place startService() is run.
     * It is also the only time bindService is run without dependency on connectStatus.
     */
    conn = new TelnetServiceConnection();
    //start the service which handles telnet
    Intent i = new Intent();
    i.setClassName( "com.wingedvictorydesign.LightfactoryRemote", "com.wingedvictorydesign.LightfactoryRemote.TelnetService" );
    startService(i);
    //bind to the service
    bindService(i, conn, 0);

    setContentView(R.layout.connect);
    setupConnectUI();

}//end OnCreate()

@Override
protected void onDestroy() {
    super.onDestroy();

    //unbind the service and null it out
    if (conn != null) {
        unbindService(conn);
        conn = null;
        }

    if(connectStatus == 0) {
        //stop the service
        Intent i = new Intent();
        i.setClassName( "com.wingedvictorydesign.LightfactoryRemote", "com.wingedvictorydesign.LightfactoryRemote.TelnetService" );
        stopService(i);
        Log.d("LightfactoryRemote", "Connect onDestroy() attempted to stop service");
        }

    Log.d("LightfactoryRemote", "Connect onDestroy()");
    }//end onDestroy()

Ainsi, le service est démarré au démarrage de l'activité et arrêté lorsque l'activité est détruite si aucune connexion telnet n'a été établie avec succès (connectStatus == 0). Les autres activités ne se lient au service que si une connexion a été établie avec succès (connectStatus == 1, Enregistré dans une préférence partagée). Voici leurs onResume() et onDestroy():

@Override
protected void onResume() {
    super.onResume();

    //retrieve the shared preferences file, and grab the connectionStatus out of it.
    SharedPreferences settings = getSharedPreferences(PREFS_NAME, MODE_WORLD_WRITEABLE);
    connectStatus = settings.getInt("connectStatus", 0);

    Log.d("LightfactoryRemote", "Focus onResume with " + connectStatus);

    //if a telnet connection is active, start the service and bind to it
    if (connectStatus == 1) {
        conn = new TelnetServiceConnection();
        Intent i = new Intent();
        i.setClassName("com.wingedvictorydesign.LightfactoryRemote", "com.wingedvictorydesign.LightfactoryRemote.TelnetService");
        bindService(i, conn, 0);
        //TODO write restore texview code
        }//end if
    }//end onResume

@Override
protected void onDestroy() {
    super.onDestroy();
    //unbind the service and null it out.
    if (conn != null) {
        Log.d("LightfactoryRemote", "Focus onDestroy() attempted to unbind service");
        unbindService(conn);
        conn = null;
        }
    Log.d("LightfactoryRemote", "Focus onDestroy()");
    }//end onDestroy()

Ainsi, la liaison a lieu dans onResume() de sorte qu'elle récupère l'état modifié à partir de l'activité de connexion, et dans la fonction onDestroy(), elle est non liée, si nécessaire.

Fin éditer

Toutefois, le message d'erreur relatif à une fuite de mémoire "L'activité a généré une fuite de ServiceConnection @ 438030a8 initialement liée ici" par intermittence lors du changement d'activité. Qu'est-ce que je fais mal?

Merci d'avance pour tous conseils ou astuces !!!

Le message d'erreur complet suit (à partir du code révisé):

01-02 22:04:26.642: DEBUG/LightfactoryRemote(2024): Focus onStop()
01-02 22:04:26.642: DEBUG/LightfactoryRemote(2024): Focus onDestroy() attempted to unbind service
01-02 22:04:26.642: DEBUG/LightfactoryRemote(2024): Focus onDestroy()
01-02 22:04:26.672: ERROR/ActivityThread(2024): Activity com.wingedvictorydesign.LightfactoryRemote.LightfactoryRemote has leaked ServiceConnection com.wingedvictorydesign.LightfactoryRemote.LightfactoryRemote$TelnetServiceConnection@439e51e8 that was originally bound here
01-02 22:04:26.672: ERROR/ActivityThread(2024): Android.app.ServiceConnectionLeaked: Activity com.wingedvictorydesign.LightfactoryRemote.LightfactoryRemote has leaked ServiceConnection com.wingedvictorydesign.LightfactoryRemote.LightfactoryRemote$TelnetServiceConnection@439e51e8 that was originally bound here
01-02 22:04:26.672: ERROR/ActivityThread(2024):     at Android.app.ActivityThread$PackageInfo$ServiceDispatcher.<init>(ActivityThread.Java:927)
01-02 22:04:26.672: ERROR/ActivityThread(2024):     at Android.app.ActivityThread$PackageInfo.getServiceDispatcher(ActivityThread.Java:822)
01-02 22:04:26.672: ERROR/ActivityThread(2024):     at Android.app.ApplicationContext.bindService(ApplicationContext.Java:842)
01-02 22:04:26.672: ERROR/ActivityThread(2024):     at Android.content.ContextWrapper.bindService(ContextWrapper.Java:319)
01-02 22:04:26.672: ERROR/ActivityThread(2024):     at com.wingedvictorydesign.LightfactoryRemote.LightfactoryRemote.onResume(LightfactoryRemote.Java:102)
01-02 22:04:26.672: ERROR/ActivityThread(2024):     at Android.app.Instrumentation.callActivityOnResume(Instrumentation.Java:1225)
01-02 22:04:26.672: ERROR/ActivityThread(2024):     at Android.app.Activity.performResume(Activity.Java:3559)
01-02 22:04:26.672: ERROR/ActivityThread(2024):     at Android.app.ActivityThread.performResumeActivity(ActivityThread.Java:2838)
01-02 22:04:26.672: ERROR/ActivityThread(2024):     at Android.app.ActivityThread.handleResumeActivity(ActivityThread.Java:2866)
01-02 22:04:26.672: ERROR/ActivityThread(2024):     at Android.app.ActivityThread.handleLaunchActivity(ActivityThread.Java:2420)
01-02 22:04:26.672: ERROR/ActivityThread(2024):     at Android.app.ActivityThread.access$2100(ActivityThread.Java:116)
01-02 22:04:26.672: ERROR/ActivityThread(2024):     at Android.app.ActivityThread$H.handleMessage(ActivityThread.Java:1794)
01-02 22:04:26.672: ERROR/ActivityThread(2024):     at Android.os.Handler.dispatchMessage(Handler.Java:99)
01-02 22:04:26.672: ERROR/ActivityThread(2024):     at Android.os.Looper.loop(Looper.Java:123)
01-02 22:04:26.672: ERROR/ActivityThread(2024):     at Android.app.ActivityThread.main(ActivityThread.Java:4203)
01-02 22:04:26.672: ERROR/ActivityThread(2024):     at Java.lang.reflect.Method.invokeNative(Native Method)
01-02 22:04:26.672: ERROR/ActivityThread(2024):     at Java.lang.reflect.Method.invoke(Method.Java:521)
01-02 22:04:26.672: ERROR/ActivityThread(2024):     at com.Android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.Java:791)
01-02 22:04:26.672: ERROR/ActivityThread(2024):     at com.Android.internal.os.ZygoteInit.main(ZygoteInit.Java:549)
01-02 22:04:26.672: ERROR/ActivityThread(2024):     at dalvik.system.NativeStart.main(Native Method)
01-02 22:04:26.692: WARN/ActivityManager(558): Unbind failed: could not find connection for Android.os.BinderProxy@43c509a8

Éditez le 2e
Merci encore une fois bdls pour vos suggestions. J'ai fait ce que vous avez suggéré et ajouté un onUnBind() au service. onUnBind() n'est en réalité déclenché que lorsque tous les clients sont déconnectés du service, mais lorsque je clique sur le bouton d'accueil, il s'exécute, le message d'erreur s'affiche! Cela n’a aucun sens pour moi, car tous les clients ne sont pas liés au service. Comment un client détruit peut-il laisser échapper un serviceConnexion? Vérifiez-le:

01-03 19:38:30.837: DEBUG/LightfactoryRemote(1118): Focus onPause()1
01-03 19:38:31.577: WARN/IInputConnectionWrapper(1118): showStatusIcon on inactive InputConnection
01-03 19:38:31.587: DEBUG/LightfactoryRemote(1118): Focus onStop()
01-03 19:38:31.600: DEBUG/LightfactoryRemote(1118): Focus onDestroy() attempted to unbind service
01-03 19:38:31.607: DEBUG/LightfactoryRemote(1118): Focus onDestroy()
01-03 19:38:31.677: DEBUG/LightfactoryRemote(1125): TelnetService onUnBind()
01-03 19:38:31.727: ERROR/ActivityThread(1118): Activity com.wingedvictorydesign.LightfactoryRemote.LightfactoryRemote has leaked ServiceConnection com.wingedvictorydesign.LightfactoryRemote.LightfactoryRemote$TelnetServiceConnection@435baeb0 that was originally bound here
01-03 19:38:31.727: ERROR/ActivityThread(1118): Android.app.ServiceConnectionLeaked: Activity com.wingedvictorydesign.LightfactoryRemote.LightfactoryRemote has leaked ServiceConnection com.wingedvictorydesign.LightfactoryRemote.LightfactoryRemote$TelnetServiceConnection@435baeb0 that was originally bound here
01-03 19:38:31.727: ERROR/ActivityThread(1118):     at Android.app.ActivityThread$PackageInfo$ServiceDispatcher.<init>(ActivityThread.Java:886)
01-03 19:38:31.727: ERROR/ActivityThread(1118):     at Android.app.ActivityThread$PackageInfo.getServiceDispatcher(ActivityThread.Java:781)
01-03 19:38:31.727: ERROR/ActivityThread(1118):     at Android.app.ApplicationContext.bindService(ApplicationContext.Java:820)
01-03 19:38:31.727: ERROR/ActivityThread(1118):     at Android.content.ContextWrapper.bindService(ContextWrapper.Java:307)
01-03 19:38:31.727: ERROR/ActivityThread(1118):     at com.wingedvictorydesign.LightfactoryRemote.LightfactoryRemote.onResume(LightfactoryRemote.Java:102)
01-03 19:38:31.727: ERROR/ActivityThread(1118):     at Android.app.Instrumentation.callActivityOnResume(Instrumentation.Java:1225)
01-03 19:38:31.727: ERROR/ActivityThread(1118):     at Android.app.Activity.performResume(Activity.Java:3530)
01-03 19:38:31.727: ERROR/ActivityThread(1118):     at Android.app.ActivityThread.performResumeActivity(ActivityThread.Java:2619)
01-03 19:38:31.727: ERROR/ActivityThread(1118):     at Android.app.ActivityThread.handleResumeActivity(ActivityThread.Java:2647)
01-03 19:38:31.727: ERROR/ActivityThread(1118):     at Android.app.ActivityThread.handleLaunchActivity(ActivityThread.Java:2287)
01-03 19:38:31.727: ERROR/ActivityThread(1118):     at Android.app.ActivityThread.access$1800(ActivityThread.Java:112)
01-03 19:38:31.727: ERROR/ActivityThread(1118):     at Android.app.ActivityThread$H.handleMessage(ActivityThread.Java:1692)
01-03 19:38:31.727: ERROR/ActivityThread(1118):     at Android.os.Handler.dispatchMessage(Handler.Java:99)
01-03 19:38:31.727: ERROR/ActivityThread(1118):     at Android.os.Looper.loop(Looper.Java:123)
01-03 19:38:31.727: ERROR/ActivityThread(1118):     at Android.app.ActivityThread.main(ActivityThread.Java:3948)
01-03 19:38:31.727: ERROR/ActivityThread(1118):     at Java.lang.reflect.Method.invokeNative(Native Method)
01-03 19:38:31.727: ERROR/ActivityThread(1118):     at Java.lang.reflect.Method.invoke(Method.Java:521)
01-03 19:38:31.727: ERROR/ActivityThread(1118):     at com.Android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.Java:782)
01-03 19:38:31.727: ERROR/ActivityThread(1118):     at com.Android.internal.os.ZygoteInit.main(ZygoteInit.Java:540)
01-03 19:38:31.727: ERROR/ActivityThread(1118):     at dalvik.system.NativeStart.main(Native Method)
01-03 19:38:31.777: WARN/ActivityManager(564): Unbind failed: could not find connection for Android.os.BinderProxy@4370f8a8

Je pensais que cela pourrait ressembler à quelque chose comme vous l'avez dit, où la liaison au service n'est pas complète lorsque unbindService() est appelé, mais j'ai essayé d'appeler une méthode sur le service tout en appuyant chaque activité pour vérifier que la liaison est complet, et ils ont tous passé très bien.

En général, ce comportement ne semble pas lié à la durée de mon activité dans chaque activité. Une fois que la première activité a perdu sa serviceConnexion, ils le font tous comme je les ai parcourus par la suite.

Une autre chose, si j'active "Détruire immédiatement les activités" dans Outils de développement, cela évite cette erreur.

Des idées?

122
catdotgif

Vous n'avez fourni aucun de vos codes à partir de LightFactoryRemote, ce n'est donc qu'une présomption, mais cela ressemble au genre de problème que vous verriez si vous utilisiez la méthode bindService sur sa propre.

Pour que le service continue à fonctionner, même après le lancement de la méthode onDestroy de l'activité démarrée, vous devez d'abord utiliser startService.

Le Android docs pour startService état:

L'utilisation de startService () annule la durée de vie du service par défaut gérée par bindService (Intent, ServiceConnection, int): le service doit rester actif jusqu'à ce que stopService (Intent) soit appelé, que des clients lui soient connectés ou non.

Attendu que pour bindService :

Le service ne sera considéré comme requis par le système que tant que le contexte de l'appel existe. Par exemple, si ce contexte est une activité arrêtée, le service ne sera pas obligé de continuer à s'exécuter jusqu'à la reprise de l'activité.


Il s’agit donc de l’activité qui a lié (et donc démarré) le service, a été arrêtée et le système pense donc que le service n’est plus requis et provoque cette erreur (puis arrête probablement le service).


Exemple

Dans cet exemple, le service doit continuer à fonctionner, que l'activité appelante soit ou non en cours d'exécution.

ComponentName myService = startService(new Intent(this, myClass.class));
bindService(new Intent(this, myClass.class), myServiceConn, BIND_AUTO_CREATE);

La première ligne démarre le service et la seconde le lie à l'activité.

54
bdls

Vous pouvez utiliser:

@Override
public void onDestroy() {
    super.onDestroy();

    if (mServiceConn != null) {
        unbindService(mServiceConn);
    }
}
38
Mostafa Rostami

Vous liez dans onResume mais dissociez dans onDestroy. Vous devez plutôt annuler la liaison dans onPause, afin qu'il y ait toujours des paires d'appels bind/unbind identiques. Vos erreurs intermittentes seront celles où votre activité est suspendue mais non détruite, puis reprise à nouveau.

30
Nick

Vous ne devriez avoir besoin que de dissocier le service dans onDestroy(). Ensuite, l'avertissement ira.

Voir ici .

Comme l'explique la doc d'activité, vous utiliserez trois groupes principaux bind/unbind: onCreate () et onDestroy (), onStart () et onStop (), et onResume () et onPause ().

13
pierrotlefou

Vous dites que l'utilisateur bascule rapidement entre les activités. Se pourrait-il que vous appeliez unbindService avant que la connexion au service ait été établie? Cela peut avoir pour effet de ne pas dissocier, puis de fuir la liaison.

Vous ne savez pas trop comment vous pouvez gérer cela ... Peut-être que lorsque onServiceConnected sera appelé, vous pourrez appeler unbindService si onDestroy a déjà été appelé. Je ne sais pas si ça marchera bien.


Si vous ne l'avez pas déjà fait, vous pouvez ajouter une méthode onUnbind à votre service. De cette façon, vous pouvez voir exactement quand vos classes se dissocient et cela pourrait aider au débogage.

@Override
public boolean onUnbind(Intent intent) {
    Log.d(this.getClass().getName(), "UNBIND");
    return true;
}
9
bdls

Essayez d’utiliser unbindService () dans OnUserLeaveHint (). Il empêche le scénario de fuite de ServiceConnection et d'autres exceptions.
Je l'ai utilisé dans mon code et fonctionne bien.

2

Vous pouvez simplement le contrôler avec un booléen, de sorte que vous appelez unsbind si la liaison a été faite

public void doBindService()
{
    if (!mIsBound)
    {
        bindService(new Intent(this, DMusic.class), Scon, Context.BIND_AUTO_CREATE);
        mIsBound = true;
    }
}

public void doUnbindService()
{
    if (mIsBound)
    {
        unbindService(Scon);
        mIsBound = false;
    }
}

Si vous voulez seulement le dissocier s'il a été connecté

public ServiceConnection Scon = new ServiceConnection() {

    public void onServiceConnected(ComponentName name, IBinder binder)
    {
        mServ = ((DMusic.ServiceBinder) binder).getService();
        mIsBound = true;
    }

    public void onServiceDisconnected(ComponentName name)
    {
        mServ = null;
    }
};
1
D4rWiNS

Chaque service lié à une activité doit être dissocié à la fermeture de l'application.

Alors essayez d'utiliser

 onPause(){
   unbindService(YOUR_SERVICE);
   super.onPause();
 }
0
Punit