CalendarContract langsam (Abrufen von Kalenderdaten)

  • 17 Antworten
  • Letztes Antwortdatum
S

skywalker22

Neues Mitglied
0
Hallo,

ich habe folgenden Code, der mir alle Kalenderdaten für einen gewissen Zeitraum abruft:

Code:
Cursor cur = null;
        ContentResolver cr = this.c.getContentResolver();

        String[] mProjection =
                {
                        CalendarContract.Instances._ID,
                        CalendarContract.Instances.EVENT_ID,
                        CalendarContract.Instances.CALENDAR_ID,
                        CalendarContract.Instances.TITLE,
                        CalendarContract.Instances.EVENT_LOCATION,
                        CalendarContract.Instances.DESCRIPTION,
                        CalendarContract.Instances.EVENT_COLOR,
                        CalendarContract.Instances.DTSTART,
                        CalendarContract.Instances.DTEND,
                        CalendarContract.Instances.EVENT_TIMEZONE,
                        CalendarContract.Instances.EVENT_END_TIMEZONE,
                        CalendarContract.Instances.DURATION,
                        CalendarContract.Instances.ALL_DAY,
                        CalendarContract.Instances.RRULE,
                        CalendarContract.Instances.RDATE,
                        CalendarContract.Instances.EXDATE

                };


        ArrayList<ReadCalendar> allcalendars = getAllCalendars(false, true);

        String selection_allcalendars = "";
        for (int i = 0; i < allcalendars.size(); i++) {
            ReadCalendar cal = allcalendars.get(i);

            if (i == allcalendars.size() - 1) {
                selection_allcalendars += " (" + CalendarContract.Instances.CALENDAR_ID + " = " + cal.getId() + ") ";
            } else {
                selection_allcalendars += " (" + CalendarContract.Instances.CALENDAR_ID + " = " + cal.getId() + ") OR ";
            }
        }
        if (selection_allcalendars.length() > 1) {
            selection_allcalendars = "(" + selection_allcalendars;
            selection_allcalendars += ") AND ";
        }
        if (selection_allcalendars.length() <= 0) {
            selection_allcalendars += " (" + CalendarContract.Instances.CALENDAR_ID + " = -1) AND ";
        }

        Uri uri = Uri.parse(CalendarContract.Instances.CONTENT_URI + "/" +
                Long.toString(from) + "/" +
                Long.toString(to));


        cur = cr.query(uri, mProjection, selection, null, CalendarContract.Instances.DTSTART + " ASC");
        ArrayList<ReadEvent> readevent = new ArrayList<ReadEvent>();
        ArrayList<String> available = new ArrayList<>();
        MySQLiteHelper db = new MySQLiteHelper(this.c);

        TimeZone tz = TimeZone.getDefault();

        while (cur != null && cur.moveToNext()) {
...
}

Ich habe eine Kalenderapp programmiert, in der ich diesen Code verwende. Für eine einfache Tagesansicht klappt alles super, die Termine werden unter 80 ms angezeigt. Für eine Wochenansicht braucht dieser Code allerdings ca. 3-5 Sekunden. Dann hängt die App...

Wie machen das Apps wie 'aCalendar' oder die interne Google Kalender App, dass die Termine blitzschnell angezeigt werden?
 
a) wo "hängt" es denn dann ? Logs mit System.currentTimeMillis() ?
b) offensichtlich scheinst du später eine Kopie in eine Sqlite zu schreiben - richtig ?


c) Und wie verhält sich das Ganze , wenn du es in einem Thread/Handler mit Callback ausführst um somit der UI die Luft zu geben ?
(Und dann erst deinem RecyclerView (oder was auch immer) die ArrayList übergibst)

Würde ich zumindnest grundsätzlich tun - das Ganze sieht m.E. ziemlich nach einem Blockieren aus.
Handbremse voll angezogen :)
 
Zuletzt bearbeitet:
skywalker22 schrieb:
selection_allcalendars += " (" + CalendarContract.Instances.CALENDAR_ID + " = " + cal.getId() + ") ";
Einen großen Geschwindigkeit Punkt sehe ich zb hier.

Strings sind Immutable. Im Hintergrund wird nicht der String ergänzt, sondern ein neuerer erstellt mit der Größe des alten plus des neuen Inhaltes.
In den neuen wird dann der alte Inhalt und der neuer Inhalt kopiert.
Das braucht Speicher und Zeit.

Da würde sich ein StringBuffer / Builder besser eignen.
 
Zuletzt bearbeitet:
a) wo "hängt" es denn dann ? Logs mit System.currentTimeMillis() ?
b) offensichtlich scheinst du später eine Kopie in eine Sqlite zu schreiben - richtig ?


c) Und wie verhält sich das Ganze , wenn du es in einem Thread/Handler mit Callback ausführst um somit der UI die Luft zu geben ?
(Und dann erst deinem RecyclerView (oder was auch immer) die ArrayList übergibst)
zu a) Für eine Tagesansicht dauert die Schleife nur 150 ms. Für eine Wochenansicht 500 ms. Es hängt also gar nicht vom Auslesen der Datenbank ab. Ich habe das Hinzufügen der Daten zum View in einem Thread abgelegt. Das dauert dann allerdings mehr als 4 Sekunden.

zu b) Nein. Keine Kopie in eine sqllite.

zu c) Es ist bereits in einem Thread mit einem Handler() und Runnable().

Einen großen Geschwindigkeit Punkt sehe ich zb hier.

Strings sind Immutable. Im Hintergrund wird nicht der String ergänzt, sondern ein neuerer erstellt mit der Größe des alten plus des neuen Inhaltes, das braucht Speicher und Zeit.

Da würde sich ein StringBuffer / Builder besser eignen.
Nein, ist es nicht. Ich habe die Zeit gemessen und dafür wird nur 1 ms aufgewendet.


Zusammengefasst lässt sich sagen:

Tagesansicht: 150 ms Zeitaufwand zum Auslesen aus der Datenbank. Das Hinzufügen zum View geschieht sehr schnell.
Wochenansicht: 500 ms Zeitaufwand zum Auslesen aus der Datenbank. Das Hinzufügen zum View geschieht sehr langsam (mehr als 3-5 Sekunden).

Die Ansichten habe ich folgendermaßen konstruiert:

- ein DoubleViewpager für Tages-, Wochen- und eine Monatsansicht.
- in der Wochenansicht gibt es 7 ListViews (für 7 Tage). Dort werden die Daten einfach in die ListViews eingefügt. Gemessen sind es nur höchstens 200 ms. Aber wenn ich die App benutze und zur Wochenansicht scrolle, dauert es ca. 5-6 Sekunden, bis alles geladen ist.
 
Zuletzt bearbeitet:
Es gibt ein "Fragment" für den DoubleViewpager.

Der folgende Code fügt ein Datum in die ListView ein, für den Tag i, das in numoflinearlayout definiert wird.

Code:
private void addDate(int numoflinearlayout, long from, long to, String title, String color, int wholeday, ReadEvent readevent) {
Calendar c1 = Calendar.getInstance();
    Calendar c2 = Calendar.getInstance();

    TimeZone tz = c1.getTimeZone();
    TimeZone tz2 = c2.getTimeZone();

    from = ((from - tz.getOffset(from)) / 1000) * 1000;
    to = ((to - tz2.getOffset(to)) / 1000) * 1000;

    c1.setTimeInMillis(from);
    c2.setTimeInMillis(to);

    String daysplus = "";
    if(((to - from) / 60 / 60 / 24 / 1000) > 0) {
daysplus = String.valueOf(((to - from) / 60 / 60 / 24 / 1000)) + "+";
    }

String hourfromto = "";
    if(c1.getTimeInMillis() == c2.getTimeInMillis() - 86400000) {

} else {
hourfromto = new String(c1.get(Calendar.HOUR_OF_DAY) + ":" + new DateHelper().singleNumber(c1.get(Calendar.MINUTE)) + " - " + c2.get(Calendar.HOUR_OF_DAY) + ":" + new DateHelper().singleNumber(c2.get(Calendar.MINUTE)) + " " + daysplus + " ");

    }

showPopup(numoflinearlayout, hourfromto, title, color, wholeday, readevent);
}

Hier werden alle Daten ausgelesen und dann in die Listen gepackt:

Code:
 Handler handler = new Handler();
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                final Events events = new Events(WeekFragment.this.c);

                Calendar c1_init = (Calendar) day.clone();
                c1_init.set(Calendar.HOUR_OF_DAY, 0);
                c1_init.set(Calendar.MINUTE, 0);
                c1_init.set(Calendar.SECOND, 0);

                Calendar c2_init = (Calendar) day.clone();
                c2_init.set(Calendar.HOUR_OF_DAY, 0);
                c2_init.set(Calendar.MINUTE, 0);
                c2_init.set(Calendar.SECOND, 0);
                c2_init.add(Calendar.DATE, 1);
                c2_init.add(Calendar.DATE, 6);

                long startTime = System.nanoTime();
                ArrayList<ReadEvent> alldateshourly_init = events.getAllEventsByWeek(c1_init, c2_init);
                long endTime   = System.nanoTime();
                long totalTime = endTime - startTime;

                System.out.println("Week Time execution: " + totalTime);



             
                day.set(Calendar.HOUR_OF_DAY, 0);
                day.set(Calendar.MINUTE, 0);
                day.set(Calendar.SECOND, 0);



                for(final ReadEvent readevent : alldateshourly_init) {

                    Calendar c1 = Calendar.getInstance();
                    Calendar c2 = Calendar.getInstance();

                    TimeZone tz = c1.getTimeZone();
                    TimeZone tz2 = c2.getTimeZone();
                    long from = readevent.getDtstart();
                    long to = readevent.getDtend();
                    from = ((from - tz.getOffset(from)) / 1000) * 1000;
                    to = ((to - tz2.getOffset(to)) / 1000) * 1000;

                    c1.setTimeInMillis(from);
                    c2.setTimeInMillis(to);

                    String daysplus = "";
                    if (((to - from) / 60 / 60 / 24 / 1000) > 0) {
                        daysplus = ((to - from) / 60 / 60 / 24 / 1000) + "+";
                    }

                    int i = (int)((c1.getTimeInMillis() - day.getTimeInMillis() + 1000) / 1000l / 60 / 24 / 60);
                    if(i >= 0 && i <= 6) {

                        if (readevent.getAllDayBoolean() == true) {

                            System.out.println("EEE: " + "  " + (c1.getTimeInMillis() - day.getTimeInMillis()) + " " + (int)((c1.getTimeInMillis() - day.getTimeInMillis()) / 1000l / 60 / 24 / 60) + readevent.getEventname());
                            if (readevent.getEventcolor() == null)
                                WeekFragment.this.addDate(i, readevent.getDtstart(), readevent.getDtend(), readevent.getEventname(), events.getCalendarColor(readevent.getCalendarid()), readevent.getAllday(), readevent);

                            else
                                WeekFragment.this.addDate(i, readevent.getDtstart(), readevent.getDtend(), readevent.getEventname(), readevent.getEventcolor(), readevent.getAllday(), readevent);

                        } else {
                

                            String hourfromto = "";
                            if (c1.getTimeInMillis() == c2.getTimeInMillis() - 86400000) {
                                WeekFragment.this.arraylist.get(i).add(new String(readevent.getEventname()));
                            } else {
                                hourfromto = new String(new DateHelper().singleNumber(c1.get(Calendar.HOUR_OF_DAY)) + ":" + new DateHelper().singleNumber(c1.get(Calendar.MINUTE)) + " " + daysplus + " ");

                                WeekFragment.this.arraylist.get(i).add(new String(hourfromto + " " + readevent.getEventname()));
                            }


                            if (readevent.getEventcolor() == null) {
                                try {
                                    WeekFragment.this.colorslist.get(i).add(Integer.valueOf(events.getCalendarColor(readevent.getCalendarid())));
                                } catch (Exception e) {
                                    WeekFragment.this.colorslist.get(i).add(-6306073);
                                }
                            } else
                                WeekFragment.this.colorslist.get(i).add(Integer.valueOf(readevent.getEventcolor()));


                            WeekFragment.this.listenerlist.get(i).add(new View.OnClickListener() {
                                @Override
                                public void onClick(View view) {

                                    showDialog(getActivity(), readevent);

                                }
                            });
                        }
                    }
                }

                for(int i = 0; i < WeekFragment.this.arraylist.size(); i++) {

                    for(int k = 0; k < arraylist.get(i).size(); k++) {
                        if(arraylist.get(i).get(k) == null || colorslist.get(i).get(k) == null || listenerlist.get(i).get(k) == null) {
                            arraylist.get(i).remove(k);
                            colorslist.get(i).remove(k);
                            listenerlist.get(i).remove(k);
                        }
                    }


                    ArrayAdapterCostum adapter = new ArrayAdapterCostum(WeekFragment.this.c, R.layout.month_listitem, arraylist.get(i).toArray(new String[arraylist.get(i).size()]), colorslist.get(i).toArray(new Integer[colorslist.get(i).size()]), listenerlist.get(i).toArray(new View.OnClickListener[listenerlist.get(i).size()]));
                    listviews.get(i).setAdapter(adapter);

                    linearlayouts.get(i).addView(listviews.get(i));
                }
            }
        }, 0);
 
Zuletzt bearbeitet:
skywalker22 schrieb:
Das Hinzufügen zum View geschieht sehr langsam (mehr als 3-5 Sekunden).
dann zeige das mal
Beiträge automatisch zusammengeführt:

skywalker22 schrieb:
Aber wenn ich die App benutze und zur Wochenansicht scrolle, dauert es ca. 5-6 Sekunden, bis alles geladen ist.
Benutze RecyclerView das ist performanter als das alte ListView.

Aber wie gesagt ohne Code kann man wenig sagen.
 
Zuletzt bearbeitet:
@skywalker22

zu b) du initiierst einen DB Helper dazu in deinem obigen (ersten) Code
(Das hatte mich stutzig gemacht)
MySQLiteHelper db = new MySQLiteHelper(this.c);
In deinem zweiten Post ist er wie von Zauberhand verschwunden ..........
(Daher uns bitte nicht verwirren - wir gelangen dann auch auf einen falschen Pfad, um zu helfen )


Zu deinem zweiten (Source) Post :

Deine Vorgehensweise ist leider falsch - Nun ist es auch logisch, warum die App zur Schnecke wird :)

NIEMALS im Thread/Handler ein UI Element füllen - nur wenn es überhaupt nicht mehr geht (ugly)
Wenn du wenigstens mal runOnUiThread verwendet hättest, könnte man das ja mit zwei zugedrückten Augen durchwinken.

FAZIT : Du ballerst das System mit deiner Loop quasi zu , du musst das ganze am besten Objektorientiert entzerren .
Das ist leider kein Desktop :)


Lösung zum Performanten :

a) Definiere eine ArrayList<ModelObjekt> (nicht String) übergeordnet, fülle sie im Thread und übergib sie einem RecyclerView Adapter am ENDE und AUSSERHALB des Threads. (Callback implementieren)
b) Erstelle Dir für den Thread eine eigene Klasse (nicht static) und bilde dann eine Instanz.

c) Handle dann erst beim Callback deine UI Ausgabe:
Create dynamic lists with RecyclerView | Android Developers

P.S. Mit ListView wird das leider nichts -> ein gänzlich falsches Element für dein Vorhaben


Und zwei kleine Anmerkung :
a) Handler auch nicht ohne Duration starten - 100-200 ist ein schöner Wert und fällt auch nicht auf
b)
System.out.println("Week Time execution: " + totalTime);
Besser LogCat benutzen , das ist performanter und filterbar -> System .out bremst
 
Zuletzt bearbeitet:
Hallo,

ich habe jetzt den RecyclerView implementiert.

Wenn ich von oben nach unten scrolle, läuft es schon mal einwandfrei flüssig. :)

Wenn ich aber von der Tagesansicht zur Wochenansicht wechsle, läuft es immer noch sehr sperrig.

Wie macht das aCalendar oder Google Kalender, dass die Events sofort angezeigt werden?

Bei mir muss das immer noch 500 ms laden, bis die Wochenansicht angezeigt wird.
 
Hast du denn auch die Separation mit callbacks vorgenommen ?
Wenn ja, stimmt an deinem gesamten Aufbau der App etwas nicht.
 
Zuletzt bearbeitet:
Ja, hab jetzt auch mit Callbacks implementiert.

Es ist das selbe.

Ich nutze DoubleViewPager von GitHub - juliome10/DoubleViewPager: Horizontal + Vertical ViewPager .

Meine App funktioniert einfach zu langsam... (zumindest, was die Wochenanzeige angeht). Wenn man 500 ms für das Laden der Daten für die Wochenansicht braucht, dann kann man die App schon in den Müll schmeißen.
 
Zuletzt bearbeitet:
Du bist Dir schon darüber im Klaren- dass das obige Repository nichts mehr mit den heutigen Techniken zu tun hat ?

Das ist Android 6 konform, mehr auch nicht
(Siehe die v4 Abhängigkeiten)

Warum machst du das Bisschen nicht selbst und verwendest aktuelle Techniken ?
Dafür gibt es die aktuelle API

Meine App funktioniert einfach zu langsam.
Und wenn du - wie oben angeraten - das Ganze in eine extra Klasse setzt und eine Instanz für heute , und eine Instanz für die Woche nimmst ? - Kannst du doch Beides anstossen ........ und schlichtweg nur den Adapter updaten.



Ehrlich gesagt verstehe ich dich nicht :
Altes Grundmaterial nehmen , Techniken eigentlich nicht verstehen, und offensichtlich API Docu nicht lesen.

.... aber sich über ein unperformantes Verhalten "beklagen" :)
Das passt irgendwie nicht :)

Nun denn : Viel Erfolg ...


BTW : Ein "Danke" wäre an der einen oder anderen Stelle schon mal passend gewesen ...... wenn man sich schon die Mühe macht
 
Zuletzt bearbeitet:
  • Danke
Reaktionen: jogimuc
Wie macht das aCalendar oder Google Kalender, dass die Events sofort angezeigt werden?
Dann schaue dir an welchen Viewpager da benutzt wird. Da wird sicherlich der Viewpager2 benutzt. Der auch im Hintergrund Daten der Nachbar Fragments lädt. Fur ein gutes swipe brauchst du das ja auch.


Wie schon gesagt auf die alte Quelle von Double Pager würde ich nicht mehr setzen.
 
  • Danke
Reaktionen: swa00
@jogimuc

Der meldet sich wahrscheinlich nicht mehr :)
Zumal ich mir nicht wirklich sicher bin , ob er das auch mit Instanzen , Callbacks und UI Thread wirklich verstandnen hat ........ :)

Sieht man an seiner letzten Reaktion ..........
 
Zuletzt bearbeitet:
@swa00 ja das kann sein.
Wollte nur einen Tipp auf seine Frage wie womit es andrere aktuelle Apps machen., geben.

Der Rest ligt jetzt bei ihm. :D
 
  • Danke
Reaktionen: swa00
Hallo,

okay, ich werde es versuchen umzuprogrammieren, wenn ich die Zeit dazu finde.

Was lädt denn der ViewPager2 denn im Hintergrund? Nur die Ansichten links und rechts? Oder auch vertikal? Auch sowohl links und rechts als auch vertikal links rechts?

Wenn der ViewPager nur die Ansichten nach rechts und links lädt, dann wird es ja wohl das Gleiche wie beim ViewPager sein.

Ich melde mich, sobald ich es umprogrammiert habe.
 
Zuletzt bearbeitet:
Was lädt denn der ViewPager2 denn im Hintergrund? Nur die Ansichten links und rechts? Oder auch vertikal? Auch sowohl links und rechts als auch vertikal links rechts?

Wenn der ViewPager nur die Ansichten nach rechts und links lädt, dann wird es ja wohl das Gleiche wie beim ViewPager sein.

Steht alles detailliert in der API Documentation - das tägliche Gebetsbuch für jeden Android Entwickler
Android API reference | Android Developers
 
Zuletzt bearbeitet:
  • Danke
Reaktionen: jogimuc
Hallo,

ich habe es jetzt zum ViewPager2 (mit FragmentStateAdapter) umprogrammiert. Allerdings habe ich das Problem, dass die Ansichten (die Fragments) beim Scrollen nicht aktualisieren. Sie werden nur statisch einmalig angezeigt. Das war mit ViewPager(1) noch möglich.

Code:
public class ScreenSlidePagerDayAdapter extends FragmentStateAdapter {
    public ScreenSlidePagerDayAdapter(FragmentActivity fa) {
        super(fa);
    }

    @Override
    public Fragment createFragment(int position) {

        return DayFragment.newInstance(position, true);

    }

    @Override
    public int getItemCount() {
        return 10000;
    }
}


Code:
doubleViewPagerDay = (ViewPager2) findViewById(R.id.pager);
doubleViewPagerDay.setBackgroundColor(Color.WHITE);
doubleViewPagerDayAdapter = new ScreenSlidePagerDayAdapter(this);
doubleViewPagerDay.setAdapter(doubleViewPagerDayAdapter);

Es steht ja auch, dass ViewPager2 noch in der Beta-Version ist... :1f621:
 
Wenn du 10000 Fragmente hast und warscheinlich auch läst ist es klar das es langsam geht.
 
  • Haha
Reaktionen: swa00

Ähnliche Themen

S
  • skywalker22
Antworten
1
Aufrufe
318
swa00
swa00
J
Antworten
1
Aufrufe
187
swa00
swa00
A
Antworten
4
Aufrufe
428
AppDevelopment
A
Zurück
Oben Unten