Beiträge mit dem Tag ‘onCreate’

Threads in Android Teil 2 (Aktualisierung der UI)

Werden Threads in Android überhaupt benötigt? Ja, besonders bei Operationen mit langer Laufzeit wie z.B. beim Zugriff auf das Netzwerk. Solche Operationen können den Haupt-Thread, durch den die Benutzeroberfläche (UI) ausgeführt wird, blockieren. Als Folge dessen ist die UI nicht in der Lage Benutzereingaben entgegenzunehmen.
Das folgende Beispiel soll das Problem veranschaulichen:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    textView = (TextView) findViewById(R.id.textView);
    textView.setText("hello World");

    // simulate long running operation
    try {
        Thread.sleep(5000);
    } catch (InterruptedException e) {
        Log.e(TAG, "Thread.sleep", e);
    }
}

Gekapselte Prozesse brauchen in Threads länger, besonders dann wenn der Prozess in den Lebenszyklus der Aktivität gestartet wird (onCreate, onResume, onPause).
Wenn eine Anwendung nicht reagiert, liefert das Android-System normalerweise die Meldung "Anwendung reagiert nicht. Sofort schließen / Warten" zurück (ANR Dialog – Application Not Responding).

Um dies zu umgehen kann ein Thread erstellt werden der am Ende eine Meldung ausgibt:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    textView = (TextView) findViewById(R.id.textView);
    textView.setText("hello World");

    thread = new Thread() {
        public void run() {
            try {
                // simulate long running operation
                Thread.sleep(5000);
                textView.setText("new value...");
            } catch (InterruptedException e) {
                Log.e(TAG, "run in thread", e);
            }
        }
    };
    thread.start();
}

War es das schon? Nein, leider nicht. Nachfolgend können Sie sehen wie sich die Anwendung mit folgender Meldung verabschiedet:
CalledFromWrongThreadException Only the original thread that created a view hierarchy can touch its views.

CalledFromWrongThreadException

Genau wie in Java oder C# ein UI-Objekt (in unserem Fall ein textView) kann dieses nur durch den Thread verändert werden, durch den es auch erzeugt wurde. In diesem Beispiel ist dies der Main-UI-Thread (nicht der neue Thread).

Schauen Sie sich dazu auch den Artikel Painless Threading auf der Android-Entwickler-Seite an. Ein weiteres Tutorial dort zeigt wie mittels des Handler die UI eines Threads aktualisiert werden kann.

Eine Handler-Instanz wird an den Main-UI-Thread gebunden und ist so in der Lage das UI-Objekt zu aktualisieren, wenn Benachrichtigungen von anderen Threads empfangen werden.

Als nächstes wird eine Unterklasse der Handler-Klasse erzeugt und die handleMessage Methode überschrieben. msg.obj enthält die Nachricht aus dem anderen Thread.

private Handler uiHandler = new UIHandler();

class UIHandler extends Handler {
    @Override
    public void handleMessage(Message msg) {
        // a message is received; update UI text view
        textView.setText(msg.obj.toString());
        super.handleMessage(msg);
    }
}

Erstellen Sie in dem länger laufenden Thread das Message-Objekt und übergeben Sie es an den Handler. Verwenden Sie Message.obtain() um eine Nachricht aus dem Pool wieder verwendbarer Objekte zu übernehmen.

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    textView = (TextView) findViewById(R.id.textView);
    textView.setText("hello World");

    thread = new Thread() {
        public void run() {
            try {
                // simulate long running operation
                Thread.sleep(5000);

                // create message which will be send to handler
                Message msg = Message.obtain(uiHandler);
                msg.obj = "new value...";
                uiHandler.sendMessage(msg);

            } catch (InterruptedException e) {
                Log.e(TAG, "run in thread", e);
            }
        }
    };
    thread.start();
}

Das uiHandler-Objekt erhält die Nachricht und gibt den Inhalt im UI-Objekt aus.

Falls Sie unterschiedliche Threads haben und kontrollieren möchten woher die Nachricht kommt, können Sie msg.what verwenden um die Nachricht zu identifizieren:

class UIHandler extends Handler {
    private static final int ID_0 = 0;
    private static final int ID_1 = 1;

    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
        case ID_0:
            // a message is received; update UI text view
            if (msg.obj != null)
                textView.setText(msg.obj.toString());
            break;

        default:
            break;
        }
        super.handleMessage(msg);
    }
}

Ändern Sie den Thread um eine Nachricht zu senden (benutzerdefinierter Code):

// create message which will be send to handler
Message msg = Message.obtain(uiHandler, UIHandler.ID_0);
msg.obj = "new value...";
uiHandler.sendMessage(msg);