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?
la source
Réponses:
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 labindService
mé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
onDestroy
méthode appelée, vous devez d'abord utiliserstartService
.La documentation Android pour l' état de startService :
Alors que pour bindService :
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.
La première ligne démarre le service et la seconde le lie à l'activité.
la source
Vous pouvez utiliser:
la source
Vous vous liez
onResume
mais vous vous détachezonDestroy
. Vous devriez faire la dissociation dans à laonPause
place, 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.la source
Vous ne devriez avoir qu'à dissocier le service dans
onDestroy()
. Ensuite, l'avertissement disparaîtra.Regardez ici .
la source
Vous mentionnez que l'utilisateur passe d'une activité à l'autre assez rapidement. Se pourrait-il que vous appeliez
unbindService
avant 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
onServiceConnected
est appelé, vous pouvez appelerunbindService
sionDestroy
a 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.
la source
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.
la source
Vous pouvez simplement le contrôler avec un booléen, donc vous n'appelez unbind que si la liaison a été effectuée
Si vous ne souhaitez le dissocier que s'il a été connecté
la source
Chaque service lié à l'activité doit être dissocié à la fermeture de l'application.
Alors essayez d'utiliser
la source
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:
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:
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 lebound
sur false au moment où vous appelezunbindService
.Ceci est couvert plus en détail dans le blog d'Erik .
Hope qui est jamais venu ici a satisfait sa curiosité.
la source
cette erreur se produit lorsque vous allez lier un service limité. donc, sol devrait être: -
dans la connexion de service, ajoutez serviceBound comme ci-dessous:
};
dissocier le service onDestroy
la source