L'activité <App Name> a divulgué ServiceConnection <ServiceConnection Name> @ 438030a8 qui était à l'origine lié ici

134

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

Edit Merci BDLS pour votre réponse informative. J'ai réécrit mon code à la lumière de vos éclaircissements sur la différence entre l'utilisation enbindService()tant que fonction autonome ou aprèsstartService(), et je ne reçois maintenant le message d'erreur de fuite que par intermittence lorsque vous utilisez le bouton de retour pour passer d'une activité à l'autre.

Mon activité de connexion comprend les éléments suivants onCreate()et onDestroy():

/** 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é lorsque l'activité est démarrée et arrêté lorsque l'activité est détruite si aucune connexion telnet n'a été établie ( connectStatus == 0). Les autres activités se lient au service uniquement si une connexion réussie a été établie ( connectStatus == 1, enregistrée dans des préférences partagées). Voici leur 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 se produit onResume()afin qu'elle récupère l'état modifié de l'activité de connexion, et dans la onDestroy()fonction, elle est non liée, si nécessaire.

Terminer la modification

Mais je reçois toujours le message d'erreur de fuite de mémoire "L'activité a divulgué ServiceConnection @ 438030a8 qui était initialement lié ici" par intermittence lors du changement d'activité. Qu'est-ce que je fais mal?

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

Le message d'erreur complet suit (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

Modifiez le 2e
Merci encore une fois pour vos suggestions. J'ai fait ce que vous avez suggéré et j'ai ajouté unonUnBind()remplacement au service. onUnBind()n'est en fait déclenché que lorsque tous les clients se déconnectent du service, mais lorsque j'ai appuyé sur le bouton d'accueil, il s'est exécuté, puis le message d'erreur est apparu! Cela n'a aucun sens pour moi, car tous les clients ont été dissociés du service, alors comment celui qui a détruit pourrait-il fuir un serviceConnection? 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 être quelque chose comme vous l'avez dit, où la liaison au service n'est pas complète lors de l' unbindService()appel, mais j'ai essayé d'appeler une méthode sur le service lorsque j'ai parcouru chaque activité pour vérifier que la liaison est complète, et ils sont tous allés à travers très bien.

En général, ce comportement ne semble pas lié à la durée pendant laquelle je reste dans chaque activité. Une fois que la première activité perd son serviceConnection, cependant, ils font tous ce que je reviens par la suite.

Une autre chose, si j'active "Détruire immédiatement les activités" dans Dev Tools, cela empêche cette erreur.

Des idées?

catdotgif
la source
Pouvez-vous ajouter le code LightfactoryRemote.onCreate ()? (com.wingedvictorydesign.LightfactoryRemote.LightfactoryRemote.onCreate (LightfactoryRemote.java:97))
tbruyelle

Réponses:

57

Vous n'avez fourni aucun de votre code à partir de LightFactoryRemote, donc ce n'est qu'une présomption, mais cela ressemble au genre de problème que vous rencontreriez si vous utilisiez la bindServiceméthode seule.

Pour vous assurer qu'un service continue de fonctionner, même après que l'activité qui l'a démarré a eu sa onDestroyméthode appelée, vous devez d'abord utiliser startService.

La documentation Android pour l' état de startService :

L'utilisation de startService () remplace la durée de vie du service par défaut qui est gérée par bindService (Intent, ServiceConnection, int): elle nécessite que le service reste en cours d'exécution jusqu'à ce que stopService (Intent) soit appelé, que des clients y soient connectés ou non.

Alors que pour bindService :

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


Donc, ce qui s'est passé, c'est l'activité qui a lié (et donc démarré) le service, a été arrêtée et donc le système pense que le service n'est plus nécessaire et provoque cette erreur (puis arrête probablement le service).


Exemple

Dans cet exemple, le service doit continuer à fonctionner indépendamment du fait que l'activité d'appel soit 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é.

bdls
la source
Je démarre le service en utilisant START_STICKY et il est également lancé par l'intention de démarrage. Si j'appelle StartService, cela crée une autre instance du service et je ne veux pas que 2 services fonctionnent en même temps. Comment puis-je corriger l'erreur dans ce cas?
opc0de
16
@ opc0de, non, vous ne créez pas un autre service lorsque vous appelez startService () deux fois. Les services sont logiquement uniques par nature. Peu importe le nombre de fois que vous le démarrez, un seul service s'exécute.
mahkie
2
Quelle est la solution pour continuer le service en arrière-plan pour lecteur de musique?
Anand Savjani
45

Vous pouvez utiliser:

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

    if (mServiceConn != null) {
        unbindService(mServiceConn);
    }
}
Mostafa Rostami
la source
Excellent. C'est ce que c'est.
Seltsam
31

Vous vous liez onResumemais vous vous détachez onDestroy. Vous devriez faire la dissociation dans à la onPauseplace, afin qu'il y ait toujours des paires d'appels de liaison / détachement correspondantes. Vos erreurs intermittentes seront celles où votre activité est interrompue mais pas détruite, puis reprise à nouveau.

pseudo
la source
13

Vous ne devriez avoir qu'à dissocier le service dans onDestroy(). Ensuite, l'avertissement disparaîtra.

Regardez ici .

Comme le document d'activité essaie de l'expliquer, vous utiliserez trois principaux regroupements de liaison / détachement: onCreate () et onDestroy (), onStart () et onStop (), et onResume () et onPause ().

Pierrotlefou
la source
7
comme vous l'avez peut-être expérimenté, onDestroy n'est pratiquement jamais appelé à partir du système d'exploitation
martyglaubitz
Voir ici le lien vers le contenu interdit -> Avertissement relatif au contenu interdit. Quelqu'un peut-il y avoir accès? Le bouton "Continuer vers le groupe" actualise simplement la page et affiche le même avertissement pour moi.
Blaze Gawlik
11

Vous mentionnez que l'utilisateur passe d'une activité à l'autre assez rapidement. Se pourrait-il que vous appeliez unbindServiceavant que la connexion de service n'ait été établie? Cela peut avoir pour effet d'échouer à dissocier, puis de fuir la liaison.

Je ne sais pas exactement comment vous pourriez gérer cela ... Peut-être que quand onServiceConnectedest appelé, vous pouvez appeler unbindServicesi onDestroya déjà été appelé. Je ne sais pas si cela fonctionnera.


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 s'en détachent, et cela peut aider au débogage.

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

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 très bien.

Ashwini Shahapurkar
la source
1

Vous pouvez simplement le contrôler avec un booléen, donc vous n'appelez unbind que si la liaison a été effectuée

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 ne souhaitez le dissocier que 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;
    }
};
D4rWiNS
la source
0

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

Alors essayez d'utiliser

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

J'ai lu très récemment sur Android Service et j'ai eu l'occasion de m'y plonger en profondeur. J'ai rencontré une fuite de service, pour ma situation, cela s'est produit parce que j'avais un service non lié qui démarrait un service lié , mais dans ce cas, mon service non lié est remplacé par une activité .

Ainsi, lorsque j'arrêtais mon service non lié en utilisant stopSelf (), la fuite s'est produite, la raison était que j'arrêtais le service parent sans dissocier le service lié. Maintenant, le service lié est en cours d'exécution et il ne sait pas à qui il appartient.

La solution simple et directe est que vous devez appeler unbindService (YOUR_SERVICE); dans votre fonction onDestroy () de votre activité / service parent. De cette façon, le cycle de vie garantira que vos services liés sont arrêtés ou nettoyés avant que votre activité / services parent ne s'arrête.

Il existe une autre variante de ce problème. Parfois, dans votre service lié, vous souhaitez que certaines fonctions ne fonctionnent que si le service est lié, nous finissons donc par mettre un indicateur lié dans le onServiceConnected comme:

public void onServiceConnected(ComponentName name, IBinder service) {
            bounded = true;
            // code here
        }

Cela fonctionne bien jusqu'à ici, mais le problème survient lorsque nous traitons la fonction onServiceDisconnected comme un rappel pour l' appel de la fonction unbindService , cela par la documentation n'est appelé que lorsqu'un service est tué ou planté . Et vous n'obtiendrez jamais ce rappel dans le même fil . Par conséquent, nous finissons par faire quelque chose comme:

public void onServiceDisconnected(ComponentName name) {
            bounded = false;
        }

Ce qui crée un bogue majeur dans le code car notre drapeau lié n'est jamais remis à false et lorsque ce service est à nouveau connecté la plupart du temps, il l'est true. Donc, pour éviter ce scénario, vous devez définir le boundsur false au moment où vous appelez unbindService.

Ceci est couvert plus en détail dans le blog d'Erik .

Hope qui est jamais venu ici a satisfait sa curiosité.

Farhaan Bukhsh
la source
0

cette erreur se produit lorsque vous allez lier un service limité. donc, sol devrait être: -

  1. dans la connexion de service, ajoutez serviceBound comme ci-dessous:

    private final ServiceConnection serviceConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        // your work here.
    
        serviceBound = true;
    
    }
    
    @Override
    public void onServiceDisconnected(ComponentName name) {
    
        serviceBound = false;
    }

    };

  2. dissocier le service onDestroy

        if (serviceBound) {
            unbindService(serviceConnection);
        }
Atef Farouk
la source