Działanie <nazwa aplikacji> wyciekło ServiceConnection <ServiceConnection Name> @ 438030a8, który został pierwotnie powiązany w tym miejscu

135

Pracuję nad moją pierwszą aplikacją na Androida. W mojej aplikacji są trzy działania, a użytkownik dość często przełącza się między nimi. Mam również usługę zdalną, która obsługuje połączenie telnet. Aplikacje muszą łączyć się z tą usługą, aby wysyłać / odbierać wiadomości telnet.

Edytuj Dziękuję BDLS za wyczerpującą odpowiedź. Ponownie napisałem swój kod w świetle twojego wyjaśnienia dotyczącego różnicy między używaniembindService()jako samodzielnej funkcji lub późniejstartService(), a teraz otrzymuję komunikat o błędzie wycieku tylko sporadycznie, gdy używam przycisku Wstecz do przełączania między czynnościami.

Moja aktywność związana z połączeniem obejmuje następujące onCreate()i 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()

Tak więc usługa jest uruchamiana, gdy aktywność jest uruchamiana, i zatrzymywana, gdy aktywność jest niszczona, jeśli nie zostało nawiązane połączenie telnet ( connectStatus == 0). Pozostałe działania wiążą się z usługą tylko w przypadku pomyślnego nawiązania połączenia ( connectStatus == 1zapisanego we wspólnych preferencjach). Oto ich onResume()i 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()

Tak więc wiązanie zachodzi w onResume()taki sposób, że odbierze zmieniony stan z aktywności połączenia, aw onDestroy()funkcji jest niezwiązane, jeśli to konieczne.

Zakończ edycję

Ale nadal pojawia się komunikat o błędzie wycieku pamięci „Wyciekła aktywność ServiceConnection @ 438030a8, która została pierwotnie powiązana tutaj” sporadycznie podczas przełączania działań. Co ja robię źle?

Z góry dziękuję za wszelkie wskazówki i wskazówki !!!

Pełny komunikat o błędzie (ze zmienionego kodu):

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

Edytuj drugie
dzięki jeszcze raz bdl za sugestie. Zrobiłem tak, jak zasugerowałeś i dodałemonUnBind()zastąpienie do usługi. onUnBind()jest w rzeczywistości wyzwalany tylko wtedy, gdy wszyscy klienci odłączają się od usługi, ale kiedy nacisnąłem przycisk home, wykonał on, a następnie pojawił się komunikat o błędzie! Nie ma to dla mnie sensu, ponieważ wszyscy klienci zostali odłączeni od usługi, więc w jaki sposób jeden zniszczył wyciek serviceConnection? Sprawdź to:

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

Pomyślałem, że może to być coś takiego, jak powiedziałeś, gdzie powiązanie z usługą nie jest kompletne po unbindService()wywołaniu, jednak próbowałem wywołać metodę w usłudze, gdy cofałem się przez każde działanie, aby sprawdzić, czy powiązanie jest kompletne, i wszystkie poszły w porządku.

Ogólnie rzecz biorąc, to zachowanie nie wydaje się być związane z tym, jak długo pozostaję w każdej czynności. Jednak gdy pierwsza aktywność wycieknie ze swojego serviceConnection, wszyscy robią to, co ja przez nie później.

Jeszcze jedna rzecz, jeśli włączę opcję „Natychmiast niszcz działania” w Narzędziach dla programistów, zapobiega to temu błędowi.

Jakieś pomysły?

catdotgif
źródło
Czy możesz dodać kod LightfactoryRemote.onCreate ()? (com.wingedvictorydesign.LightfactoryRemote.LightfactoryRemote.onCreate (LightfactoryRemote.java:97))
tbruyelle

Odpowiedzi:

57

Nie dostarczyłeś żadnego kodu z LightFactoryRemote, więc jest to tylko domniemanie, ale wygląda na to, że masz problem, gdybyś używał samej bindServicemetody.

Aby upewnić się, że usługa działa, nawet po onDestroywywołaniu metody działania, które ją uruchomiło , należy najpierw użyć startService.

Dokumentacja systemu Android dla stanu startService :

Użycie startService () przesłania domyślny okres istnienia usługi, który jest zarządzany przez bindService (Intent, ServiceConnection, int): wymaga, aby usługa działała do momentu wywołania stopService (Intent), niezależnie od tego, czy jacyś klienci są z nią połączeni.

Natomiast w przypadku bindService :

Usługa będzie uznawana za wymaganą przez system tylko tak długo, jak istnieje kontekst wywołujący. Na przykład jeśli ten kontekst jest działaniem, które zostało zatrzymane, usługa nie będzie musiała kontynuować działania do momentu wznowienia działania.


Więc to, co się wydarzyło, to aktywność, która związała (a tym samym uruchomiła) usługę, została zatrzymana, a zatem system uważa, że ​​usługa nie jest już wymagana i powoduje ten błąd (i prawdopodobnie zatrzymuje usługę).


Przykład

W tym przykładzie usługa powinna działać niezależnie od tego, czy uruchomiona jest aktywność wywołująca.

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

Pierwsza linia uruchamia usługę, a druga wiąże ją z działaniem.

bdls
źródło
Uruchamiam usługę za pomocą START_STICKY i jest ona również uruchamiana przez intencję rozruchu. Jeśli wywołuję StartService, tworzy to kolejne wystąpienie usługi i nie chcę, aby 2 usługi działały w tym samym czasie. Jak mogę naprawić błąd w tym przypadku?
opc0de
17
@ opc0de, nie, nie tworzysz innej usługi, gdy dwukrotnie wywołujesz startService (). Usługi są logicznie pojedyncze z natury. Bez względu na to, ile razy ją uruchomisz, działa tylko jedna usługa.
mahkie
2
Jakie jest rozwiązanie dla kontynuowania usługi w tle dla odtwarzacza muzyki?
Anand Savjani,
46

Możesz użyć:

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

    if (mServiceConn != null) {
        unbindService(mServiceConn);
    }
}
Mostafa Rostami
źródło
Świetny. To jest to.
Seltsam
31

Zwiążesz się onResumejednak w unbind onDestroy. Zamiast tego powinieneś wykonać unbinding in onPause, aby zawsze istniały pasujące pary wywołań bind / unbind. Twoje sporadyczne błędy będą powodowały wstrzymanie Twojej aktywności, ale nie jej zniszczenie, a następnie ponowne jej wznowienie.

Nacięcie
źródło
13

Powinieneś tylko odpiąć usługę w onDestroy(). Wtedy ostrzeżenie przejdzie.

Zobacz tutaj .

Jak wyjaśnia dokument Activity, istnieją trzy główne grupy bind / unbind, których będziesz używać: onCreate () i onDestroy (), onStart () i onStop () oraz onResume () i onPause ().

pierrotlefou
źródło
7
jak można się było przekonać, Destroy prawie nigdy nie jest wywoływany z systemu operacyjnego
martyglaubitz
Tutaj link prowadzi do zabronionych treści -> Ostrzeżenie o zakazanej treści. Czy każdy może uzyskać dostęp? Przycisk „Przejdź do grupy” tylko odświeża stronę i wyświetla to samo ostrzeżenie.
Blaze Gawlik
11

Wspomniałeś, że użytkownik dość szybko przełącza się między działaniami. Czy to możliwe, że dzwonisz, unbindServicezanim połączenie serwisowe zostało nawiązane? Może to skutkować niepowodzeniem rozpinania, a następnie wyciekiem wiązania.

Nie do końca jestem pewien, jak sobie z tym poradzisz ... Być może, kiedy onServiceConnectedktoś dzwoni, możesz zadzwonić, unbindServicejeśli onDestroyjuż zostałeś wezwany. Nie jestem pewien, czy to zadziała.


Jeśli jeszcze tego nie zrobiłeś, możesz dodać metodę onUnbind do swojej usługi. W ten sposób możesz dokładnie zobaczyć, kiedy Twoje klasy się od niego odłączają, i może to pomóc w debugowaniu.

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

Spróbuj użyć unbindService () w OnUserLeaveHint (). Zapobiega scenariuszowi wycieku ServiceConnection i innym wyjątkom.
Użyłem go w swoim kodzie i działa dobrze.

Ashwini Shahapurkar
źródło
2

ten błąd występuje, gdy zamierzasz powiązać usługę ograniczoną. więc sol powinno być: -

  1. w połączeniu serwisowym dodaj usługę powiązaną jak poniżej:

    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. unbind usługi onDestroy

        if (serviceBound) {
            unbindService(serviceConnection);
        }
    
Atef Farouk
źródło
1

Możesz po prostu kontrolować to za pomocą wartości logicznej, więc wywołujesz unbind tylko wtedy, gdy zostało wykonane bind

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

Jeśli chcesz go rozwiązać tylko wtedy, gdy został podłączony

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
źródło
0

Każda usługa, która jest związana z działaniem, musi zostać odłączona po zamknięciu aplikacji.

Więc spróbuj użyć

 onPause(){
   unbindService(YOUR_SERVICE);
   super.onPause();
 }
Punit
źródło
0

Niedawno czytałem o usłudze Android i miałem okazję dogłębnie się nią zająć. Napotkałem wyciek usługi, w mojej sytuacji zdarzyło się to, ponieważ miałem niezwiązaną usługę, która uruchamiała powiązaną usługę, ale w tej niezwiązanej usłudze została zastąpiona działaniem .

Więc kiedy zatrzymywałem moją niezwiązaną usługę za pomocą stopSelf (), wystąpił wyciek, powodem było zatrzymywanie usługi nadrzędnej bez odłączania powiązanej usługi. Teraz powiązana usługa działa i nie wie, do kogo należy.

Prostą i prostą poprawką jest wywołanie unbindService (YOUR_SERVICE); w funkcji onDestroy () Twojej aktywności / usługi nadrzędnej. W ten sposób cykl życia zapewni, że powiązane usługi zostaną zatrzymane lub wyczyszczone, zanim działanie / usługi nadrzędne zostaną wyłączone.

Jest jeszcze jedna odmiana tego problemu. Czasami w Twojej powiązanej usłudze chcesz, aby pewne funkcje działały tylko wtedy, gdy usługa jest powiązana, więc w końcu umieszczamy flagę powiązaną w onServiceConnected, na przykład:

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

Do tej pory działa to dobrze, ale problem pojawia się, gdy traktujemy funkcję onServiceDisconnected jako wywołanie zwrotne dla wywołania funkcji unbindService , przez dokumentację jest to wywoływane tylko wtedy, gdy usługa zostanie zabita lub uległa awarii . I nigdy nie otrzymasz tego wywołania zwrotnego w tym samym wątku . Dlatego w końcu robimy coś takiego:

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

Co powoduje poważny błąd w kodzie, ponieważ nasza flaga związana nigdy nie jest resetowana do wartości false i gdy ta usługa jest ponownie połączona w większości przypadków true. Aby uniknąć tego scenariusza, boundw momencie dzwonienia należy ustawić wartość false unbindService.

Bardziej szczegółowo omówiono to na blogu Erika .

Mam nadzieję, że kto tu przyjechał, zaspokoił swoją ciekawość.

Farhaan Bukhsh
źródło