AsyncTask blockiet den UI Thread

  • 10 Antworten
  • Letztes Antwortdatum
1

123thomas

Fortgeschrittenes Mitglied
44
Hallo Leute,

ich habe mal wieder ein kleines Problem, zu dem ich nicht wirklich was im Internet finden konnte.

Unzwar habe ich eine App, welche mit AsyncTask Daten aus einer MySQL Datenbank abgleicht. Nun dachte ich das die AsyncTasks völlig unabhängig vom UI Thread arbeiten, doch leider wird meine App Bedienung blockiert wenn der AsyncTask anfängt zu arbeiten.

Dann habe ich die Methode doinBackground durchgesehen, ob ich irgendwo runOnUiThread eingefügt habe, aber habe ich nicht.

Hier der Quelltext:

Aufruf des Asynctask:
Runnable r=new Runnable() {
public void run()
{
//Lade Daten aus SQL
new MyBaseAdapter().new LadeSQLDaten().execute();


Code:
class LadeSQLDaten extends AsyncTask<String,String,String> {


        @Override
        protected void onPreExecute()
        {
            super.onPreExecute();
            LadeEinträge = true;
        }

        @Override
        protected String doInBackground(String... params1) {


            try{
                if(isOnline()) {
                  
                    //Wir erstellt aber nicht benötigt daher leer
                    List<NameValuePair> params = new ArrayList<NameValuePair>();

                    //Lade Daten aus der Datenbank
                    jsonLade = jsonpraserLade.makeHttpRequest(URLLadeEinträge, "GET", params);
                    Log.d("LadeEinträge", "Verbindung" + "URL:" + URLLadeEinträge);


                    int success = 0;
                    success = jsonLade.getInt("success");
                    Log.d("LadeEinträge", "success:" + String.valueOf(success));

                    //Wenn erfolgreich gemeldet wird
                    if (success == 1) {
                        //Hole die Produkte
                        JSONArray products = null;
                        products = jsonLade.getJSONArray("products");

                        for (int i = 0; i < products.length(); i++) {
                            JSONObject lade = null;

                            lade = products.getJSONObject(i);


                            //Setzte Eintrag
                            Log.d("LadeEinträge", String.valueOf(lade.getInt("Position")) + " " + lade.getString("Gegenstand") + " " + lade.getString("Anzahl")+ " " + lade.getString("Strike"));
                            //if(String.valueOf(lade.getInt("Position")) != "null")

                            boolean tempStrike = false;
                            if(lade.getString("Strike").equals("true"))
                            {
                                tempStrike = true;
                                Log.d("Streiche","Position|" + lade.getInt("Position") + "||"+ i);
                            }
                            setEintrag(String.valueOf(lade.getInt("Position")), lade.getString("Gegenstand"), lade.getString("Anzahl"), tempStrike);
                            //else
                            //Log.d("LadeEinträge", "Position null");
                        }
                        LadeEinträgeerfolgreich = true;
                        //showToast("Einträge erfolgreich geladen");


                        //Kein Fehler Verbindung grün auslösen
                        if (listener != null)
                        {
                            Log.d("OnStatusChange", "grün");
                            listener.onStatusChange("grün");
                        }

                        Log.d("LadeEinträge", "Angelegt");
                    } else {
                        //K für Kein Eintrag gefunden
                        if (jsonLade.getString("message").startsWith("K")) {
                            //löscheAlles();
                            //showToast("Kein Eintrag in der Online Datenbank gefunden");
                            Log.d("LadeEinträge", "löscheAlles()");

                            //Kein Fehler Verbindung grün auslösen
                            if (listener != null) {
                                listener.onStatusChange("grün");
                            }

                            LadeEinträgeKeinEintrag = true;
                        } else {
                            //Fehler Verbindung rot auslösen
                            if (listener != null) {
                                listener.onStatusChange("rot");
                            }
                        }

                        Log.d("LadeEinträge", "Failed success: " + jsonLade.getString("message"));
                        LadeEinträgeerfolgreich = false;

                    }
                }
                else
                {
                    Log.d("LadeSQLDaten", "Keine Verbindung");
                    //Keine Verbindung
                    if(listener != null)
                    {
                        listener.onStatusChange("orange");
                    }
                }
           }
            catch(Exception e) {
                Log.e("LadeEinträge", "Failed: " + e.getMessage());

                //Fehler Verbindung rot auslösen
                if(listener != null)
                {
                    listener.onStatusChange("rot");
                }

                LadeEinträgeerfolgreich = false;
            }

            return null;
        }

        protected void onPostExecute(String hhh)
        {
            //listview aktualisieren
            updateListonChange();

            LadeEinträge = false;
        }

    }

Ist der AsyncTask kein unabhängiger Thread oder habe ich einen Fehler gemacht?

Mit freundlichen Grüßen
Thomas
 
123thomas schrieb:
Runnable r=new Runnable() {
public void run()
{...}

Kurze frage, was machst du mit den runnable? Packst du den Asynctask in einen zweiten Thread. Das hört sich nicht gar nicht gut an.

Zu deiner Frage: Der AsyncTask ist nur für kurze Threads gedacht. Für alles, was länger dauert, brauchst du einen "richtigen" Thread, sonst hast du Einbrüche in der Performance.
 
Danke für die Antwort.

Ich rufe ein Handler alle 20 Sekunden auf um die App mit dem Internet zu synchronisieren. Der Handler führt dann die Runnable aus. Daher die Runnable.

Ist das ein falscher Ansatz muss ich alles auf Threads umstricken?
 
Mit einen Handler.post() ist das okay. Dein Problem ist nicht der AsyncTask, sondern die Methoden und Objekte, welche die dieser in der Methode doBackground() benutzt.

Ich gehe mal davon aus, das diese zur Activity gehören. D.h. dein AsyncTask ruft Methoden auf, welche im UiThread ausgeführt werden. D.h. der UiThread wartet so lange, bis er den Code abgearbeitet hat.

Kapsele deine Datenbankabfragen in einer Klasse, initialisiere die Klasse als Objekt in deinem AsyncTask, und greife so direkt auf Datenbank zu. Für die Übergabe der Daten benutzt du dann die Schnittstellen des AsyncTask (z.B. onPostExecute (Result result)).
 
markus.tullius schrieb:
Ich gehe mal davon aus, das diese zur Activity gehören. D.h. dein AsyncTask ruft Methoden auf, welche im UiThread ausgeführt werden. D.h. der UiThread wartet so lange, bis er den Code abgearbeitet hat.

Der Aufruf kommt doch aber aus einem anderem Thread, dem von AsyncTask. Er würde zur Laufzeit einen Fehler kriegen wenn er versucht Methoden die normalerweise im UIThread ablaufen müssen aus einem anderen Thread zu rufen...
 
Ich werde es mal in einem Thread umsetzen und ausprobieren aber es dann stabiler läuft und dann berichten.
 
Also ich habe nun die AsyncTasks in Threads umgestetzt. Es läuft immer noch nicht flüssig. Ich bin langsam mit meinem Latein am Ende.

Kann nochmal jemand kurz und knapp erklären was genau Runnable und Handler sind? Die Threads kenne ich aus der C# Programmierung. Nur die Runnable und Handler sind mit neu und die im Internet zu findenden Informationen sind nicht die passenden. Also ich meine ob Runnnable und Handler abhängen vom UI Thread oder ob diese extern laufen wie ein Thread.

Gibts es noch eine Angabe die dem Thread sagt, wenn die CPU zu sehr belastet wird, dass dann der Thread wartet bis Kapazitäten frei sind?

Danke
 
Hallo Thomas,

zeige mal dein aktuellen Code. Ich hoffe du greifst nicht mehr vom Thread auf Methoden zu, die in der Activity definiert wurden.
 
Hallo danke für die Antwort,

ich entschuldige mich jetzt schon mal für den Code. Da ich oft erfahren musste durch meine Hobby Programmierung, das der Code nicht immer sauber ist und freue mich für Verbesserungshinweise.
Als Beispiel nehme ich den Aufruf beim starten der App:

Aufruf in der MainActivity:
Code:
Log.i("Hole ", "Einträge");
        Handler AbfragebeimStarten = new Handler();
        AbfragebeimStarten.post(new Runnable() {

            @Override
            public void run() {
                myBaseAdapter.getSQLEinträge();
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                myBaseAdapter.updateListonChange();
            }
           
        });


myBaseAdapter.getSQLEinträge:
Code:
public static boolean getSQLEinträge()
    {
        löscheAlles();
        LadeEinträgeerfolgreich = false;
        Runnable r=new Runnable() {
            public void run()
            {
                //Lade Daten aus SQL
                //new MyBaseAdapter().new LadeSQLDaten().execute();
                com.example.einkaufszettel.LadeSQLDaten Daten = new com.example.einkaufszettel.LadeSQLDaten();
               
                //Event erstellt, das den Status weiterreicht an die MainActivity
                Daten.setonStatusChangeListener(new com.example.einkaufszettel.LadeSQLDaten.LadeSQLDatenListener() {
                    @Override
                    public void onStatusChange(String title) {
                        boolean temp = true;
                        //Log.d("OnStatusChange","Der Staus wird geändet nach " + title);

                        if(title.equals("rot"))
                        {
                            if (listener != null)
                            {
                                listener.onStatusChange("rot");
                            }
                            temp = false;
                        }
                        else if(title.equals("grün"))
                        {
                            if (listener != null) {
                                listener.onStatusChange("grün");
                            }
                            temp = false;
                        }
                        else if(title.equals("orange"))
                        {
                            if (listener != null)
                            {
                                listener.onStatusChange("orange");
                            }
                            temp = false;
                        }

                        if (temp) {
                            Log.e("OnStatusChange", "falsche Übergabe");
                        }
                    }
                });
               
                //Thread starten
                Daten.start();

                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    //
                    e.printStackTrace();
                }
                for(int i = 0; i<8;i++)
               
                    //prüfen ob das Einträge laden schon abgeschlossen ist
                    if(!LadeEinträgeerfolgreich)
                    {
                        cLängeLade++;
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
                //Wenn immer noch nicht abgeschlossen dann die Zahl auf 100 Auffüllen
                if(!LadeEinträgeerfolgreich)
                {
                    cLängeLade += 92;
                }

                //Wenn der listener erstellt wurde dann Event anstoßen, das die Liste aktualisiert
                if(listener != null)
                {
                    listener.onStrikeChange();
                    Log.d("Streiche","Streiche update");
                }
            }
        };
        r.run();
        return LadeEinträgeerfolgreich;
    }

com.example.einkaufszettel.LadeSQLDaten
Code:
package com.example.einkaufszettel;

import android.util.Log;

import org.apache.http.NameValuePair;
import org.json.JSONArray;
import org.json.JSONObject;

import java.util.ArrayList;
import java.util.List;

/**
* Created by Thomas on 15.11.2015.
*/
public class LadeSQLDaten extends Thread {
    private MyBaseAdapter myBaseAdapter = new MyBaseAdapter();
    private LadeSQLDatenListener listener;

    //Variablen für LadeEinträge
    private static JSONParser jsonpraserLade = new JSONParser();
    private static JSONObject jsonLade;
    private static String URLLadeEinträge = "******" + MyBaseAdapter.Datenbank + "/get_all_products.php";
    //public static boolean LadeEinträge = false;
    private static boolean LadeEinträgeerfolgreich = false;
    //public static int cLängeLade = 0;
    //public static boolean LadeEinträgeKeinEintrag = false;

    public LadeSQLDaten()
    {

    }

    public void run()
    {
        myBaseAdapter.LadeEinträge = true;

        try{
            //Prüfen ob Onlineverbindung vorhanden
            if(myBaseAdapter.isOnline()) {

                //Wir erstellt aber nicht benötigt daher leer
                List<NameValuePair> params1 = new ArrayList<NameValuePair>();

                //Lade Daten aus der Datenbank
                jsonLade = jsonpraserLade.makeHttpRequest(URLLadeEinträge, "GET", params1);
                Log.d("LadeEinträge", "Verbindung" + "URL:" + URLLadeEinträge);


                int success = 0;
                success = jsonLade.getInt("success");
                Log.d("LadeEinträge", "success:" + String.valueOf(success));

                //Wenn erfolgreich gemeldet wird
                if (success == 1) {
                    //Hole die Produkte
                    JSONArray products = null;
                    products = jsonLade.getJSONArray("products");

                    for (int i = 0; i < products.length(); i++) {
                        JSONObject lade = null;

                        lade = products.getJSONObject(i);


                        //Setzte Eintrag
                        Log.d("LadeEinträge", String.valueOf(lade.getInt("Position")) + " " + lade.getString("Gegenstand") + " " + lade.getString("Anzahl")+ " " + lade.getString("Strike"));
                        //if(String.valueOf(lade.getInt("Position")) != "null")

                        boolean tempStrike = false;
                        if(lade.getString("Strike").equals("true"))
                        {
                            tempStrike = true;
                            Log.d("Streiche","Position|" + lade.getInt("Position") + "||"+ i);
                        }
                        myBaseAdapter.setEintrag(String.valueOf(lade.getInt("Position")), lade.getString("Gegenstand"), lade.getString("Anzahl"), tempStrike);
                        //else
                        //Log.d("LadeEinträge", "Position null");
                    }
                    LadeEinträgeerfolgreich = true;
                    //showToast("Einträge erfolgreich geladen");


                    //Kein Fehler Verbindung grün auslösen
                    if (listener != null)
                    {
                        Log.d("OnStatusChange", "grün");
                        listener.onStatusChange("grün");
                    }

                    Log.d("LadeEinträge", "Angelegt");
                } else {
                    //K für Kein Eintrag gefunden
                    if (jsonLade.getString("message").startsWith("K")) {
                        //löscheAlles();
                        //showToast("Kein Eintrag in der Online Datenbank gefunden");
                        Log.d("LadeEinträge", "löscheAlles()");

                        //Kein Fehler Verbindung grün auslösen
                        if (listener != null) {
                            listener.onStatusChange("grün");
                        }

                        myBaseAdapter.LadeEinträgeKeinEintrag = true;
                    } else {
                        //Fehler Verbindung rot auslösen
                        if (listener != null) {
                            listener.onStatusChange("rot");
                        }
                    }

                    Log.d("LadeEinträge", "Failed success: " + jsonLade.getString("message"));
                    LadeEinträgeerfolgreich = false;

                }
            }
            else
            {
                Log.d("LadeSQLDaten", "Keine Verbindung");
                //Keine Verbindung
                if(listener != null)
                {
                    listener.onStatusChange("orange");
                }
            }
        }
        catch(Exception e) {
            Log.e("LadeEinträge", "Failed: " + e.getMessage());

            //Fehler Verbindung rot auslösen
            if(listener != null)
            {
                listener.onStatusChange("rot");
            }

            LadeEinträgeerfolgreich = false;
        }

        //listview aktualisieren
        myBaseAdapter.updateListonChange();

        myBaseAdapter.LadeEinträge = false;
        this.interrupt();
    }
    public interface LadeSQLDatenListener {


        //Wenn der Staus geändert wird
        public void onStatusChange(String title);
    }

    public synchronized void setonStatusChangeListener(LadeSQLDatenListener list)
    {
        Log.d("listner", String.valueOf(list));
        this.listener = list;
    }

}
 
"A Handler allows you to send and process Message and Runnable objects associated with a thread's MessageQueue. Each Handler instance is associated with a single thread and that thread's message queue. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it -- from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue."

Kurz: Du erstellst da einen Handler der Runnables an die MessageQueue der Activity sendet...du Blockierst wieder den UIThread damit.

Datenbankabfragen gehören aber nciht dahin und erst Recht nicht ein Sleep() auf dem MAinThread der Activity!!!

Versuch mal das hier so zu machen:

Java:
Log.i("Hole ", "Einträge");
        new Thread() {

            @Override
            public void run() {
                myBaseAdapter.getSQLEinträge();
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                myBaseAdapter.updateListonChange();
            }
         
        }.start();
 
Zurück
Oben Unten