2015-04-17 24 views
5

Sono un principiante assoluto in Java e ho creato un semplice snippet Java per Android dove in un Runnable dopo 1,5 secondi cambio lo TextView da Hello World a Hola Mundo. Funziona perfettamente, in pratica un WeakReference dovrebbe impedire che questa perdita di memoria avvenga correttamente? Ho il dubbio che non ci sia assolutamente perdita di memoria ogni volta che si verifica l'orientamento del dispositivo. Mi piacerebbe controllare questo, ma non riesco a cambiare orientamento nel mio Android emulato.Questo Runnable è sicuro da perdite di memoria?

Questo è il codice:

package com.example.helloworld; 

import android.app.Activity; 
import android.os.Bundle; 
import android.os.Handler; 
import android.widget.TextView; 
import android.util.Log; 
import java.lang.ref.WeakReference; 

public class HelloWorldActivity extends Activity 
{ 
    private Handler h = new Handler(); 
    private static TextView txtview; 
    /** Called when the activity is first created. */ 
    @Override 
    public void onCreate(Bundle savedInstanceState) 
    { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.main); 
     txtview = (TextView) findViewById(R.id.mainview); 

     h.postDelayed(new WeakRunnable(txtview),1500); 
    } 

    private static final class WeakRunnable implements Runnable { 
     private final WeakReference<TextView> mtextview; 

     protected WeakRunnable(TextView textview){ 
      mtextview = new WeakReference<TextView>(textview); 
     } 

      @Override 
      public void run() { 
       TextView textview = mtextview.get(); 
       if (textview != null) { 
        txtview.setText("Hola Mundo"); 
        textview = null; // No idea if setting to null afterwards is a good idea 
       } 
       Log.d("com.example.helloworld", "" + textview); 
      } 
    }   

} 

EDIT

E 'al sicuro da perdite di memoria, ma alcune risposte sono stati anche occupa il blocco thread dell'interfaccia utente. In realtà questo codice esegue il gestore nel thread principale (UI). Per generare un nuovo thread sto un thread manualmente come segue:

package com.example.helloworld; 

import android.app.Activity; 
import android.os.Bundle; 
import android.os.Handler; 
import android.widget.TextView; 
import android.util.Log; 
import java.lang.ref.WeakReference; 

public class HelloWorldActivity extends Activity 
{ 

    private static TextView txtview; 
    /** Called when the activity is first created. */ 
    @Override 
    public void onCreate(Bundle savedInstanceState) 
    { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.main); 
     txtview = (TextView) findViewById(R.id.mainview); 

     Thread t = new Thread(new WeakRunnable(txtview)); 
     t.start(); 
    } 

    private static final class WeakRunnable implements Runnable { 
     private final WeakReference<TextView> mtextview; 

     protected WeakRunnable(TextView textview){ 
      mtextview = new WeakReference<TextView>(textview); 
     } 

      @Override 
      public void run() { 
       TextView textview = mtextview.get(); 
       if (textview != null) { 
        /* 
        try { 
         Thread.sleep(1500); 
        } catch (InterruptedException e) { 
         e.printStackTrace(); 
        } 
        */ 
        txtview.setText("Hola Mundo"); 
        textview = null; 
       } 
       Log.d("com.example.helloworld", "" + Thread.currentThread().getName()); // Outputs "Thread-<num>" if not running on UI thread 
      } 
    }   

} 

Il problema ora è che io non riesco a ritardare la discussione deposto le uova in qualsiasi modo, in caso contrario funziona.

questo:

try { 
    Thread.sleep(1500); 
} catch (InterruptedException e) { 
    e.printStackTrace(); 
} 

rende l'applicazione stessa chiudere e io non capisco perché. Qualcosa mi dice che lo sto ritardando nel modo sbagliato.

EDIT2

Grazie al collegamento @EugenMatynov mi danno: update ui from another thread in android ho capito perché l'applicazione lasciò. Tutto si riduce al motivo Non è possibile chiamare i metodi dell'interfaccia utente da thread diversi dal thread principale. ed è una cattiva pratica aggiornare l'interfaccia utente da un altro thread.

+2

pulito il gestore del fare la fila su onPause, e sei privo di perdite al 100%. In questo caso. – Blackbelt

+1

Non c'è una perdita di memoria in virtù del campo statico TextView? – stevehs17

risposta

3

Credo che il codice è perdite gratuito se si utilizza:

private static Handler h = new Handler(); 

o

txtview.postDelayed(new WeakRunnable(txtview),1500); 

perché è stato memorizzato la vista come una debolezza. il metodo:

txtview.postDelayed(new WeakRunnable(txtview),1500); 

semplicemente chiamano conduttore principale del thread UI modo se l'attività viene distrutta la vista è nullo e la dose eseguibile nulla.

anche a causa del debole riferimento l'attività può essere garbage collection perché non vi è alcun riferimento forte ad esso.

+0

Dopo aver accettato di nuovo la mia risposta ho rivisto la mia risposta e la tua domanda e ho trovato un bug nella mia risposta, la mia risposta è valida solo se si utilizza il 'static handler privato h = new Handler();' oppure 'txtview.postDelayed (new WeakRunnable (txtview), 1500);' puoi accettare blackbeltt rispondi o lasciarmi modificare. si noti inoltre che l'esecuzione di un eseguibile in base al ritardo è diversa dalla sospensione sul thread principale. perché penso che le persone ti rendano confuso :-) – mmlooloo

+0

Qual è la differenza tra la versione statica e quella non statica? –

+0

Capito, bella spiegazione. Sono piuttosto nuovo di Java quindi devo fare clic su alcuni concetti. Ho optato per 'txtview.postDelayed (new WeakRunnable (txtview), 1500);' dato che sembra più semplice e occupa uno spazio visivo relativamente inferiore. Sentiti libero di modificare la tua risposta :) –

2

h.postDelayed (new WeakRunnable (txtview), 1500); Penso che bloccherà il thread UI. questo è un buon esempio di perdita di memoria. https://github.com/badoo/android-weak-handler

+1

puoi aggiungere il codice pertinente in risposta e dove il codice in questione può essere migliorato? –

+4

* Penso che bloccherà il thread UI *. No non lo fa. – Blackbelt

+0

Non bloccherà l'interfaccia utente, così come il codice è esattamente lo stesso codice scritto nel blog relativo alla libreria https://techblog.badoo.com/blog/2014/08/28/android-handler-memory-leaks –

3

ho un dubbio se non c'è alcuna perdita di memoria quando si verifica dispositivo orientamento.

Potrebbe essere. Per 1,5 secondi. Dopo che la coda è stata svuotata, il gestore può essere garbage collection e anche la vecchia Activity. Per essere sicuri onPause override, e chiamare handler.removeCallbacks(null); per cancellare la coda della Handler

+2

Nel mio caso, ho dovuto chiamare 'handler.removeCallbacksAndMessages (null);', perché dall'implementazione della versione 23 di Android, 'removeCallbacks();' non fa nulla quando si passa null come parametro (si veda 'RemoveMessages() di MessageQueue ; '). –

+0

@MateusGondim grazie per il suggerimento – Blackbelt

1

Si prega di fare questo, altrimenti si bloccherà UIThread e non è raccomandato. Per fare questo, è anche possibile utilizzare un TimerTask, controllare qui: http://developer.android.com/reference/java/util/TimerTask.html

import android.widget.TextView; 
import android.util.Log; 
import java.lang.ref.WeakReference; 

public class HelloWorldActivity extends Activity 
{ 
    private Handler h = new Handler(); 
    private static TextView txtview; 

    /** Called when the activity is first created. */ 
    @Override 
    public void onCreate(Bundle savedInstanceState) 
    { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.main); 
     txtview = (TextView) findViewById(R.id.mainview);   

     h.postDelayed(new Runnable() { 
      @Override 
      public void run() { 
       changeText(); 
      } 
     }, 1500); 
    } 

    public void changeText(){ 
     txtview.setText("Hola mundo."); 
     h.removeCallbacksAndMessages(null); 
    }   

} 

Tra l'altro, è possibile modificare l'orientamento nel vostro emulatore questo modo: Ctrl + F12

+0

** La classe interna non statica può creare perdite di memoria come il tuo codice lo fa esattamente: -) ** – mmlooloo

+0

Ho modificato il codice, forse rimuovendo callback e messaggi, renderà il codice meno suscettibile di causare perdite di memoria. Cosa ne pensi? – Grender

+1

la tua perdita è causata dall'uso di un nuovo Runnable() anonimo, in realtà il codice OP è corretto, puoi rimuovere i callback su onPause ma potrebbe essere la dose dell'OP che non piace farlo perché ad esempio l'utente preme il tasto home e dopo 2 secondi torna all'app, quindi vuole vedere il cambiamento. – mmlooloo