Android Ladezeiten verringern

MARJAVA

Aktives Mitglied
Hallo zusammen,

meine App, eine Anwendung die auf eine Fimdatenbank zugreift("TMDB") zeigt auf fast jedem neuen Fragment bzw. Activity Daten aus der Datenbank, Text, Bilder, Fotos.

Das alles muss in dem Moment wenn auf die activity oder das Fragment zugegriffen wird geladen werden. Es werden JSON-Daten geladen und verarbeitet. Auch wenn die Seite bereits geladen wurde und nur das Tablayout hin und hergeschalten wurde, muss die Information neu geladen werden.
Das sind ziemlich lange Ladezeiten, die ich mit meinen AsyncTasks brauche. Für vereinzelte Ladezeiten nutze ich eine Progressbar, doch das kann ich nicht überall einbauen. Bei vergleichbaren Apps dauert das nicht so lange, allerdings kenne ich den Code dieser nicht.

Hat jemand eine Idee, wie ich die Ladezeiten reduzieren kann? Ist es sinnvoll, die geladenen Daten in eine SQLIte-Datenbank zu schreiben um sie bei bedarf, gleich aus der App-Datenbank zu lesen. Das sollte schneller gehen als übers Internet. Gibt es andere Methode um die Daten schneller griffbereit zu haben.

Beispiel: Ich lade ein Gridview mit Filmen, hinter dem sich bei jedem Feld eine Filmid versteckt. Beim Klick auf das Feld läd er die Filmdaten zur entsprechenden ID. Ich könnte ja alle Daten bereits vorm Klick auf das Feld im Hintergrund geladen haben um die Verzögerung zur vermeiden. Allerdings sind das dann ob ich die Daten anschaue oder nicht massiv viel Datenvolumen, was hier verbraucht wird. Gibt es andere Methoden bereits geladene Daten im Arbeitsspeicher vorzuhalten.
Hat dazu vielleicht jemand eine Idee oder Erfahrungen gesammelt?
 

mrBrown

Super-Moderator
Mitarbeiter
Ist es sinnvoll, die geladenen Daten in eine SQLIte-Datenbank zu schreiben um sie bei bedarf, gleich aus der App-Datenbank zu lesen. Das sollte schneller gehen als übers Internet. Gibt es andere Methode um die Daten schneller griffbereit zu haben.
Die Daten irgendwo cachen, je nach Bedarf entweder in einer Datenbank, die dann über mehrere Starts erhalten bleibt, oder nur "In Memory".
Im Idealfall kombiniert mit Cache-Direktiven von Server-Seite.

Ich könnte ja alle Daten bereits vorm Klick auf das Feld im Hintergrund geladen haben um die Verzögerung zur vermeiden. Allerdings sind das dann ob ich die Daten anschaue oder nicht massiv viel Datenvolumen, was hier verbraucht wird.
Wenn es sich nur im Daten handelt (und keine Bilder, Videos, ...) ist das Volumen meistens verschmerzbar - da muss man abwägen zwischen ein paar Daten zu viel laden und auf Daten warten.



Wie genau das bei dir umsetzbar ist, hängt von deiner Anwendung ab. Nutzt du irgendein Architekturmuster, zB Flux?
 

Thallius

Top Contributor
Also ich verstehe gerade nicht warum das Laden der Filmdaten für eine id länger als ein paar Millisekunden dauern sollte? Wenn da viele Bilder bei sind, dann lade halt die Bilder asynchron und stelle die Seite zuerst mit Platzhaltern für die Bilder da und lade die Bilder dann nach und nach in die Seite.
 

mrBrown

Super-Moderator
Mitarbeiter
Also ich verstehe gerade nicht warum das Laden der Filmdaten für eine id länger als ein paar Millisekunden dauern sollte?
Weil allein die RTT länger als "ein paar Millisekunden" dauert, dazu noch die Zeit, die der Server braucht, und man ist schnell bei >200-300ms.

Dann noch ein paar sequentielle Requests und man ist im Sekundenbereich.
 

MARJAVA

Aktives Mitglied
Ich habe mal ne Hausnummer. 80 Datensätze in 80 Asynctasks brauchen 30 min bis sie ausgelesen sind. Das ist doch nicht normal, irgendeine idee, wie ich näher eingrenzen könnte woran es liegt?
 

mrBrown

Super-Moderator
Mitarbeiter
ganz generell Logging oder Debugging.

So aus dem Stehgreif kann man da wenig sagen, vermutlich hast du irgendwas drin, welches das so langsam werden lässt.
 

MARJAVA

Aktives Mitglied
generelles Logging zeigt mir das an.

Meine Vermutung ist, dass die Parallelen Asynctasks sich gegenseitig verlangsamen, da alle 80 Asynctasks aus einer for-schleife heraus gestartet werden. Die Downloadleitung wird sicher am Anschlag sein.

Java:
            for(int i=0; i<filmIds.size();i++){
                url="https://api.themoviedb.org/3/movie/"+ filmIds.get(i)+"?api_key=c4ee4a6a3f605d3c3754e368a4885414&append_to_response=credits,videos,images";

                task=new DownloadTaskDatenFilm(position,context,fragment,i+1);
                task.execute(url);
            }

Nachdem der eine Asynctask für die Ids fertig ist, laufen die 80 Asynctasks parallel aus einer For schleife. Vielleicht wäre es besser die Asynctaskmethode für die 80 Asynctasks nicht parallel, sondern rekursiv, nacheinander ablaufen zu lassen?
 
Zuletzt bearbeitet:

mrBrown

Super-Moderator
Mitarbeiter
80 parallele Downloads sollten nicht viel länger als 80 Downlaods nacheinander dauern. Vermutlich wäre da aber generell ein ExecutorService mit begrenztem ThreadPool sinnvoll.


Aber wie gesagt: mit den spärlichen Informationen kann man nur sagen: ist halt so.
 

Thallius

Top Contributor
Also ich würde da mit einer Download queue arbeiten und es sequenziell abarbeiten. Das hat den Vorteil, dass wenn der User in der Zwischenzeit die page wechselt und die restlichen Daten gar nicht mehr geladen werden müssen du nicht 80 Threads stoppen Musst sondern einfach die queue leer machst.
 

MARJAVA

Aktives Mitglied
@Thallius Ist bei deinem download queue die Klasse Queue gemeint?
@mrBrown was meinst du mir spärlichen Infos, welche Infos bräuchtest du denn?

Nach dem Umbau meiner App, startet jeder asyncTask nach beendigung den nächsten AsyncTask. Die verbrauchte Zeit liegt aber immer noch bei ca 25-30min pro 80 Datensätze inklusive Bildern herunterladen.
Nun habe ich ein weiteres Problem, denn der Download, der aus dem GUI-Fragment heraus geladen wird, verlangsamt die gesamte GUI. Es wäre sicher sinnvoll, die Downloads von der GUI zu trennen oder? Wäre da ein zweiter Thread für den alleinigen Download unabdingbar?
 

mrBrown

Super-Moderator
Mitarbeiter
@Thallius Ist bei deinem download queue die Klasse Queue gemeint?
Queue ist ein Interface, und ja, das bietet passende Implementierungen dafür.
Du machst es dir aber einfacher, wenn du einen ExecutorService dafür nutzt (ich gehe mal nicht davon aus, das die unter Android nicht verfügbar sind?), damit bekommst du sowohl die Queue als auch das Threading geschenkt, zusätzlich zu anderem.

@mrBrown was meinst du mir spärlichen Infos, welche Infos bräuchtest du denn?
Naja, deine Infos bisher sind: 80 Downloads mit AsyncTask sind langsam - da kann man nicht wirklich was zu sagen ;)

Nach dem Umbau meiner App, startet jeder asyncTask nach beendigung den nächsten AsyncTask. Die verbrauchte Zeit liegt aber immer noch bei ca 25-30min pro 80 Datensätze inklusive Bildern herunterladen.
also 20-30sek pro Datensatz?

Wie groß sind die Datensätze? Wie genau lädst du das alles runter (ein Datensatz scheinen ja mehrere Dateien zu sein)? Auf was für einem Gerät mit welche Internetverbindung testest du?

Nun habe ich ein weiteres Problem, denn der Download, der aus dem GUI-Fragment heraus geladen wird, verlangsamt die gesamte GUI. Es wäre sicher sinnvoll, die Downloads von der GUI zu trennen oder? Wäre da ein zweiter Thread für den alleinigen Download unabdingbar?

Ein AsyncTask läuft bereits in eigenem Thread.

Wird die GUI nur langsamer oder blockiert sie? Manipulierst du aus dem Task heraus die GUI?
 

MARJAVA

Aktives Mitglied
Zitat von MARJAVA:
@Thallius Ist bei deinem download queue die Klasse Queue gemeint?
Queue ist ein Interface, und ja, das bietet passende Implementierungen dafür.
Du machst es dir aber einfacher, wenn du einen ExecutorService dafür nutzt (ich gehe mal nicht davon aus, das die unter Android nicht verfügbar sind?), damit bekommst du sowohl die Queue als auch das Threading geschenkt, zusätzlich zu anderem.
Wird durch den ExecutorService der AsyncTask ersetzt? Hast du Links etc. über diesen Weg?


Zitat von MARJAVA:
Nach dem Umbau meiner App, startet jeder asyncTask nach beendigung den nächsten AsyncTask. Die verbrauchte Zeit liegt aber immer noch bei ca 25-30min pro 80 Datensätze inklusive Bildern herunterladen.
also 20-30sek pro Datensatz?

Wie groß sind die Datensätze? Wie genau lädst du das alles runter (ein Datensatz scheinen ja mehrere Dateien zu sein)? Auf was für einem Gerät mit welche Internetverbindung testest du?

Ein Datensatz besteht aus ca 10 Variablen, wobei nur eine Variable mehr als 3 Strings hat. Nix besonderes also.
Meine Internetverbindung liegt bei 100 MBit Download, 12mBit Upload, und 19ms Ping. Die scheint nicht das Problem zu sein.



Zitat von MARJAVA:
Nun habe ich ein weiteres Problem, denn der Download, der aus dem GUI-Fragment heraus geladen wird, verlangsamt die gesamte GUI. Es wäre sicher sinnvoll, die Downloads von der GUI zu trennen oder? Wäre da ein zweiter Thread für den alleinigen Download unabdingbar?
Ein AsyncTask läuft bereits in eigenem Thread.

Wird die GUI nur langsamer oder blockiert sie? Manipulierst du aus dem Task heraus die GUI?

Die GUI blockiert teilweise, da ich sie aus dem Task wieder angesteuert habe. Ich habe jetzt die Downloads von der GUI getrennt. Das wird in Ausnahmen aber nicht gehen.
 

mrBrown

Super-Moderator
Mitarbeiter
Wird durch den ExecutorService der AsyncTask ersetzt? Hast du Links etc. über diesen Weg?
In manchen Bereichen lässt sich der ersetzen, ja. Hier gibts zb was dazu: https://www.baeldung.com/java-executor-service-tutorial

Ein Datensatz besteht aus ca 10 Variablen, wobei nur eine Variable mehr als 3 Strings hat. Nix besonderes also.
Meine Internetverbindung liegt bei 100 MBit Download, 12mBit Upload, und 19ms Ping. Die scheint nicht das Problem zu sein.
Hast du nicht kurz vorher noch was von Bildern gesagt?
 

MARJAVA

Aktives Mitglied
Zitat von MARJAVA:
Ein Datensatz besteht aus ca 10 Variablen, wobei nur eine Variable mehr als 3 Strings hat. Nix besonderes also.
Meine Internetverbindung liegt bei 100 MBit Download, 12mBit Upload, und 19ms Ping. Die scheint nicht das Problem zu sein.
Hast du nicht kurz vorher noch was von Bildern gesagt?

Die in Summe aller Downloads 160 Datensaetze sind nur Textdaten, darunter Links in Textform. Das Laden der Bilder geschieht in separaten AsyncTasks, die für mich gefühlt aber schneller ablaufen, als die Datensaetze mit den 10 Variablen.
Die 160 Datensätze haben zum Download ca 28 min gedauert, das ging schon mal viel fixer als bisher, ist aber noch immer zu langsam.
 

mrBrown

Super-Moderator
Mitarbeiter
Die in Summe aller Downloads 160 Datensaetze sind nur Textdaten, darunter Links in Textform.
Waren das nicht grad noch 80? :p

Die 160 Datensätze haben zum Download ca 28 min gedauert, das ging schon mal viel fixer als bisher, ist aber noch immer zu langsam.
Wenn das laden einer Textdatei knapp 10sek dauert, läuft da in jedem Fall was falsch. Völlig unabhängig davon, ob sequentiell oder parallel.

Kannst du Code dazu zeigen?
 

MARJAVA

Aktives Mitglied
Waren das nicht grad noch 80? :p
Ich lese für die sparte Film 4 verschiedene serveranfragen aus. zuerst waren es nur eine categorie deshalb erst 80, jetzt 160.

Das Laden der Daten geschieht innerhalb von zwei Klassen, die wie folgt aussehen. Nachbilden wird allerdings schwer, da ich meine API-Nummer rausgelöscht habe. Wer dennoch den Code nachbilden will, muss sich eine API-Nummer besorgen, bei der entsprechenden Seite.
Aber für einen Eindruck reicht das.
Java:
package de.markusc.movieplanet;

import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;
import android.view.View;
import android.widget.GridView;
import android.widget.ProgressBar;

import java.util.ArrayList;

import static android.content.Context.MODE_PRIVATE;

public class DataLoaderFilm {

    private Context context;
    private ContentFragmentFilm fragment;
    private ArrayList<Integer> loadFilms=new ArrayList<Integer>();
    private ArrayList<Integer> loadFilmsType=new ArrayList<Integer>();
    private int zaehler;

    public DataLoaderFilm(Context context,ArrayList<Integer> loadFilms,ArrayList<Integer> loadFilmsType){
        this.loadFilms=loadFilms;
        this.loadFilmsType=loadFilmsType;
        this.context=context;
        zaehler=0;

        //loadFilms();
    }
    public void startLoadingData(){


        DownloadTaskDatenFilm task=null;
        String url="";
        if(zaehler==0){

            Log.i("log zaehler","zaehler: "+(zaehler+1)+" von "+(loadFilms.size()+zaehler+1));
            url="https://api.themoviedb.org/3/movie/"+ loadFilms.get(0)+"?API-Nummer&append_to_response=credits,videos,images";
            zaehler++;

            task=new DownloadTaskDatenFilm(loadFilmsType.get(0),this,context);
            task.execute(url);
            loadFilms.remove(0);
        }else if((loadFilms.size()!=0)){
            zaehler++;

            Log.i("log zaehler","zaehler: "+(zaehler+1)+" von "+(loadFilms.size()+zaehler+1));

            url="https://api.themoviedb.org/3/movie/"+ loadFilms.get(0)+"?API-Nummer&append_to_response=credits,videos,images";

            task=new DownloadTaskDatenFilm(loadFilmsType.get(0),this,context);
            task.execute(url);
            loadFilms.remove(0);

        }else{
            Log.i("log Forschritt","Download abgeschlossen");
        }



    }



}

Java:
package de.markusc.movieplanet;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.os.AsyncTask;
import android.util.Log;

import com.google.gson.Gson;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;

import static android.content.Context.MODE_PRIVATE;

public class DownloadTaskDatenFilm extends AsyncTask<String,Void, String> {

    private int position;
    private ArrayList<String> characterListe=new ArrayList<String>();
    private ArrayList<String> schauspielerListe=new ArrayList<String>();
    private ArrayList<String> schauspielerbilder=new ArrayList<String>();
    private SQLiteDatabase myDatabase;
    private DataLoaderFilm dataLoaderFilm;

    public DownloadTaskDatenFilm(int position,DataLoaderFilm dataLoaderFilm,Context context){
        this.position=position;
        this.dataLoaderFilm=dataLoaderFilm;
        myDatabase=context.openOrCreateDatabase("filmdatenbank",MODE_PRIVATE,null);
    }
    @Override
    protected String doInBackground(String... urls) {
        String result= new String();
        URL url;
        HttpURLConnection urlConnection=null;
        try{

                url = new URL(urls[0]);
                urlConnection = (HttpURLConnection) url.openConnection();
                InputStream inputStream = urlConnection.getInputStream();
                InputStreamReader reader = new InputStreamReader(inputStream);
                int data = reader.read();
                String string="";
                while (data != -1) {
                    char current = (char) data;
                     string+= current;
                    data = reader.read();
                }
                result=string;

            return result;
        } catch (MalformedURLException e) {
            e.printStackTrace();
            return null;
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }

    }


    protected void onPostExecute(String s) {
        super.onPostExecute(s);



        try {


            JSONObject jsonObject = new JSONObject(s);

            String creditResult = jsonObject.getString("credits");

            JSONObject jsonObject1=new JSONObject(creditResult);
            String castResult=jsonObject1.getString("cast");
            String page1Result = jsonObject.getString("genres");
            String releaseD = jsonObject.getString("release_date");
            int idnumber = Integer.parseInt(jsonObject.getString("id"));
            String overview = jsonObject.getString("overview");
            overview=overview.replace("\"","").replace("'","");
            String filmFoto = jsonObject.getString("poster_path");
            int rates=jsonObject.getInt("vote_average");
            int budget = jsonObject.getInt("budget");
            int runtime = jsonObject.getInt("runtime");
            String title = jsonObject.getString("original_title");
            title=title.replace("\"","").replace("'","");
            JSONArray jsonArray = new JSONArray(page1Result);
            JSONArray jsonArray1=new JSONArray(castResult);
            for (int i = 0; i < jsonArray1.length(); i++) {
                JSONObject characterObject = jsonArray1.getJSONObject(i);
                String character=characterObject.getString("character").replace("\"","").replace("'","");
                characterListe.add(character);
                String actor=characterObject.getString("name").replace("\"","").replace("'","");
                schauspielerListe.add(actor);
                String bild=characterObject.getString("profile_path");
                schauspielerbilder.add("https://image.tmdb.org/t/p/w500"+bild);

            }




            String genre="";
            for (int i = 0; i < jsonArray.length(); i++) {
                JSONObject film = jsonArray.getJSONObject(i);
                genre += film.getString("name") + ", ";


            }


            Gson gson1 = new Gson();
            String arrayListChara= gson1.toJson(characterListe);

            Gson gson2 = new Gson();
            String arrayListActor= gson2.toJson(schauspielerListe);

            Gson gson3 = new Gson();
            String arrayListBilder= gson3.toJson(schauspielerbilder);


            String fotoUrl="https://image.tmdb.org/t/p/w500"+filmFoto;




                myDatabase.execSQL("INSERT INTO filme(id,type,title,releasedate,genre,overview,voteaverage,budget,runtime,character,name,profilepath,posterpath) VALUES ('"+idnumber+"','"+position+"','"+title+"','"+releaseD+"','"+genre+"','"+overview+"','"+rates+"',"+budget+","+runtime+",'"+arrayListChara+"','"+arrayListActor+"','"+arrayListBilder+"','"+fotoUrl+"')");




        } catch (JSONException e) {
            e.printStackTrace();
        }
            dataLoaderFilm.startLoadingData();
            this.cancel(true);

    }
}
 

MARJAVA

Aktives Mitglied
ist eine gängige Methode, die einem in Java-Kursen und Handbüchern über den Weg läuft. Was wäre denn eine gute Alternative, die das ganze schneller macht?
 

MARJAVA

Aktives Mitglied
Mit dem folgenden Code läuft der Download wirklich schnell.
Java:
package de.markusc.movieplanet;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.os.AsyncTask;
import android.os.Build;
import android.support.annotation.RequiresApi;

import com.google.gson.Gson;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.logging.Filter;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import de.markusc.movieplanet.DataLoaderFilm;

import static android.content.Context.MODE_PRIVATE;

public class DownloadTaskDatenTV extends AsyncTask<String,Void, String> {

    private ArrayList<Integer> tvIds=new ArrayList<Integer>();
    private String tabName;
    private int position;
    private ArrayList<String> characterListe=new ArrayList<String>();
    private ArrayList<String> schauspielerListe=new ArrayList<String>();
    private ArrayList<String> schauspielerbilder=new ArrayList<String>();
    private SQLiteDatabase myDatabase;
    private DataLoaderTV dataLoaderTV;
    private int numberOfId;

    public DownloadTaskDatenTV(int position, DataLoaderTV dataLoaderTV, Context context){
        this.position=position;
        this.dataLoaderTV=dataLoaderTV;
        myDatabase=context.openOrCreateDatabase("filmdatenbank",MODE_PRIVATE,null);
    }
    @RequiresApi(api = Build.VERSION_CODES.N)
    @Override
    protected String doInBackground(String... urls) {
        String result= new String();
        URL url;
        HttpURLConnection urlConnection=null;
        try{

                url = new URL(urls[0]);
                urlConnection = (HttpURLConnection) url.openConnection();
                InputStream inputStream = urlConnection.getInputStream();

                BufferedReader br=new BufferedReader(new InputStreamReader(inputStream,Charset.defaultCharset()));
                result=br.lines().collect(Collectors.joining(System.lineSeparator()));


            return result;
        } catch (MalformedURLException e) {
            e.printStackTrace();
            return null;
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }

    }


    protected void onPostExecute(String s) {
        super.onPostExecute(s);



        try {


            JSONObject jsonObject = new JSONObject(s);

            String creditResult = jsonObject.getString("credits");

            JSONObject jsonObject1=new JSONObject(creditResult);
            String castResult=jsonObject1.getString("cast");
            String page1Result = jsonObject.getString("genres");
            String firstairdate = jsonObject.getString("first_air_date");
            int idnumber = Integer.parseInt(jsonObject.getString("id"));
            String overview = jsonObject.getString("overview");
            overview=overview.replace("\"","").replace("'","");
            String filmFoto = jsonObject.getString("poster_path");
            int rates=jsonObject.getInt("vote_average");
            int numberofseasons = jsonObject.getInt("number_of_seasons");
            int numberofepisodes = jsonObject.getInt("number_of_episodes");
            String title = jsonObject.getString("original_name");
            title=title.replace("\"","").replace("'","");
            JSONArray jsonArray = new JSONArray(page1Result);
            JSONArray jsonArray1=new JSONArray(castResult);
            for (int i = 0; i < jsonArray1.length(); i++) {
                JSONObject characterObject = jsonArray1.getJSONObject(i);
                String character=characterObject.getString("character").replace("\"","").replace("'","");
                characterListe.add(character);
                String actor=characterObject.getString("name").replace("\"","").replace("'","");
                schauspielerListe.add(actor);
                String bild=characterObject.getString("profile_path");
                schauspielerbilder.add("https://image.tmdb.org/t/p/w500"+bild);

            }




            String genre="";
            for (int i = 0; i < jsonArray.length(); i++) {
                JSONObject film = jsonArray.getJSONObject(i);
                genre += film.getString("name") + ", ";


            }


            Gson gson1 = new Gson();
            String arrayListChara= gson1.toJson(characterListe);

            Gson gson2 = new Gson();
            String arrayListActor= gson2.toJson(schauspielerListe);

            Gson gson3 = new Gson();
            String arrayListBilder= gson3.toJson(schauspielerbilder);


            String fotoUrl="https://image.tmdb.org/t/p/w500"+filmFoto;




                myDatabase.execSQL("INSERT INTO tv(id,type,title,firstairdate,genre,overview,voteaverage,numberofseasons,numberofepisodes,character,name,profilepath,posterpath) VALUES ('"+idnumber+"','"+position+"','"+title+"','"+firstairdate+"','"+genre+"','"+overview+"','"+rates+"',"+numberofseasons+","+numberofepisodes+",'"+arrayListChara+"','"+arrayListActor+"','"+arrayListBilder+"','"+fotoUrl+"')");




        } catch (JSONException e) {
            e.printStackTrace();
        }
            dataLoaderTV.startLoadingData();
            this.cancel(true);

    }
}

Allerdings kommt es nun zu Abstürzen des Downloads zu unterschiedlichen Zeitpunkten, ohne Muster.
Die Abstürze werden durcch eine FileNotFoundException ausgelöst. Was der Grund für den Leerstring ist kann ich mir nicht wirklich herleiten.
Java:
2019-01-17 16:36:35.609 7366-7422/de.markusc.movieplanet W/System.err: java.io.FileNotFoundException: https://api.themoviedb.org/3/tv/84669?api_key=c4ee4a6a3f605d3c3754e368a4885414&append_to_response=credits,videos,images
2019-01-17 16:36:35.611 7366-7366/de.markusc.movieplanet E/AndroidRuntime: FATAL EXCEPTION: main
    Process: de.markusc.movieplanet, PID: 7366
    java.lang.NullPointerException: Attempt to invoke virtual method 'int java.lang.String.length()' on a null object reference
        at org.json.JSONTokener.nextCleanInternal(JSONTokener.java:116)
        at org.json.JSONTokener.nextValue(JSONTokener.java:94)
        at org.json.JSONObject.<init>(JSONObject.java:159)
        at org.json.JSONObject.<init>(JSONObject.java:176)
        at de.markusc.movieplanet.DownloadTaskDatenTV.onPostExecute(DownloadTaskDatenTV.java:92)
        at de.markusc.movieplanet.DownloadTaskDatenTV.onPostExecute(DownloadTaskDatenTV.java:36)
        at android.os.AsyncTask.finish(AsyncTask.java:695)
        at android.os.AsyncTask.-wrap1(Unknown Source:0)
        at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:712)
        at android.os.Handler.dispatchMessage(Handler.java:105)
        at android.os.Looper.loop(Looper.java:164)
        at android.app.ActivityThread.main(ActivityThread.java:6541)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
2019-01-17 16:36:36.150 7366-7422/de.markusc.movieplanet W/System.err: java.io.FileNotFoundException: https://api.themoviedb.org/3/movie/52371?api_key=c4ee4a6a3f605d3c3754e368a4885414&append_to_response=credits,videos,images
2019-01-17 16:41:00.070 1664-1739/? E/ConnectivityService: RemoteException caught trying to send a callback msg for NetworkRequest [ LISTEN id=10, [ Capabilities: INTERNET&NOT_RESTRICTED&TRUSTED&FOREGROUND]
 
Zuletzt bearbeitet:

MARJAVA

Aktives Mitglied
In manchen Bereichen lässt sich der ersetzen, ja. Hier gibts zb was dazu: https://www.baeldung.com/java-executor-service-tutorial

Habe den Code auf einen Executorservice umgeschrieben. Der Code funktioniert bis auf eine Tatsache ganz gut. Ich erhalte keinen Nullstring sondern mehrere Exceptions, siehe unten.

Hier mal mein Code dazu:
Java:
public class DataLoaderFilm {

    private Context context;
    private ArrayList<Integer> loadFilms=new ArrayList<Integer>();
    private ArrayList<Integer> loadFilmsType=new ArrayList<Integer>();
    private SQLiteDatabase myDatabase;

    public DataLoaderFilm(Context context,ArrayList<Integer> loadFilms,ArrayList<Integer> loadFilmsType){
        this.loadFilms=loadFilms;
        this.loadFilmsType=loadFilmsType;
        this.context=context;

        //myDatabase = context.openOrCreateDatabase("filmdatenbank", MODE_PRIVATE, null);

        //loadFilms();
    }
    @RequiresApi(api = Build.VERSION_CODES.N)
    public void startLoadingData(){



        ExecutorService pool = Executors.newCachedThreadPool();




                List<Callable<String>> tasks = new ArrayList<>();
                for (int i=0;i<loadFilms.size(); i++){
                    Callable<String> callable=new DownloadTaskDatenFilm(loadFilmsType.get(i),context,"https://api.themoviedb.org/3/movie/"+loadFilms.get(i)+"?api_key=c4ee4a6a3f605d3c3754e368a4885414&language=de-DE&append_to_response=credits,videos,images");
                    tasks.add(callable);
                }

                List<Future<String>> futures = new ArrayList<>();
                for(int i=0; i<loadFilms.size();i++){
                    Future<String> future1 = pool.submit(tasks.get(i));
                    futures.add(future1);
                }
            try
            {   List<String> results = new ArrayList<>();
                for(int i=0; i<loadFilms.size();i++){

                    String result="";
                    try {
                         result= (futures.get(i)).get(10, TimeUnit.SECONDS);
                    }catch (TimeoutException e) {
                        e.printStackTrace();
                    }
                    if(result==null) {

                        Toast.makeText(context, "Nummer "+loadFilms.get(i)+" nicht geladen!", Toast.LENGTH_SHORT).show();
                        Log.i("Log loadFilmFehler ", "Film "+i);



                    }else{
                        Log.i("Log loadFilm ", "Film "+i);
                    }
                }

            }
            catch (InterruptedException | ExecutionException e)
            {
                e.printStackTrace();
            }
            pool.shutdown();

        //Toast.makeText(context, "Download abgeschlossen!", Toast.LENGTH_SHORT).show();




    }
}

Beim Durchlauf der App, kommt folgende Exception:
Java:
2019-03-19 18:27:32.981 4077-4314/de.markusc.movieplanet W/System.err: java.io.FileNotFoundException: https://api.themoviedb.org/3/tv/326?api_key=c4ee4a6a3f605d3c3754e368a4885414&language=de-DE&append_to_response=credits,videos,images
2019-03-19 18:27:32.986 4077-4374/de.markusc.movieplanet W/System.err: java.io.FileNotFoundException: https://api.themoviedb.org/3/tv/1406?api_key=c4ee4a6a3f605d3c3754e368a4885414&language=de-DE&append_to_response=credits,videos,images
2019-03-19 18:27:33.008 4077-4281/de.markusc.movieplanet W/System.err: java.io.FileNotFoundException: https://api.themoviedb.org/3/tv/31911?api_key=c4ee4a6a3f605d3c3754e368a4885414&language=de-DE&append_to_response=credits,videos,images
2019-03-19 18:27:33.178 4077-4337/de.markusc.movieplanet W/System.err: java.io.FileNotFoundException: https://api.themoviedb.org/3/tv/32573?api_key=c4ee4a6a3f605d3c3754e368a4885414&language=de-DE&append_to_response=credits,videos,images
2019-03-19 18:27:33.355 4077-4403/de.markusc.movieplanet W/System.err: java.io.FileNotFoundException: https://api.themoviedb.org/3/tv/47450?api_key=c4ee4a6a3f605d3c3754e368a4885414&language=de-DE&append_to_response=credits,videos,images
2019-03-19 18:27:33.549 4077-4379/de.markusc.movieplanet W/System.err: java.net.SocketException: socket failed: EMFILE (Too many open files)
2019-03-19 18:27:33.551 4077-4380/de.markusc.movieplanet W/System.err: java.net.SocketException: socket failed: EMFILE (Too many open files)
2019-03-19 18:27:33.672 4077-4381/de.markusc.movieplanet W/System.err: java.net.SocketException: socket failed: EMFILE (Too many open files)
2019-03-19 18:27:33.745 4077-4329/de.markusc.movieplanet W/System.err: java.net.SocketException: socket failed: EMFILE (Too many open files)
2019-03-19 18:27:33.831 4077-4268/de.markusc.movieplanet W/System.err: java.net.SocketException: socket already closed
2019-03-19 18:27:33.833 4077-4258/de.markusc.movieplanet W/System.err: java.net.SocketException: socket failed: EMFILE (Too many open files)
2019-03-19 18:27:33.835 4077-4322/de.markusc.movieplanet W/System.err: java.net.SocketException: socket failed: EMFILE (Too many open files)
2019-03-19 18:27:33.836 4077-4344/de.markusc.movieplanet W/System.err: java.net.SocketException: socket failed: EMFILE (Too many open files)
2019-03-19 18:27:33.837 4077-4413/de.markusc.movieplanet W/System.err: java.net.SocketException: socket already closed
2019-03-19 18:27:33.840 4077-4289/de.markusc.movieplanet W/System.err: java.net.SocketException: socket already closed
2019-03-19 18:27:33.844 4077-4396/de.markusc.movieplanet W/System.err: java.net.SocketException: socket failed: EMFILE (Too many open files)
2019-03-19 18:27:34.154 4077-4296/de.markusc.movieplanet W/System.err: javax.net.ssl.SSLException: Unable to create application data
2019-03-19 18:27:34.539 4077-4314/de.markusc.movieplanet W/System.err: java.io.FileNotFoundException: https://api.themoviedb.org/3/tv/87362?api_key=c4ee4a6a3f605d3c3754e368a4885414&language=de-DE&append_to_response=credits,videos,images
2019-03-19 18:27:43.193 4077-4113/de.markusc.movieplanet W/System.err: java.util.concurrent.TimeoutException
2019-03-19 18:27:53.195 4077-4113/de.markusc.movieplanet W/System.err: java.util.concurrent.TimeoutException
2019-03-19 18:28:03.196 4077-4113/de.markusc.movieplanet W/System.err: java.util.concurrent.TimeoutException
2019-03-19 18:28:13.197 4077-4113/de.markusc.movieplanet W/System.err: java.util.concurrent.TimeoutException

Speziell die FileNotFoundException ist nicht zu verstehen, denn wenn ich dem Link hinter der Exception folge, dann erhalte ich eine JSON-Objekt im Browser. Zwar mit zeitlicher Verzögerung, doch ich erhalte einen. Aber vielleicht ist gerade die zeitliche Verzögerung das Problem. Hat der Server vielleicht lange Reaktionszeiten und die App kommt damit nicht klar?
 
Zuletzt bearbeitet:

mrBrown

Super-Moderator
Mitarbeiter
Versuch das ganze mal mit einem single-Thread-Executor, dann wird das alles nacheinander ausgeführt und man kann das parallele als Fehlerquelle für die FNFE ausschließen.

Der relevante Teil, das runterladen, fehlt aber, oder?
 

MARJAVA

Aktives Mitglied
Der Call dazu sieht so aus:
Java:
public class DownloadTaskDatenFilm implements Callable<String> {

    private int position;
    private ArrayList<String> characterListe=new ArrayList<String>();
    private ArrayList<String> schauspielerListe=new ArrayList<String>();
    private ArrayList<String> schauspielerbilder=new ArrayList<String>();
    private SQLiteDatabase myDatabase;
    private String urls;
    private String result;

    public DownloadTaskDatenFilm(int position,Context context, String urls){
        this.position=position;
        //myDatabase=context.openOrCreateDatabase("filmdatenbank",MODE_PRIVATE,null);
        this.urls=urls;
    }


    @RequiresApi(api = Build.VERSION_CODES.N)
    @Override
    public String call() throws Exception {

        result= new String("");
        URL url;
        HttpURLConnection urlConnection=null;
        try{

            url = new URL(urls);
            urlConnection = (HttpURLConnection) url.openConnection();
            InputStream inputStream = urlConnection.getInputStream();
            BufferedReader br=new BufferedReader(new InputStreamReader(inputStream,Charset.defaultCharset()));
            result=br.lines().collect(Collectors.joining(System.lineSeparator()));


        }catch (FileNotFoundException e){
            e.printStackTrace();

        } catch (MalformedURLException e) {
            e.printStackTrace();

        } catch (IOException e) {
            e.printStackTrace();

        }catch (Exception e){
            e.printStackTrace();

        }
        return result;
    }
}
 

MARJAVA

Aktives Mitglied
Am Code hat sich bei einem SingleThreadExecutor nicht so viel geändert:
Java:
public class DataLoaderFilm {

    private Context context;
    private ArrayList<Integer> loadFilms=new ArrayList<Integer>();
    private ArrayList<Integer> loadFilmsType=new ArrayList<Integer>();
    private SQLiteDatabase myDatabase;

    public DataLoaderFilm(Context context,ArrayList<Integer> loadFilms,ArrayList<Integer> loadFilmsType){
        this.loadFilms=loadFilms;
        this.loadFilmsType=loadFilmsType;
        this.context=context;

        //myDatabase = context.openOrCreateDatabase("filmdatenbank", MODE_PRIVATE, null);

        //loadFilms();
    }
    @RequiresApi(api = Build.VERSION_CODES.N)
    public void startLoadingData(){



        ExecutorService pool = Executors.newSingleThreadExecutor();




                List<Callable<String>> tasks = new ArrayList<>();
                for (int i=0;i<loadFilms.size(); i++){
                    Callable<String> callable=new DownloadTaskDatenFilm(loadFilmsType.get(i),context,"https://api.themoviedb.org/3/movie/"+loadFilms.get(i)+"?api_key=&language=de-DE&append_to_response=credits,videos,images");
                    tasks.add(callable);
                }

                List<Future<String>> futures = new ArrayList<>();
                for(int i=0; i<loadFilms.size();i++){
                    Future<String> future1 = pool.submit(tasks.get(i));
                    futures.add(future1);
                }
            try
            {   List<String> results = new ArrayList<>();
                for(int i=0; i<loadFilms.size();i++){

                    String result="";
                    try {
                         result= (futures.get(i)).get(10, TimeUnit.SECONDS);
                    }catch (TimeoutException e) {
                        e.printStackTrace();
                    }
                    if(result==null) {

                        Toast.makeText(context, "Nummer "+loadFilms.get(i)+" nicht geladen!", Toast.LENGTH_SHORT).show();
                        Log.i("Log loadFilmFehler ", "Film "+i);



                    }else{
                        Log.i("Log loadFilm ", "Film "+i);
                    }
                }

            }
            catch (InterruptedException | ExecutionException e)
            {
                e.printStackTrace();
            }
            pool.shutdown();

        //Toast.makeText(context, "Download abgeschlossen!", Toast.LENGTH_SHORT).show();




    }

, aber die Fehler sind gleich geblieben

Java:
2019-03-20 07:34:28.751 2644-2756/de.markusc.movieplanet W/System.err: java.util.concurrent.TimeoutException
2019-03-20 07:34:38.753 2644-2756/de.markusc.movieplanet W/System.err: java.util.concurrent.TimeoutException
2019-03-20 07:34:44.494 2644-3706/de.markusc.movieplanet W/System.err: java.io.FileNotFoundException: https://api.themoviedb.org/3/tv/2710?api_key=c4ee4a6a3f605d3c3754e368a4885414&language=de-DE&append_to_response=credits,videos,images
2019-03-20 07:34:44.494 2644-3711/de.markusc.movieplanet W/System.err: java.io.FileNotFoundException: https://api.themoviedb.org/3/tv/615?api_key=c4ee4a6a3f605d3c3754e368a4885414&language=de-DE&append_to_response=credits,videos,images
2019-03-20 07:34:44.495 2644-3717/de.markusc.movieplanet W/System.err: java.io.FileNotFoundException: https://api.themoviedb.org/3/tv/58474?api_key=c4ee4a6a3f605d3c3754e368a4885414&language=de-DE&append_to_response=credits,videos,images
2019-03-20 07:34:44.496 2644-3799/de.markusc.movieplanet W/System.err: java.io.FileNotFoundException: https://api.themoviedb.org/3/tv/33217?api_key=c4ee4a6a3f605d3c3754e368a4885414&language=de-DE&append_to_response=credits,videos,images
2019-03-20 07:34:44.497 2644-3733/de.markusc.movieplanet W/System.err: java.io.FileNotFoundException: https://api.themoviedb.org/3/tv/65567?api_key=c4ee4a6a3f605d3c3754e368a4885414&language=de-DE&append_to_response=credits,videos,images
20
 

MARJAVA

Aktives Mitglied
Hat noch jemand eine Idee, ob ich ausschließen kann, dass es sich um einen Programmfehler handelt. Ist es eine Serverseitige Problematik?
 

Neue Themen


Oben