Android - Threads: Unterschied zwischen den Versionen
| (Eine dazwischenliegende Version desselben Benutzers wird nicht angezeigt) | |||
| Zeile 46: | Zeile 46: | ||
postDelayed(Runnable r, long delayMillis) | postDelayed(Runnable r, long delayMillis) | ||
| − | ==== Beispiel ==== | + | ==== Beispiel Handler mit Runnable ==== |
| + | <pre> | ||
| + | public class HandlerActivity extends Activity { | ||
| + | TextView tvHandler1; | ||
| + | private boolean task1_is_running = false; | ||
| + | private boolean task2_is_running = false; | ||
| + | |||
| + | /*** Handler mit Runnable Beispiel ***/ | ||
| + | // 1. Erzeuge Handler | ||
| + | Handler h1 = new Handler(); | ||
| + | |||
| + | // 2. Erzeuge Runnable | ||
| + | Runnable r1 = new Runnable() { | ||
| + | int count = 0; | ||
| + | @Override | ||
| + | public void run() { | ||
| + | count += 1; | ||
| + | tvHandler1.append("Runnable r1 "+Integer.toString(count)+". mal\n"); | ||
| + | if(task1_is_running){ | ||
| + | // 4. Runnable kann auch sich selbst aufrufen | ||
| + | h1.postDelayed(this,600); | ||
| + | } | ||
| + | } | ||
| + | }; | ||
| + | // Aufgerufen z.B. über Klick | ||
| + | public void startThread1(View v){ | ||
| + | if(task1_is_running){ | ||
| + | task1_is_running = false; | ||
| + | }else{ | ||
| + | task1_is_running = true; | ||
| + | // 3. Übergebe Handler an Runnable | ||
| + | h1.post(r1); | ||
| + | } | ||
| + | } | ||
| + | ... | ||
| + | </pre> | ||
=== Handler Callback mit Message === | === Handler Callback mit Message === | ||
| − | + | Callback erbt von Handler. Daher brauchen wir | |
| − | + | <pre> | |
| − | + | /*** Handler 2 mit Callback Message ***/ | |
| + | final Handler h2 = new Handler(new Callback() { | ||
| + | @Override | ||
| + | public boolean handleMessage(Message msg) { | ||
| + | tvHandler1.append("handler h2 calling \n"); | ||
| + | return false; | ||
| + | } | ||
| + | }); | ||
| + | |||
| + | public void startThread2(View v){ | ||
| + | h2.sendEmptyMessage(0); | ||
| + | } | ||
| + | /*** End Handler2 Example ***/ | ||
| + | </pre> | ||
== Interrupt Thread == | == Interrupt Thread == | ||
Aktuelle Version vom 9. März 2013, 20:33 Uhr
Links[Bearbeiten]
- http://openbook.galileocomputing.de/javainsel9/javainsel_14_002.htm#mj45ef6b526108a654e748170e45b47506
- http://www.vogella.com/articles/AndroidBackgroundProcessing/article.html#androidbackground (gute Artikel Reihe)
Einführung[Bearbeiten]
UI-Thread und Hintergrund Threads[Bearbeiten]
- Jede App läuft in einem eigenen Prozess mit eigener Dalvik Virtual Machine. Für die Sichtbaren Bestandteile der App ist ein User-Interface-Thread zuständig. Die Start-Activity läuft in diesem UI-Thread.
- Aufwändige Berechnungen frieren deshalb das UI ein. Wenn der Thread zu lange "weg" und nicht auf Benutzereingaben reagieren kann, wird er von Android gekillt (Application Not Responding - ANR.
- Threads sind problemlos möglich aber Zugriff auf Views nur über den UI-Thread Möglich.
-> Kommunikation zwischen Hintergrund-Thread und UI-Thread notwendig.
Typisches Beispiel[Bearbeiten]
- Button Click führt zu langer Berechnung
- Berechnung im Hintergrund-Thread
- Anzeige des Ergebnisses in TextView
-> Problem Anzeige geht nur über UI-Thread, Berechnung geht nur über Hintergrund Thread, UI soll nach Berechnung sofort Ergebnis anzeigen.
Man braucht also etwas was es ermöglicht Threads vom Haupthread zu starten und das Ergebnis zurück zu schicken.
Lösung Callback für Threads[Bearbeiten]
Runnable[Bearbeiten]
Objekte die etwas ausführen können
Message[Bearbeiten]
Container für Daten (z.B. Bundle)
Handler[Bearbeiten]
- Ist für die Kommunikation zwischen Threads zuständig. Er kann dazu Runnable und Message Objekte Verschicken und Verarbeiten.
- Stellt zusätzlich eine Warteschlange für Runnables und Message Objekten zur Verfügung um diese nacheinander abzuarbeiten.
So funktioniert es[Bearbeiten]
übergibt Handler Instanz
UI-Thread -------------------------------------> Hintergrund-Thread
legt ab Runnables / Messages
Hintergrund-Thread -----------------------------> Handler-Instanz
Handler Callback mit Runnables[Bearbeiten]
- Erzeuge Handler-Instanz in UI-Thread
- Erzeuge Runnable in UI-Thread
- Erzeuge Hintergrund-Thread und Übergebe Verweis auf Handler
- Übergebe Runnable in der run() Funktion z.B. mittels post(runnable)
- Starte Hintergrund Thread
- Alternativen zu post sind
post(Runnable r) postAtTime(Runnable r, long uptimeMillis) postDelayed(Runnable r, long delayMillis)
Beispiel Handler mit Runnable[Bearbeiten]
public class HandlerActivity extends Activity {
TextView tvHandler1;
private boolean task1_is_running = false;
private boolean task2_is_running = false;
/*** Handler mit Runnable Beispiel ***/
// 1. Erzeuge Handler
Handler h1 = new Handler();
// 2. Erzeuge Runnable
Runnable r1 = new Runnable() {
int count = 0;
@Override
public void run() {
count += 1;
tvHandler1.append("Runnable r1 "+Integer.toString(count)+". mal\n");
if(task1_is_running){
// 4. Runnable kann auch sich selbst aufrufen
h1.postDelayed(this,600);
}
}
};
// Aufgerufen z.B. über Klick
public void startThread1(View v){
if(task1_is_running){
task1_is_running = false;
}else{
task1_is_running = true;
// 3. Übergebe Handler an Runnable
h1.post(r1);
}
}
...
Handler Callback mit Message[Bearbeiten]
Callback erbt von Handler. Daher brauchen wir
/*** Handler 2 mit Callback Message ***/
final Handler h2 = new Handler(new Callback() {
@Override
public boolean handleMessage(Message msg) {
tvHandler1.append("handler h2 calling \n");
return false;
}
});
public void startThread2(View v){
h2.sendEmptyMessage(0);
}
/*** End Handler2 Example ***/
Interrupt Thread[Bearbeiten]
Interrupting Threads[Bearbeiten]
Beispiel (Android)
Boolean flag = true;
Thread secondary = new Thread(new Runnable() {
@Override
public void run() {
while (flag) {
// do something
}
}
});
secondary.start(); //start the thread
flag = false; // this will force secondary to finish its execution
try {
secondary.join(); // wait for secondary to finish
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
Thread Lifetime[Bearbeiten]
Beispiel[Bearbeiten]
Quelle: vogella s.o.
package de.vogella.android.threadslifecycle;
import java.io.IOException;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.ImageView;
public class ThreadsLifecycleActivity extends Activity {
// Static so that the thread access the latest attribute
private static ProgressDialog dialog;
private static Bitmap downloadBitmap;
private static Handler handler;
private ImageView imageView;
private Thread downloadThread;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Create a handler to update the UI
handler = new Handler() {
@Override
public void handleMessage(Message msg) {
imageView.setImageBitmap(downloadBitmap);
dialog.dismiss();
}
};
// get the latest imageView after restart of the application
imageView = (ImageView) findViewById(R.id.imageView1);
Context context = imageView.getContext();
System.out.println(context);
// Did we already download the image?
if (downloadBitmap != null) {
imageView.setImageBitmap(downloadBitmap);
}
// Check if the thread is already running
downloadThread = (Thread) getLastNonConfigurationInstance();
if (downloadThread != null && downloadThread.isAlive()) {
dialog = ProgressDialog.show(this, "Download", "downloading");
}
}
public void resetPicture(View view) {
if (downloadBitmap != null) {
downloadBitmap = null;
}
imageView.setImageResource(R.drawable.icon);
}
public void downloadPicture(View view) {
dialog = ProgressDialog.show(this, "Download", "downloading");
downloadThread = new MyThread();
downloadThread.start();
}
// Save the thread
@Override
public Object onRetainNonConfigurationInstance() {
return downloadThread;
}
// dismiss dialog if activity is destroyed
@Override
protected void onDestroy() {
if (dialog != null && dialog.isShowing()) {
dialog.dismiss();
dialog = null;
}
super.onDestroy();
}
// Utiliy method to download image from the internet
static private Bitmap downloadBitmap(String url) throws IOException {
HttpUriRequest request = new HttpGet(url.toString());
HttpClient httpClient = new DefaultHttpClient();
HttpResponse response = httpClient.execute(request);
StatusLine statusLine = response.getStatusLine();
int statusCode = statusLine.getStatusCode();
if (statusCode == 200) {
HttpEntity entity = response.getEntity();
byte[] bytes = EntityUtils.toByteArray(entity);
Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0,
bytes.length);
return bitmap;
} else {
throw new IOException("Download failed, HTTP response code "
+ statusCode + " - " + statusLine.getReasonPhrase());
}
}
static public class MyThread extends Thread {
@Override
public void run() {
try {
// Simulate a slow network
try {
new Thread().sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
downloadBitmap = downloadBitmap("http://www.devoxx.com/download/attachments/4751369/DV11");
// Updates the user interface
handler.sendEmptyMessage(0);
} catch (IOException e) {
e.printStackTrace();
} finally {
}
}
}
}
Old Stuff[Bearbeiten]
Threads in Android Activities[Bearbeiten]
http://android-developers.blogspot.de/2009/05/painless-threading.html
In Android läuft jede Activity in einem eigenen Thread (Main Thread). D.h. wenn diese Beschäftigt ist z.B. für eine Berechnung, dann ist auch das User Interface blockiert.
Android erlaubt ganz normale Java Threads und auf den ersten Blick funktioniert es auch.
Thread thread = new Thread(new Runnable(){
@Override
public void run() {
// mach was
}
});
thread.start();
So sollte man es aber nicht machen.
Beispiel
Bild über das Netzwerk runterladen und als ImageView zeigen.
So nicht
public void onClick(View v) {
new Thread(new Runnable() {
public void run() {
Bitmap b = loadImageFromNetwork();
mImageView.setImageBitmap(b);
}
}).start();
}
Um immer mit dem Hauptthread verbunden zu bleiben gibt es mehrerer Wege:
- Activity.runOnUiThread(Runnable)
- View.post(Runnable)
- View.postDelayed(Runnable, long)
- Handler
Beispiel
public void onClick(View v) {
new Thread(new Runnable() {
public void run() {
final Bitmap b = loadImageFromNetwork();
mImageView.post(new Runnable() {
public void run() {
mImageView.setImageBitmap(b);
}
});
}
}).start();
}
Der Nachteil ist bei allen, daß der Code schnell Komplex und unübersichtlich wird. Daher gibt es das Konzept AsyncTask
Beispiel mit AsyncTask
public void onClick(View v) {
new DownloadImageTask().execute("http://example.com/image.png");
}
private class DownloadImageTask extends AsyncTask {
protected Bitmap doInBackground(String... urls) {
return loadImageFromNetwork(urls[0]);
}
protected void onPostExecute(Bitmap result) {
mImageView.setImageBitmap(result);
}
}
Handlers[Bearbeiten]
Handler braucht man wenn:
- Ein Thread mit dem UserInterface kommunizieren will.
- Ein Thread MessageQueuing, scheduling and wiederholende Tasks ausführen soll.
Der Handler wird in der Acivity (z.B. in der onCreate() Methode) erzeugt und kann runnables entgegen nehmen (oder messages von einem scheduler) , die somit zugriff auf die Activity haben ohne den Main Thread zu blockieren.
AsyncTasks[Bearbeiten]
Async Task sind eine gute Wahl, wenn es darum geht längere Background Prozesse auszuführen (z.B. Downloads). Siehe Beispiel oben.