Die Aktivität <Anwendungsname> hat ServiceConnection <ServiceConnection-Name> @ 438030a8 durchgesickert, die ursprünglich hier gebunden war

134

Ich arbeite an meiner ersten Android App. Ich habe drei Aktivitäten in meiner App und der Benutzer wechselt ziemlich häufig hin und her. Ich habe auch einen Remote-Dienst, der eine Telnet-Verbindung verwaltet. Die Apps müssen sich an diesen Dienst binden, um Telnet-Nachrichten senden / empfangen zu können.

Bearbeiten Vielen Dank an BDLS für Ihre informative Antwort. Ich habe meinen Code im Lichte Ihrer Klarstellung über den Unterschied zwischen der VerwendungbindService()als eigenständige Funktion oder danach neu geschriebenstartService()und erhalte jetzt nur zeitweise die Leckfehlermeldung, wenn Sie die Zurück-Taste zum Wechseln zwischen Aktivitäten verwenden.

Meine Verbindungsaktivität hat Folgendes onCreate()und 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()

Der Dienst wird also gestartet, wenn die Aktivität gestartet wird, und gestoppt, wenn die Aktivität zerstört wird, wenn keine erfolgreiche Telnet-Verbindung hergestellt wurde ( connectStatus == 0). Die anderen Aktivitäten sind nur dann an den Dienst gebunden, wenn eine erfolgreiche Verbindung hergestellt wurde ( connectStatus == 1gespeichert in gemeinsamen Einstellungen). Hier ist ihr onResume()und 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()

Die Bindung erfolgt also onResume()so, dass der geänderte Status aus der Verbindungsaktivität übernommen wird, und in der onDestroy()Funktion ist sie gegebenenfalls ungebunden.

Bearbeiten beenden

Ich erhalte jedoch immer noch die Fehlermeldung "Speicherverlust" "Aktivität hat ServiceConnection @ 438030a8 durchgesickert, die ursprünglich hier gebunden war", wenn zeitweise Aktivitäten gewechselt werden. Was mache ich falsch?

Vielen Dank im Voraus für alle Tipps oder Hinweise !!!

Es folgt die vollständige Fehlermeldung (aus dem überarbeiteten Code):

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

Bearbeiten Sie die 2.
Danke noch einmal bdls für Ihre Vorschläge. Ich habe getan, was Sie vorgeschlagen haben, undonUnBind()dem ServiceeineÜberschreibunghinzugefügt. onUnBind()wird eigentlich nur ausgelöst, wenn alle Clients die Verbindung zum Dienst trennen, aber wenn ich die Home-Taste drücke, wird sie ausgeführt, und die Fehlermeldung wird angezeigt! Dies macht für mich keinen Sinn, da alle Clients nicht an den Service gebunden waren. Wie könnte der zerstörte also eine serviceConnection auslaufen lassen? Hör zu:

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

Ich dachte, es könnte so etwas wie das sein, was Sie gesagt haben, bei dem die Bindung an den Dienst nicht vollständig unbindService()ist, wenn es aufgerufen wird. Ich habe jedoch versucht, eine Methode für den Dienst aufzurufen, während ich jede Aktivität durchgesehen habe, um zu überprüfen, ob die Bindung vollständig ist, und alle gingen durch gut.

Im Allgemeinen scheint dieses Verhalten nicht damit zu tun zu haben, wie lange ich in jeder Aktivität bleibe. Sobald die erste Aktivität ihre serviceConnection verliert, tun sie alle, was ich danach durch sie zurück gehe.

Eine andere Sache, wenn ich in Dev Tools "Aktivitäten sofort zerstören" aktiviere, wird dieser Fehler verhindert.

Irgendwelche Ideen?

catdotgif
quelle
Können Sie den Code LightfactoryRemote.onCreate () hinzufügen? (com.wingedvictorydesign.LightfactoryRemote.LightfactoryRemote.onCreate (LightfactoryRemote.java:97))
tbruyelle

Antworten:

57

Sie haben keinen Code von bereitgestellt LightFactoryRemote, daher ist dies nur eine Vermutung, aber es sieht nach dem Problem aus, das Sie sehen würden, wenn Sie die bindServiceMethode alleine verwenden würden.

Um sicherzustellen, dass ein Dienst auch nach dem Aufrufen der onDestroyMethode, die gestartet wurde, weiter ausgeführt wird , sollten Sie ihn zuerst verwenden startService.

Die Android-Dokumente für den StartService- Status:

Die Verwendung von startService () überschreibt die von bindService (Intent, ServiceConnection, int) verwaltete Standarddienstlebensdauer: Der Dienst muss so lange ausgeführt werden, bis stopService (Intent) aufgerufen wird, unabhängig davon, ob Clients mit ihm verbunden sind.

Für bindService gilt Folgendes :

Der Dienst wird vom System nur so lange als erforderlich angesehen, wie der aufrufende Kontext vorhanden ist. Wenn es sich bei diesem Kontext beispielsweise um eine Aktivität handelt, die gestoppt wird, muss der Dienst erst fortgesetzt werden, wenn die Aktivität wieder aufgenommen wird.


Was also passiert ist, ist die Aktivität, die den Dienst gebunden (und daher gestartet) hat, gestoppt wurde, und daher glaubt das System, dass der Dienst nicht mehr benötigt wird, und verursacht diesen Fehler (und stoppt dann wahrscheinlich den Dienst).


Beispiel

In diesem Beispiel sollte der Dienst weiterhin ausgeführt werden, unabhängig davon, ob die aufrufende Aktivität ausgeführt wird.

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

Die erste Zeile startet den Dienst und die zweite bindet ihn an die Aktivität.

bdls
quelle
Ich starte den Dienst mit START_STICKY und er wird auch durch die Startabsicht gestartet. Wenn ich StartService aufrufe, wird eine weitere Instanz des Dienstes erstellt, und ich möchte nicht, dass zwei Dienste gleichzeitig ausgeführt werden. Wie kann ich den Fehler in diesem Fall beheben?
opc0de
16
@ opc0de, nein, Sie erstellen keinen weiteren Dienst, wenn Sie startService () zweimal aufrufen. Services sind von Natur aus logisch Singletons. Unabhängig davon, wie oft Sie es starten, wird nur ein Dienst ausgeführt.
Mahkie
2
Was ist die Lösung für die Fortsetzung des Dienstes vor dem Hintergrund eines Musik-Players?
Anand Savjani
45

Sie können verwenden:

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

    if (mServiceConn != null) {
        unbindService(mServiceConn);
    }
}
Mostafa Rostami
quelle
Ausgezeichnet. Das ist es.
Seltsam
31

Sie binden sich ein onResume, lösen sich aber auf onDestroy. Sie sollten onPausestattdessen das Aufheben der Bindung durchführen, damit immer übereinstimmende Paare von Bindungs- / Entbindungsaufrufen vorhanden sind. Bei Ihren zeitweiligen Fehlern wird Ihre Aktivität angehalten, aber nicht zerstört und dann wieder aufgenommen.

Nick
quelle
13

Sie sollten nur den Dienst in binden müssen onDestroy(). Dann geht die Warnung.

Siehe hier .

Wie das Aktivitätsdokument zu erklären versucht, gibt es drei Hauptgruppen zum Binden / Entbinden, die Sie verwenden werden: onCreate () und onDestroy (), onStart () und onStop () sowie onResume () und onPause ().

pierrotlefou
quelle
7
Wie Sie vielleicht schon gesehen haben, wird Destroy vom Betriebssystem so gut wie nie aufgerufen
Martyglaubitz
Siehe hier Link weiter zu verbotenen Inhalten -> Warnung vor verbotenen Inhalten. Kann jemand Zugang bekommen? Die Schaltfläche "Weiter zur Gruppe" aktualisiert nur die Seite und zeigt die gleiche Warnung für mich an.
Blaze Gawlik
11

Sie erwähnen, dass der Benutzer ziemlich schnell zwischen Aktivitäten wechselt. Könnte es sein, dass Sie anrufen, unbindServicebevor die Dienstverbindung hergestellt wurde? Dies kann dazu führen, dass die Bindung nicht gelöst und die Bindung verloren geht.

Nicht ganz sicher, wie Sie damit umgehen könnten ... Wenn onServiceConnectedSie angerufen werden, können Sie vielleicht anrufen, unbindServicewenn onDestroybereits angerufen wurde. Ich bin mir nicht sicher, ob das funktionieren wird.


Wenn Sie dies noch nicht getan haben, können Sie Ihrem Dienst eine onUnbind-Methode hinzufügen. Auf diese Weise können Sie genau sehen, wann sich Ihre Klassen davon lösen, und dies kann beim Debuggen hilfreich sein.

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

Versuchen Sie, unbindService () in OnUserLeaveHint () zu verwenden. Es verhindert das durchgesickerte ServiceConnection-Szenario und andere Ausnahmen.
Ich habe es in meinem Code verwendet und funktioniert gut.

Ashwini Shahapurkar
quelle
1

Sie können es einfach mit einem Booleschen Wert steuern, sodass Sie Unbind nur aufrufen, wenn eine Bindung hergestellt wurde

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

Wenn Sie es nur lösen möchten, wenn es verbunden wurde

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

Jeder Dienst, der an Aktivitäten gebunden ist, muss beim Schließen der App aufgehoben werden.

Also versuchen Sie es mit

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

Ich habe vor kurzem über Android Service gelesen und hatte die Gelegenheit, mich eingehend damit zu beschäftigen. Ich bin auf ein Dienstleck gestoßen. In meiner Situation war dies darauf zurückzuführen, dass ich einen ungebundenen Dienst hatte, der einen gebundenen Dienst startete. In diesem Fall wird mein ungebundener Dienst durch eine Aktivität ersetzt .

Als ich meinen ungebundenen Dienst mithilfe von stopSelf () stoppte, trat das Leck auf. Der Grund dafür war, dass ich den übergeordneten Dienst stoppte , ohne den gebundenen Dienst zu lösen . Jetzt wird der gebundene Dienst ausgeführt und er weiß nicht, wem er gehört.

Die einfache und unkomplizierte Lösung besteht darin, dass Sie unbindService (YOUR_SERVICE) aufrufen sollten. in Ihrer onDestroy () -Funktion Ihrer übergeordneten Aktivität / Ihres übergeordneten Dienstes. Auf diese Weise stellt der Lebenszyklus sicher, dass Ihre gebundenen Dienste gestoppt oder bereinigt werden, bevor Ihre übergeordneten Aktivitäten / Dienste ausfallen.

Es gibt noch eine andere Variante dieses Problems. Manchmal möchten Sie, dass in Ihrem gebundenen Dienst bestimmte Funktionen nur funktionieren, wenn der Dienst gebunden ist, sodass wir am Ende ein gebundenes Flag in onServiceConnected setzen, wie:

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

Dies funktioniert bis hierher einwandfrei, aber das Problem tritt auf, wenn wir die Funktion onServiceDisconnected als Rückruf für den Funktionsaufruf unbindService behandeln. Dies wird laut Dokumentation nur aufgerufen, wenn ein Dienst beendet oder abgestürzt ist . Und Sie werden diesen Rückruf niemals im selben Thread erhalten . Daher machen wir am Ende so etwas wie:

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

Dies führt zu einem schwerwiegenden Fehler im Code, da unser gebundenes Flag niemals auf false zurückgesetzt wird und wenn dieser Dienst meistens wieder verbunden wird true. Um dieses Szenario zu vermeiden, sollten Sie den boundWert in dem Moment, in dem Sie anrufen, auf false setzen unbindService.

Dies wird ausführlicher in Eriks Blog behandelt .

Die Hoffnung, die jemals hierher gekommen ist, hat seine Neugier befriedigt.

Farhaan Bukhsh
quelle
0

Dieser Fehler tritt auf, wenn Sie einen begrenzten Dienst binden. Sol sollte also sein: -

  1. Fügen Sie in der Serviceverbindung serviceBound wie folgt hinzu:

    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. Service aufDestroy aufheben

        if (serviceBound) {
            unbindService(serviceConnection);
        }
Atef Farouk
quelle