2015-04-17 8 views
5

Ich bin ein absoluter Anfänger in Java und habe ein einfaches Java-Android-Snippet erstellt, wo ich in einem Runable nach 1,5 Sekunden den TextView von Hello World zu Hola Mundo ändere. Es funktioniert einwandfrei, im Grunde ein WeakReference sollte verhindern, dass dieses Speicherleck richtig passiert? Ich habe Zweifel, ob es bei der Geräteausrichtung überhaupt keinen Speicherverlust gibt. Ich würde das gerne überprüfen, aber es gelingt mir nicht, die Ausrichtung in meinem emulierten Android zu ändern.Kann dieses Runnable vor Speicherverlust geschützt werden?

Dies ist der Code:

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

Aus Speicherlecks sicher ist, aber ein paar Antworten wurden auch mit UI-Thread Sperrung betroffen. Tatsächlich führt dieser Code den Handler im Hauptthread aus. Um einen neuen Thread zu erzeugen Ich Laichen einen Thread manuell wie folgt:

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

} 

Das Problem ist jetzt, dass ich kann nicht scheinen, um die erzeugte Thread in irgendeiner Weise zu verzögern, sonst funktioniert es.

Dies:

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

macht die App selbst verlassen und ich nicht, warum. Irgendetwas sagt mir, dass ich es auf die falsche Art verzögere.

EDIT2

Dank der Verbindung @EugenMatynov mir geben: update ui from another thread in android ich verstanden, warum die App verlassen. Es kommt alles auf den Grund Sie können UI-Methoden nicht von anderen Threads als dem Hauptthread aufrufen. und es ist eine schlechte Übung, die Benutzeroberfläche von einem anderen Thread zu aktualisieren.

+2

sauber die Handlers Warteschlange auf OnPause, und Sie sind 100% leckfrei. In diesem Fall. – Blackbelt

+1

Gibt es aufgrund des statischen TextView-Felds keinen Speicherverlust? – stevehs17

Antwort

3

denke ich, Ihr Code Leck ist kostenlos, wenn Sie verwenden:

private static Handler h = new Handler(); 

oder

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

, weil Sie die Ansicht gespeichert haben als eine schwacheReferenz. Die Methode:

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

einfach Haupt Handler des UI-Thread aufrufen so, wenn die Aktivität der Ansicht zerstört wird, ist null, und die Dosis runnable nichts.

auch wegen der schwachen referenz die aktivität kann müll gesammelt werden, da es keinen starken bezug darauf gibt.

+0

Nachdem Sie meine Antwort wieder akzeptiert habe ich meine Antwort und Ihre Frage überprüft und einen Fehler in meiner Antwort gefunden, meine Antwort ist nur gültig, wenn Sie 'private statische Handler h = neue Handler();' oder verwenden 'txtview.postDelayed (new WeakRunnable (txtview), 1500);' Sie können blackbeltt answe akzeptieren oder lassen Sie mich bearbeiten. Beachten Sie auch, dass das Ausführen von runnable by delay anders ist als das des sleeping auf main thread. weil ich glaube Leute machen dich verwirrend :-) – mmlooloo

+0

Was ist der Unterschied zwischen statischer und nicht-statischer Version? –

+0

Verstanden, schöne Erklärung. Ich bin ziemlich neu in Java, also muss ich auf einige Konzepte klicken. Ich entschied mich für 'txtview.postDelayed (new WeakRunnable (txtview), 1500);' da es einfacher aussieht und relativ wenig visuellen Platz benötigt. Fühlen Sie sich frei, Ihre Antwort zu bearbeiten :) –

2

h.postVerzögert (new WeakRunnable (txtview), 1500); Ich denke, es wird UI Thread blockieren. hier ist ein gutes Beispiel für Speicherleck. https://github.com/badoo/android-weak-handler

+1

können Sie den relevanten Code in Antwort hinzufügen und wo der betreffende Code verbessert werden kann? –

+4

* Ich denke, es wird UI Thread * blockieren. Nein, tut es nicht. – Blackbelt

+0

Es wird nicht blockieren UI, als auch Code ist genau das gleiche Code, der in Blog im Zusammenhang mit der Bibliothek geschrieben https://techblog.badoo.com/blog/2014/08/28/android-handler-memory-leaks –

3

Ich habe Zweifel, ob es absolut kein Speicherverlust, wenn Gerät Orientierung auftritt.

könnte es sein. Für 1,5 Sekunden. Nachdem die Warteschlange geleert wurde, kann der Handler als Müll gesammelt werden und auch die alte Aktivität. Um sicher zu überschreiben onPause und handler.removeCallbacks(null); rufen Sie die Handler der Warteschlange zu löschen

+2

In meinem Fall musste ich 'handler.removeCallbacksAndMessages (null);' aufrufen, da 'removeCallbacks(); ab der Implementierung der Android-Version 23 nichts tut, wenn Sie null als Parameter übergeben (siehe MessageQueues' removeMessages() ; '). –

+0

@MateusGondim danke für den Tipp – Blackbelt

1

Bitte tun Sie dies, sonst werden Sie UIThread blockieren und es wird nicht empfohlen. Dazu können Sie auch eine Timertask verwenden, überprüfen Sie es hier: 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); 
    }   

} 

By the way, können Sie Orientierung in Ihrem Emulator auf diese Weise ändern: Ctrl + F12

+0

** Nicht statische innere Klasse kann Speicherlecks erstellen, wie Ihr Code es genau tut: -) ** – mmlooloo

+0

Ich habe bearbeitet Durch das Entfernen von Rückrufen und Nachrichten wird der Code möglicherweise weniger anfällig für Speicherverlust. Was denken Sie? – Grender

+1

Ihr Leck wird durch die Verwendung anonymer neuer Runnable() verursacht, eigentlich ist der OP-Code korrekt, Sie können Callbacks bei onPause entfernen, aber die OP-Dosis mag das nicht, weil zB der Benutzer die Home-Taste drückt und nach 2s er zurück zur App, damit er die Änderung sehen kann. – mmlooloo