Skip to content

Technik Blog

Programmieren | Arduino | ESP32 | MicroPython | Python | Raspberry Pi | Raspberry Pi Pico

Menu
  • Smarthome
  • Gartenautomation
  • Arduino
  • ESP32 & Co.
  • Raspberry Pi & Pico
  • Solo Mining
  • Deutsch
  • English
Menu

Android Programmierung: AsyncTask mit ProgressDialog (Lösung mit einem Callback)

Posted on 11. März 20193. Mai 2023 by Stefan Draeger

In diesem Tutorial möchte ich beschreiben, wie man eine asynchrone Aufgabe auslagert und parallel dazu einen Dialog anzeigt. Den ProgressDialog habe ich bereits im Tutorial Android, ProgressDialog für lange Operationen erläutert.

Es gibt bereits einige Beispiele dazu im Internet und die Lösungen sind zumeist praktikabel und funktionell. Jedoch sind diese zumeist mit einer inneren Klasse (wie in der offiziellen Dokumentation https://developer.android.com/reference/android/os/AsyncTask) und blähen dadurch den Quellcode unnötig auf. Ich möchte gerne einen anderen Weg gehen und den AsyncTask auslagern und mit einem Callback versehen, so halten wir unseren Quellcode schlank und haben immer das Wesentliche im blick.

Projekt erstellen

Für die nachfolgenden Schritte benötigen wir ein einfaches, leeres Android Projekt mit einer Activity (EmptyActivity). 

Gerne möchte ich dir der einfachheitshalber ein Download für ein Projekt anbieten, welches du dir in Android Studio importieren kannst.

Android Programmierung: AsyncTask mit ProgressDialog (leeres Projekt)Herunterladen

Layout erstellen

Für das Ausführen des asynchronen Task benötigen wir eine Schaltfläche und für die Anzeige der Daten  TextView Elemente. Diese Elemente fügen wir über den Designer auf das Layout „activity_main.xml“.

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/startBtn"
        android:layout_width="206dp"
        android:layout_height="63dp"
        android:layout_marginStart="16dp"
        android:layout_marginTop="38dp"
        android:text="Starten"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/errorMsgTextView" />

    <TextView
        android:id="@+id/dateinameTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="12dp"
        android:layout_marginTop="85dp"
        android:textColor="@android:color/holo_blue_dark"
        app:layout_constraintStart_toEndOf="@+id/textView4"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/dateigroesseTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="12dp"
        android:layout_marginTop="34dp"
        android:textColor="@android:color/holo_blue_dark"
        app:layout_constraintStart_toEndOf="@+id/textView5"
        app:layout_constraintTop_toBottomOf="@+id/dateinameTextView" />

    <TextView
        android:id="@+id/textView4"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:text="Dateiname: "
        android:textColor="@android:color/black"
        app:layout_constraintBaseline_toBaselineOf="@+id/dateinameTextView"
        app:layout_constraintStart_toStartOf="parent" />

    <TextView
        android:id="@+id/textView5"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:text="Dateigröße:"
        android:textColor="@android:color/black"
        app:layout_constraintBaseline_toBaselineOf="@+id/dateigroesseTextView"
        app:layout_constraintStart_toStartOf="parent" />

    <TextView
        android:id="@+id/errorMsgTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:text="Fehlermeldung:"
        android:textColor="@android:color/holo_red_light"
        app:layout_constraintBaseline_toBaselineOf="@+id/textView7"
        app:layout_constraintStart_toStartOf="parent" />

    <TextView
        android:id="@+id/textView7"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="10dp"
        android:layout_marginTop="29dp"
        android:textColor="@android:color/holo_red_light"
        app:layout_constraintStart_toEndOf="@+id/errorMsgTextView"
        app:layout_constraintTop_toBottomOf="@+id/dateigroesseTextView" />

</android.support.constraint.ConstraintLayout>

Interface ICallback erzeugen

Für die Verarbeitung des Ergebnisses unseres asynchronen Tasks benötigen wir ein Interface. Dieses ermöglicht es uns späterer mehrere Implementationen für eventuelle verschiedene Ausführungen zu implementieren.

package de.draegerit.asynctaskcallbackapp;

public interface ICallback {

    void handleResult(Result result);
}

Nun müssen wir uns eine innere Klasse schreiben welche das Interface „ICallback“ implementiert. Mit dem Implementieren des Interfaces müssen wir zusätzlich die Methode „handleResult“ implementieren.

class CallbackImpl implements ICallback {
   @Override
   public void handleResult(Result result) {

   }
}
package de.draegerit.asynctaskcallbackapp;

public class Result {

    private int size;
    
    private String filename;
    
    private String message;

    public int getSize() { return size; }
    public void setSize(int size) { this.size = size; }

    public String getMessage() { return message; }
    public void setMessage(String message) { this.message = message; }

    public String getFilename() { return filename; }
    public void setFilename(String filename) { this.filename = filename; }
}

asynchronen Task erstellen

Nun können wir uns eine öffentliche Klasse für den asynchronen Task erstellen, welchen wir mit der Schaltfläche starten wollen.

In diesem Beispiel möchte ich eine einfache Datei aus dem Internet laden. Da ich in diesem Tutorial beschreiben möchte, wie ein asynchroner Task mit einem Callback ausgestattet werden kann, überspringe ich die Erläuterungen für das Herunterladen von Dateien.

In dem Kontruktors des asynchronen Task wird der Context und zusätzlich ein Callback übergeben.

package de.draegerit.asynctaskcallbackapp;

import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.AsyncTask;
import android.util.Log;

import java.io.DataInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.net.URL;
import java.net.URLConnection;

public class RequestAsyncTask extends AsyncTask<Void, Void, Result> {

    //TAG für den Logger
    private static final String TAG = "RequestAsyncTask";

    //Zieldatei
    private static final String ADDRESS = "http://progs.draeger-it.blog/example.file";

    //Schwache Referenz auf den Context
    private WeakReference<Context> contextRef;

    //ProgressDialog für die Fortschrittsanzeige
    private ProgressDialog progressDialog;

    //Das Ergebniss des asynchronen Tasks
    private Result result;

    //Der Callback welcher zum schluss ausgeführt werden soll.
    private ICallback callback;

    //Variable welche gesetzt wird wenn die Schaltfläche "Abbrechen" im ProgressDialog betätigt wird.
    private boolean abortDownload;

    /**
     * Konstruktor
     * @param ctx - der Context
     * @param callback - der Callback welcher zum Schluss ausgeführt werden soll
     */
    public RequestAsyncTask(Context ctx, ICallback callback) {
        contextRef = new WeakReference<>(ctx);
        this.callback = callback;
    }

    @Override
    protected Result doInBackground(Void... voids) {
        result = new Result();
        try {
            //Die Progressbar soll den Fortschritt in Prozent anzeigen.
            progressDialog.setMax(100);

            DataInputStream stream = null;

            //Dateiname generieren
            String filename = String.valueOf(System.currentTimeMillis()).concat(".file");
            result.setFilename(filename);
            //Referenz des Context laden
            Context ctx = contextRef.get();
            try (FileOutputStream outputStream = ctx.openFileOutput(filename, Context.MODE_PRIVATE);){
                File privateFileDirectory = ctx.getFilesDir();
                Log.i(TAG, privateFileDirectory.getAbsolutePath());
                //Aufbau der Verbindung
                URL u = new URL(ADDRESS);
                URLConnection conn = u.openConnection();
                //ermitteln der Dateigröße
                int contentLength = conn.getContentLength();
                //ablegen der Dateigröße in unseren Result
                result.setSize(contentLength);
                //Datenstream öffnen
                stream = new DataInputStream(u.openStream());

                byte[] buffer = new byte[1024];
                int count;
                int total = 0;
                int percent;
                //Solange der Stream noch Daten hat und die Variable abortDownload nicht Boolean.True ist, mache...
                while (((count = stream.read(buffer)) != -1) && !abortDownload) {
                    outputStream.write(buffer, 0, count);
                    total += count;
                    percent = (total * 100) / contentLength;
                    progressDialog.setProgress(percent);
                }
            } catch (Exception e) {
                //Wenn ein Fehler auftritt so soll dieser in unser Result gespeichert werden.
                result.setMessage(e.getMessage());
                e.printStackTrace();
            } finally {
                //Zum Schluss den Datenstream schließen
                if (stream != null) {
                    try {
                        stream.close();
                    } catch (IOException e) {
                        //Wenn ein Fehler auftritt so soll dieser in unser Result gespeichert werden.
                        result.setMessage(e.getMessage());
                        e.printStackTrace();
                    }
                }
            }
        } catch (Exception e) {
            //Wenn ein Fehler auftritt so soll dieser in unser Result gespeichert werden.
            result.setMessage(e.getMessage());
            e.printStackTrace();
        }
        //Rückgabe unseres Ergebnisses.
        return result;
    }

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        //Anzeigen des ProgressDialoges,
        //dieses geschieht noch bevor der Download gestartet wird.
        progressDialog = getWaitDialog();
        progressDialog.show();
    }

    @Override
    protected void onPostExecute(Result result) {
        super.onPostExecute(result);
        //nach dem Download (erfolgreich oder nicht)
        //soll der ProgressDialog geschlossen werden.
        progressDialog.dismiss();
        //Ausführen des Callbacks
        callback.handleResult(result);
    }

    /**
     * Liefert einen ProgressDialog
     * @return ein ProgressDialog
     */
    private ProgressDialog getWaitDialog() {
        Context context = contextRef.get();
        String titel =  context.getResources().getString(R.string.msg_loaddialog_titel);
        String message = context.getResources().getString(R.string.msg_loaddialog_message);

        ProgressDialog progressDialog = new ProgressDialog(context);
        progressDialog.setTitle(titel);
        progressDialog.setMessage(message);
        progressDialog.setCancelable(false);
        progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
        progressDialog.setButton(ProgressDialog.BUTTON_POSITIVE, context.getResources().getString(R.string.abort), new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                abortDownload = true;
            }
        });
        return progressDialog;
    }

}

Für dieses Beispiel habe ich eine Datei mit ca. 30 MB auf eine Subdomain (http://progs.draeger-it.blog/example.file) geladen. Ich kann & möchte nicht garantieren, dass diese Datei auf ewig bereitgestellt wird. Die Datei selbst habe ich mit dem Befehl „fsutil file createnew example.file 3000000“ unter Microsoft Windows 10 erstellt.

ProgressDialog - Download im Vorgang
ProgressDialog – Download im Vorgang

Wichtig ist hier die Methode „onPostExecute“, diese Methode wird zum Schluss ausgeführt und in diesem Beispiel werde ich hier den Callback aufrufen.

@Override
protected void onPostExecute(Result result) {
    super.onPostExecute(result);
    //nach dem Download (erfolgreich oder nicht)
    //soll der ProgressDialog geschlossen werden.
    progressDialog.dismiss();
    //Ausführen des Callbacks
    callback.handleResult(result);
}

Ausführen des asynchronen Task

Für das Ausführen des asynchronen Task haben wir im ersten Schritt eine Schaltfläche erzeugt. Nun wollen wir an diese Schaltfläche einen Listener hängen und in diesem den Task starten.

public class MainActivity extends AppCompatActivity {

   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);

       Button startBtn = findViewById(R.id.startBtn);
       startBtn.setOnClickListener(new View.OnClickListener() {
          @Override
          public void onClick(View v) {
             RequestAsyncTask requestAsyncTask = new RequestAsyncTask(MainActivity.this, new CallbackImpl());
             requestAsyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
          }
      });
   }

Nun müssen wir noch einen Callback implementieren. In diesem Callback werde ich 2 Textfelder (jeweils mit Dateiname, Dateigröße) und / oder ein Feld mit der Fehlermeldung einer Exception befüllen.

class CallbackImpl implements ICallback {
   @Override
   public void handleResult(Result result) {
     //Wenn das Feld "message" leer bzw. NULL ist dann ist kein Fehler aufgetreten.
     boolean showErrorMessage = result.getMessage() == null;

     TextView errorMsgTextView = findViewById(R.id.errorMsgTextView);
     //Wenn keine Fehlermeldung aufgetreten ist dann soll das TextView Element ausgeblendet werden.
     errorMsgTextView.setVisibility(showErrorMessage ? View.VISIBLE : View.INVISIBLE);
     if(showErrorMessage){
        errorMsgTextView.setText(result.getMessage());
     }

     TextView dateinameTextView = findViewById(R.id.dateinameTextView);
     dateinameTextView.setText(result.getFilename());

     TextView dateigroesseTextView = findViewById(R.id.dateigroesseTextView);
     dateigroesseTextView.setText(String.valueOf(result.getSize()).concat(" byte"));
  }
}

Download

Android Programmierung: AsyncTask mit ProgressDialog (leeres Projekt)Herunterladen
Android Programmierung: AsyncTask mit ProgressDialogHerunterladen

Fazit

Das Auslagern des asynchronen Task hat mir geholfen ein bereits bestehendes Projekt deutlich zu verschlanken. Man könnte nun statt einem Callback auch ein funktionales Interface nutzen jedoch geht dieses erst ab Java 8. 

Schreibe einen Kommentar Antworten abbrechen

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Fragen oder Feedback?

Du hast eine Idee, brauchst Hilfe oder möchtest Feedback loswerden?
Support-Ticket erstellen

Newsletter abonnieren

Bleib auf dem Laufenden: Erhalte regelmäßig Updates zu neuen Projekten, Tutorials und Tipps rund um Arduino, ESP32 und mehr – direkt in dein Postfach.

Jetzt Newsletter abonnieren

Unterstütze meinen Blog

Wenn dir meine Inhalte gefallen, freue ich mich über deine Unterstützung auf Tipeee.
So hilfst du mit, den Blog am Leben zu halten und neue Beiträge zu ermöglichen.

draeger-it.blog auf Tipeee unterstützen

Vielen Dank für deinen Support!
– Stefan Draeger

Kategorien

Tools

  • Unix-Zeitstempel-Rechner
  • ASCII Tabelle
  • Spannung, Strom, Widerstand und Leistung berechnen
  • Widerstandsrechner
  • 8×8 LED Matrix Tool
  • 8×16 LED Matrix Modul von Keyestudio
  • 16×16 LED Matrix – Generator

Links

Blogverzeichnis Bloggerei.de TopBlogs.de das Original - Blogverzeichnis | Blog Top Liste Blogverzeichnis trusted-blogs.com

Stefan Draeger
Königsberger Str. 13
38364 Schöningen
Tel.: 01778501273
E-Mail: info@draeger-it.blog

Folge mir auf

link zu Fabook
link zu LinkedIn
link zu YouTube
link zu TikTok
link zu Pinterest
link zu Instagram
  • Impressum
  • Datenschutzerklärung
  • Disclaimer
  • Cookie-Richtlinie (EU)
©2025 Technik Blog | Built using WordPress and Responsive Blogily theme by Superb
Cookie-Zustimmung verwalten
Wir verwenden Technologien wie Cookies, um Geräteinformationen zu speichern und/oder darauf zuzugreifen. Wir tun dies, um das Surferlebnis zu verbessern und um personalisierte Werbung anzuzeigen. Wenn Sie diesen Technologien zustimmen, können wir Daten wie das Surfverhalten oder eindeutige IDs auf dieser Website verarbeiten. Wenn Sie Ihre Zustimmung nicht erteilen oder zurückziehen, können bestimmte Funktionen beeinträchtigt werden.
Funktional Immer aktiv
Die technische Speicherung oder der Zugang ist unbedingt erforderlich für den rechtmäßigen Zweck, die Nutzung eines bestimmten Dienstes zu ermöglichen, der vom Teilnehmer oder Nutzer ausdrücklich gewünscht wird, oder für den alleinigen Zweck, die Übertragung einer Nachricht über ein elektronisches Kommunikationsnetz durchzuführen.
Vorlieben
Die technische Speicherung oder der Zugriff ist für den rechtmäßigen Zweck der Speicherung von Präferenzen erforderlich, die nicht vom Abonnenten oder Benutzer angefordert wurden.
Statistiken
Die technische Speicherung oder der Zugriff, der ausschließlich zu statistischen Zwecken erfolgt. Die technische Speicherung oder der Zugriff, der ausschließlich zu anonymen statistischen Zwecken verwendet wird. Ohne eine Vorladung, die freiwillige Zustimmung deines Internetdienstanbieters oder zusätzliche Aufzeichnungen von Dritten können die zu diesem Zweck gespeicherten oder abgerufenen Informationen allein in der Regel nicht dazu verwendet werden, dich zu identifizieren.
Marketing
Die technische Speicherung oder der Zugriff ist erforderlich, um Nutzerprofile zu erstellen, um Werbung zu versenden oder um den Nutzer auf einer Website oder über mehrere Websites hinweg zu ähnlichen Marketingzwecken zu verfolgen.
Optionen verwalten Dienste verwalten Verwalten von {vendor_count}-Lieferanten Lese mehr über diese Zwecke
Einstellungen anzeigen
{title} {title} {title}