Autore Topic: Handler non statico in service  (Letto 2015 volte)

Offline Phate

  • Utente junior
  • **
  • Post: 123
  • Respect: +6
    • Mostra profilo
  • Dispositivo Android:
    Samsung galaxy S
  • Sistema operativo:
    Windows 7
Handler non statico in service
« il: 13 Agosto 2012, 17:00:26 CEST »
0
Ciao, se provo a farlo non statico mi da un warning circa un memory leak...potrei avere maggiori info?

Offline iceweasel

  • Moderatore globale
  • Utente senior
  • *****
  • Post: 878
  • Respect: +147
    • Mostra profilo
  • Dispositivo Android:
    LGE P990 - Google Nexus 5
  • Sistema operativo:
    Linux Debian Sid
Re:Handler non statico in service
« Risposta #1 il: 13 Agosto 2012, 18:48:11 CEST »
+1
Maggiori info rispetto alla documentazioni ufficiali è difficile:

Citazione
In Android, Handler classes should be static or leaks might occur. Messages
enqueued on the application thread's MessageQueue also retain their target
Handler. If the Handler is an inner class, its outer class will be retained as
well. To avoid leaking the outer class, declare the Handler as a static nested
class with a WeakReference to its outer class.

Quella classe non deve essere una "inner class" non statica altrimenti il tuo programma ha potenzialmente dei "memory leak".

Maggiori dettagli tecnici sono scritti nei commenti e nel codice di Android della classe Handler:

http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/2.3.7_r1/android/os/Handler.java
adb logcat | tee /tmp/logcat | grep TAG

Offline Phate

  • Utente junior
  • **
  • Post: 123
  • Respect: +6
    • Mostra profilo
  • Dispositivo Android:
    Samsung galaxy S
  • Sistema operativo:
    Windows 7
Re:Handler non statico in service
« Risposta #2 il: 13 Agosto 2012, 19:20:25 CEST »
0
Mmm..a quanto pare il problema deriva da eventuali messaggi rimasti in coda.
Siccome a me è tutto molto veloce, l'handler deve solo visualizzare l'argomento del messaggio in un toast, posso semplicemente fregarmene secondo te?

Offline iceweasel

  • Moderatore globale
  • Utente senior
  • *****
  • Post: 878
  • Respect: +147
    • Mostra profilo
  • Dispositivo Android:
    LGE P990 - Google Nexus 5
  • Sistema operativo:
    Linux Debian Sid
Re:Handler non statico in service
« Risposta #3 il: 13 Agosto 2012, 21:24:33 CEST »
0
No  >:(

E' una risposta che da programmatore non avrei mai voluto sentire, sembra che non hai minimamente capito il motivo per cui NON si deve fare.
adb logcat | tee /tmp/logcat | grep TAG

Offline Phate

  • Utente junior
  • **
  • Post: 123
  • Respect: +6
    • Mostra profilo
  • Dispositivo Android:
    Samsung galaxy S
  • Sistema operativo:
    Windows 7
Re:Handler non statico in service
« Risposta #4 il: 13 Agosto 2012, 21:55:37 CEST »
0
Scusa scusa era solo una conclusione a cui ero arrivato leggendo quello che hai scritto!

Mh adesso ho un dubbio perché io non ho mai usato i WeakReferences, cmq leggendo in giro:
Citazione
In computer programming, a weak reference is a reference that does not protect the referenced object from collection by a garbage collector; unlike a strong reference. An object referenced only by weak references is considered unreachable (or weakly reachable) and so may be collected at any time.

Secondo la documentazione io dovrei fare qualcosa del genere
Codice: [Seleziona]
private static MyHandler handler;
dove
Codice: [Seleziona]
private class MyHandler extends Handler{
private WeakReference wr;

public MyHandler(Service s){
wr = new WeakReference(s);
}

}

ora, siccome l'handler mi serve per mostrare dei Toasts, per farlo mi serve il context che prendo dall'attributo s, weak reference.
Ma, essendo weak, non corro il rischio che a un certo punto mi arriva una bella null pointer exception visto che s viene raccolto dal garbage collector??

Offline iceweasel

  • Moderatore globale
  • Utente senior
  • *****
  • Post: 878
  • Respect: +147
    • Mostra profilo
  • Dispositivo Android:
    LGE P990 - Google Nexus 5
  • Sistema operativo:
    Linux Debian Sid
Re:Handler non statico in service
« Risposta #5 il: 13 Agosto 2012, 23:44:38 CEST »
0
Scusa, scrivi:

Siccome a me è tutto molto veloce ...

che c'entra la velocità col "memory leak", non ha senso nessuno ha parlato di velocità (leak in inglese significa perdita). La documentazione di Google parla chiaro su' come si deve ereditare dalla classe.

L'uso dei "WeakReference" è corretto ma lo usi come sulle prime versioni di Java, è meglio così:

Codice (Java): [Seleziona]
private WeakReference<Service> wr;
...
wr = new WeakReference<Service>(s);

Il metoto "get" di "WeakReference" ritorna "null" se è stato distrutto, quindi prima di usarlo basta un semplice "if" per vedere se è valido.

adb logcat | tee /tmp/logcat | grep TAG

Offline Phate

  • Utente junior
  • **
  • Post: 123
  • Respect: +6
    • Mostra profilo
  • Dispositivo Android:
    Samsung galaxy S
  • Sistema operativo:
    Windows 7
Re:Handler non statico in service
« Risposta #6 il: 14 Agosto 2012, 00:19:05 CEST »
0
Avevo capito che c'è un memory leak finché ci sono messaggi in coda, insomma finché la coda non viene smaltita anche se la mia app è chiusa il service continua ad esistere (e quindi memory leak). Siccome nel mio caso c'è sempre un solo messaggio in coda, e questo messaggio viene smaltito velocemente, il service cmq morirebbe subito dopo la chiusura dell'app (il tempo per consumare il mess, appunto) volendo essere proprio sfortunati a beccare il messaggio in coda in quell'istante.

Quindi ogni volta devo fare un if e, se è il caso, refreshare il weakreference?

Offline iceweasel

  • Moderatore globale
  • Utente senior
  • *****
  • Post: 878
  • Respect: +147
    • Mostra profilo
  • Dispositivo Android:
    LGE P990 - Google Nexus 5
  • Sistema operativo:
    Linux Debian Sid
Re:Handler non statico in service
« Risposta #7 il: 14 Agosto 2012, 00:59:53 CEST »
+1
Praticamente quasi nulla di quello che hai scritto è corretto.

Il memory leak può capitare indipendentemente dalla presenza dei messaggi, basta solo istanza della classe. Ti consiglio di studiarti molto bene questo intervento a Google I/O:

http://www.google.com/events/io/2011/sessions/memory-management-for-android-apps.html

Il WeakReference se ritorna null significa che il Service è stato distrutto, quindi non puoi aggiornare nulla, non c'è.
adb logcat | tee /tmp/logcat | grep TAG

Offline Phate

  • Utente junior
  • **
  • Post: 123
  • Respect: +6
    • Mostra profilo
  • Dispositivo Android:
    Samsung galaxy S
  • Sistema operativo:
    Windows 7
Re:Handler non statico in service
« Risposta #8 il: 14 Agosto 2012, 01:05:23 CEST »
0
Ok, perfetto  :-(

Aspetta ora ho le idee confuse: io ho questo servizio e non voglio certo che mi venga riciclato.
Se metto il service nel weak reference, mica me lo ritrovo stoppato anzitempo?

Darò cmq un'occhiata all'intervento che, indipendentemente da tutto, sembra molto interessante!

Offline Phate

  • Utente junior
  • **
  • Post: 123
  • Respect: +6
    • Mostra profilo
  • Dispositivo Android:
    Samsung galaxy S
  • Sistema operativo:
    Windows 7
Re:Handler non statico in service
« Risposta #9 il: 19 Agosto 2012, 15:22:09 CEST »
0
Grazie al tuo link e alla interessantissima conferenza sono riuscito a trovare un leak davvero serio! Adesso la mia classe service, da un 7% di retained memory è passata a uno 0.1%  ;-)
Però la faccenda dell'handler sembra ok: ho fatto un giro col memory analyzed e non vedo leaks, inoltre anche nella conferenza il tizio consiglia di rendere non static la classe e l'oggetto, quindi anche se il warning c'è io non ci vedo motivo di leak (dopotutto è presente "may" nel messaggio)

Mio codice:
Codice: [Seleziona]
private Handler myHandler = new Handler(){

};
« Ultima modifica: 19 Agosto 2012, 15:23:51 CEST da Phate »

Offline iceweasel

  • Moderatore globale
  • Utente senior
  • *****
  • Post: 878
  • Respect: +147
    • Mostra profilo
  • Dispositivo Android:
    LGE P990 - Google Nexus 5
  • Sistema operativo:
    Linux Debian Sid
Re:Handler non statico in service
« Risposta #10 il: 20 Agosto 2012, 09:37:21 CEST »
0
Il problema del memory leak di una inner class che eredita da Handler (e anche Binder) è più complesso di una semplice variabile statica che contiene un elemento della UI.

Non a caso ho messo il link hai sorgenti della classe Handler, basta studiarla per capire il problema. I costruttori si agganciano al Looper dei messaggi. Il Looper vive esternamente alla app, se l'oggetto che contiene Handler deve essere distrutto si rischia di non distruggerlo perché esiste ancora un hard reference presente nel Looper. Se la classe che eredita da Handler non è una inner class e tutti i riferimenti esterni sono dei weak reference questo problema non sussiste, la classe che contiene Handle può essere distrutta indipendentemente dal Handler e se Handler deve processare dei messaggi si accorge della distruzione se i weak reference non sono validi.

Se si compila Android (non semplicemente app ma tutto lo stack software) in modalità debug con la variabile "FIND_POTENTIAL_LEAKS" è possibile individuare tutti questi potenziali problemi di memoria nel log di sistema.


adb logcat | tee /tmp/logcat | grep TAG