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 == 1
zapisanego 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?
źródło
Odpowiedzi:
Nie dostarczyłeś żadnego kodu z
LightFactoryRemote
, więc jest to tylko domniemanie, ale wygląda na to, że masz problem, gdybyś używał samejbindService
metody.Aby upewnić się, że usługa działa, nawet po
onDestroy
wywołaniu metody działania, które ją uruchomiło , należy najpierw użyćstartService
.Dokumentacja systemu Android dla stanu startService :
Natomiast w przypadku bindService :
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.
źródło
Możesz użyć:
@Override public void onDestroy() { super.onDestroy(); if (mServiceConn != null) { unbindService(mServiceConn); } }
źródło
Zwiążesz się
onResume
jednak w unbindonDestroy
. Zamiast tego powinieneś wykonać unbinding inonPause
, 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.źródło
Powinieneś tylko odpiąć usługę w
onDestroy()
. Wtedy ostrzeżenie przejdzie.Zobacz tutaj .
źródło
Wspomniałeś, że użytkownik dość szybko przełącza się między działaniami. Czy to możliwe, że dzwonisz,
unbindService
zanim 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
onServiceConnected
ktoś dzwoni, możesz zadzwonić,unbindService
jeślionDestroy
już 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; }
źródło
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.
źródło
ten błąd występuje, gdy zamierzasz powiązać usługę ograniczoną. więc sol powinno być: -
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; }
};
unbind usługi onDestroy
if (serviceBound) { unbindService(serviceConnection); }
źródło
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; } };
źródło
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(); }
źródło
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,bound
w momencie dzwonienia należy ustawić wartość falseunbindService
.Bardziej szczegółowo omówiono to na blogu Erika .
Mam nadzieję, że kto tu przyjechał, zaspokoił swoją ciekawość.
źródło