2013-04-24 15 views
12

Ho passato un gestore creato sul thread mainUI da Activity e passato a un thread che esegue alcune operazioni di rete e quando ottengo risultato invio il risultato all'attività utilizzando il gestore .L'istanza dell'attività esiste ancora anche dopo che onDestroy() è chiamato

Questo approccio ha avuto problema a perdite di memoria quando sono andato attraverso questi link:
Inner ClassHandler Memory Leak
Android Developers

così ho dovuto implementato WeakReference, e ha mantenuto l'istanza di attività utilizzando WeakReference. Ma sto ancora vedendo l'istanza Activity viva anche dopo che l'attività è stata distrutta.

Ho creato un'attività interna Handler e ho passato l'istanza di attività come weakreference al gestore.
Nel momento in cui il mio Handler risponde con un messaggio recapitato dopo 10 secondi, Activity viene distrutto. Ma il riferimento debole ha ancora l'istanza Activity e sto vedendo il Toast, dopo che Activity è stato distrutto.

C'è qualcosa in cui la mia comprensione è sbagliata?
Qualcuno può spiegare come gestire i messaggi consegnati a un gestore, ma l'interfaccia utente non è in giro?

import java.lang.ref.WeakReference; 

import android.os.Handler; 
import android.os.Message; 

public abstract class SingleParamHandler <T> extends Handler 
{ 
private WeakReference<T> mActivityReference; 

public SingleParamHandler(T activity) { 
    mActivityReference = new WeakReference<T>(activity); 
} 

@Override 
public void handleMessage(Message msg) { 
    if (mActivityReference.get() == null) { 
     return; 
    } 
    handleMessage(mActivityReference.get(), msg); 
} 

protected abstract void handleMessage(T activity, Message msg); 

} 

import android.app.Activity; 
import android.os.Bundle; 
import android.os.Message; 
import android.widget.Toast; 

public class MainActivity extends Activity { 

MyHandler<MainActivity> handler; 
@Override 
public void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.main1); 
    handler = new MyHandler<MainActivity>(this); 
    new Thread(new MyRunnable(handler)).start(); 
} 

public void onDestroy() { 
    super.onDestroy(); 
    System.out.println("######## Activity onDestroy() ###### "); 
} 

private class MyRunnable implements Runnable { 
    private Handler mHandler; 
    public MyRunnable(Handler handler) { 
     mHandler = handler; 
    } 

    public void run() { 
     try { 
      Thread.sleep(10000); 
      mHandler.sendMessage(Message.obtain(handler, 1)); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
    } 
} 


private static class MyHandler<T> extends SingleParamHandler<T> { 

    public MyHandler(T activity) { 
     super(activity); 
    } 

    @Override 
    public void handleMessage(T act, Message msg) { 
     if(msg.what == 1) { 
      Toast.makeText((MainActivity)act, "Called after activity destroyed", Toast.LENGTH_LONG).show();; 
     } 
    } 
} 

} 

Sulla base della risposta ottenuta , sto aggiornando la risposta qui. Puoi farlo nel modo in cui ti è piaciuto. Ma questo è un modo.

Aggiunta la funzione di seguito in SingleParamHandler

public void clear() { 
    mActivityReference.clear(); 
} 

e nell'attività OnDestroy()

public void onDestroy() { 
    super.onDestroy(); 
    System.out.println("######## Activity onDestroy() ###### "); 
    handler.clear(); 
} 

risposta

5

Non è necessario un WeakReference qui. Handler può contenere solo un riferimento allo Activity. Nell'attività onDestroy() basta chiamare un metodo su MyHandler che imposta il riferimento a Activity a null. Cerca null in handleMessage().

Un'altra scelta potrebbe essere questa: nell'attività onDestroy() chiamare un metodo che interrompe il thread in attesa in modo che si chiuda prima di inviare il messaggio.

+0

Grazie per la logica. Posso sapere, cosa ti fa dire che il riferimento debole non è necessario, perché il link che ho postato dice che Handler deve essere statico e rendere debole riferimento all'attività o al servizio e controllare prima di gestire il messaggio? non è che contraddice? La tua conoscenza su questo sarebbe apprezzata .. !! – Mani

+0

@Mani Questo suggerimento va nella stessa direzione del mio (registrazione/annullamento delle attività). Stavo solo facendo meno ipotesi. David, penso ancora in questo semplice scenario, sarebbe più facile se il Gestore controlli 'Activity.isDestroyed()', perché 'onDestroy()' non è garantito per l'esecuzione? –

+1

L'articolo che hai collegato copre una situazione specifica e non sono d'accordo con tutto ciò che è scritto lì. Il caso trattato è un "messaggio ritardato" pubblicato su un 'Handler'. L'articolo descrive un caso in cui un messaggio viene pubblicato su un 'Handler' che verrà consegnato in 10 minuti. Questo messaggio ha un riferimento al 'Handler' e il' Handler' ha un riferimento al 'Activity' e questo implica che né il' Handler' né il 'Activity' potranno essere rimossi dal garbage collector ** mentre questo messaggio è ancora in coda **. –

3

Non c'è alcuna garanzia che Android sarà davvero eliminare un oggetto dalla memoria se non è tenuto a farlo. In altre parole, gli oggetti Attività possono rimanere in memoria anche dopo aver chiamato onDestroy() (se c'è abbastanza memoria disponibile). D'altra parte, non vi è alcuna garanzia che onDestroy()sia chiamato se non c'è abbastanza memoria; al contrario, Android può interrompere l'intero processo dopo aver chiamato lo onPause() sull'attività corrente (a seconda della versione di Android).

Penso che ci sia un percorso migliore da seguire per il tuo scopo. Che cosa si potrebbe desiderare di fare è allegare, staccare ed eventualmente ricollegare (ad esempio sulle modifiche di configurazione) Attività al servizio. Non sperare che il garbage collector faccia il lavoro per te. Piuttosto, rendilo esplicitamente.

sottoclasse Activity e sovrascrivere i metodi del ciclo di vita, nonché startActivity() e startActivityForResult() di lasciare il vostro servizio sapere chi comanda in questo momento. Naturalmente, questo è solo un approccio best-effort dal momento che alcune callback non sono garantite, ma ciò conta solo in certe situazioni che non sono pericolose. Ad esempio, la tua attività non si staccherà dal tuo servizio in onPause(), ma potrebbe essere uccisa subito dopo. Ma il tuo servizio viene eseguito nello stesso processo, quindi viene ucciso contemporaneamente.Oppure viene eseguito in un processo diverso, ma in seguito Android noterà la connessione interrotta e potrebbe anche non uccidere il servizio; se no, allora tutto ciò che devi fare è implementarlo in maniera robusta per essere in grado di gestire la perdita di connessione.

Aggiornamento

Dopo aver letto il tuo commento: Hai ragione, non mi rivolgo che specificamente.

sto cercando di capire come evitare di messaggi inviati a un gestore che si crea in un'attività che viene distrutto

Dato il vostro codice di cui sopra, e supponendo che realmente desidera solo per visualizzare Toast s con un'attività fintantoché esiste, il seguente approccio dovrebbe aiutare.

  • Se il Thread si suppone per servire più di un'attività, estenderlo in modo tale che le attività possono registrarsi con il filo dopo la creazione. Se il tuo Thread serve solo uno Activity, passa il riferimento Activity insieme al riferimento Handler alla costruzione del tuo Thread (Runnable).
  • Prima che il vostro Thread invii il messaggio tramite Handler, controllare activity.isDestroyed(). Se l'attività non viene distrutta, invia il messaggio. Se l'attività è distrutta, non inviare il messaggio.
  • seconda che la discussione dovrebbe server di più di un'attività, o uscire E 'Runnable s' run() metodo o impostare è Activity riferimento ad null qualora constati che l'Activity è stato distrutto.

Questo dovrebbe risolvere il codice precedente. Tuttavia, se il tuo scenario cresce, altri approcci potrebbero essere più adatti.

+1

Grazie per il resopnse. Comprendo che l'istanza di attività non verrà cancellata perché è stato chiamato ondoestro(). Ma sto cercando di capire come evitare che i messaggi vengano inviati a un gestore creato in un'attività distrutta ... !! So che il gestore non è legato all'attività, è al thread dell'interfaccia utente principale, ma si chiede come gestirlo? – Mani

+0

@Mani Aggiornato la mia risposta, spero che aiuti! –

Problemi correlati