Beiträge von Marco Feltmann

    Im Prinzip gibt es eine Frage, die weit vor der Frage 'Wie kann Künstliche Intelligenz funktionieren' und auch noch der davor gelagerten Frage 'Was ist Künstliche Intelligenz' geklärt werden muss.


    Was ist eigentlich Intelligenz?


    Solange sich AfD Wähler für intelligent halten, kann diese Frage nicht abschließend geklärt sein.
    Wikipedia behauptet diesbezüglich:

    Zitat

    Intelligenz (von lat. intellegere „verstehen“, wörtlich „wählen zwischen …“ von lat. inter „zwischen“ und legere „lesen, wählen“) ist in der Psychologie ein Sammelbegriff für die kognitive Leistungsfähigkeit des Menschen. Da einzelne kognitive Fähigkeiten unterschiedlich stark ausgeprägt sein können und keine Einigkeit besteht, wie diese zu bestimmen und zu unterscheiden sind, gibt es keine allgemeingültige Definition der Intelligenz. Vielmehr schlagen die verschiedenen Intelligenztheorien unterschiedliche Operationalisierungen des alltagssprachlichen Begriffs vor.


    Wenn wir also nicht einmal wissen, was Intelligenz ist, wie können wir dann Künstliche Intelligenz erschaffen?

    Ich überlege gerade, kann man das nicht etwas optimieren? Indem man z.b. die länge des längsten objektes in der implementierung der liste speichert. Bei einer solchen Schleife würde dann einmalig soviel Speicher reserviert werden wie das größte Objekt braucht und alle anderen Objekte werden in dem selben Speicherbereich gespeichert.

    Wir haben eine Garbage Collection. Aber auch bei einem halbautomatischen Reference Count System hättest Du hier ein gravierendes Problem.
    Der Schleifenkörper definiert einen eigenen Gültigkeitsbereich. Welchen Automatismus man auch immer verwendet, dieser wird erst beim Verlassen des Gültigkeitsbereichs angesprungen.
    Die Automatismen können nicht wissen, ob Du welche Objekte wie lange noch brauchst.
    Man fragt die Objekte ja nicht ab, um die CPU zu beschäftigen. (Klappt auch nicht immer, wird oftmals wegoptimiert.)
    Meistens tut man etwas mit den Objekten.


    Es ist ja insofern schon optimiert, dass man mit Referenzen (Zeiger sagt man ja in Java nicht) statt mit Objektkopien hantiert.
    Referenzen haben ja eine definierte geringe Größe, so dass der Speicherunterschied zu vorher nicht so groß ist.


    Über den generierten Index aus der for Schleife werden ja auch Objektreferenzen über Instanzmethoden geholt.
    Die for-each Schleife packt das lediglich aus dem Schleifenkörper in den Schleifenkopf.


    Relevant sind for Schleifen eigentlich nur für prozedurale bzw. funktionale Programmierung, für objektorientierte empfiehlt sich schon die for-each Schleife.


    Interessant wurden diese Unterschiede erst durch Apples Swift 3.0, in dem die ursprüngliche for Schleife abgeschafft wurde.

    Dazu drei Anmerkungen:


    Du bekommst aus der Liste das raus, was in der Liste steckt.
    Sagst Du der Liste, sie beinhaltet Views und packst 25 Buttons und 1 TextView rein und in der for-each Schleife holst Du alles als Button ab, gibt es beim 26. Objekt eine ClassCastException.


    Sammelobjekte sind in Java per Definition veränderlich. Nutzt Du die Schleife um Objekte der Liste hinzuzufügen, zu entfernen oder auszutauschen, dann fliegt Dir das alles um die Ohren. Immer nur lesend in for-each Schleifen zugreifen!


    for-each Schleifen sind sicherer als reine for Schleifen, da sie statt irgend einen Wert hochzuzählen über den irgend ein Zugriff geregelt wird sauber das nächste Objekt der Liste zurückliefert. Dafür kostet sie auch viel mehr Ressourcen.
    Es muss im obigen Beispiel 26x Speicher reserviert, mit einem Objekt gefüllt und irgendwann von der Garbage Collection wieder weggeräumt werden. Die for Schleife benötigt hingegen erst einmal nur Speicherplatz für einen Integer.

    Wie stellt ihr euch die perfekte Klassenorganisation für ein Spiel, welches Bitmaps verarbeitet vor mit zB. einem Menü und einem InGame Screen?

    In OpenGL und OpenAL realisiert.


    Auch wenn die Hardware immer schneller wird und sich Flappy Bird Klone mit UI Klassen erstellen lassen, ist das UI Gedöns viel zu ressourcenhungrig für 'richtige' Spiele.


    Die ganzen Transparenzen, Redraws, Overlapping und Animationsberechnungen pro View kosten einfach viel zu viel Leistung.
    Mach's nativ, nutze Frameworks wie Cocos2D oder greife auf Drittanbieter wie Unity zurück.

    Emulator mit Android+GoogleAPI Image verwenden, dann klappts im Allgemeinen auch mit Maps.
    Die Standard Android Images haben keinerlei Bezug zu Gogle, dementsprechend keine Play Services und auch keinen Play Store, um sie nachzuinstallieren.

    In Deinem Design scheint mindestens ein Wert je Button zu fehlen.
    (Von verwirrender Namensgebung sehe ich einmal ab.)


    Du setzt Dein Label an Hand der letzten gespeicherten Zeit zuzüglich der aktuellen Zeit.
    Nirgendwo hältst Du aber fest, wie lange Button1 eben nicht hochgezählt hat.


    Da Du ja schon eine Start- und Stoppfunktionalität hast, könntest Du natürlich überlegen zwei unabhängige Chronometer-Instanzen zu benutzen.
    Ein Buttonklick startet dann den eigenen und stoppt den fremden Chronometer. Was Chronometer genau kann ist ja nicht ersichtlich...


    Alternativ merkst Du Dir zu jedem Button nicht nur die letzte Zeit (timen) sondern auch, wann zuletzt gestoppt wurde. (stopn)
    Anhand stopn und elapsedRealtime() kannst Du dann die Differenz berechnen, die timen hinzuaddiert werden soll.

    Auf zur nächsten Runde!
    Hierfür verlassen wir die Benutzeroberfläche und kümmern uns um das Kernstück der Anwendung: Die Zeiterfassung selbst.


    Im Prinzip muss diese Komponente ja nichts Anderes tun als die Sekunden zählen, die zwischen 'Kommen' und 'Gehen' verstreichen.
    Doch ist das wirklich eine so gute Idee? Stellen wir uns das kurz einmal vor: Jede Sekunde wird ein Zähler um 1 erhöht.
    Das sind dann 3.600 Berechnungen die Stunde bzw. ganze 28.800 Berechnungen bei einem 8 Stunden Tag. Sicherlich nicht viel für so ein Gerät, das derartige Berechnungen auch in wenigen Sekunden durchführen kann. Beispielsweise für Animationen, Videos, Spiele und so weiter. Nur gehen gerade Spiele und Videos sehr schnell den Akku an. Wenn das also nun jede App machen würde...


    Wir müssen auch die Unterbrechungen berücksichtigen. Kurz jemanden anrufen, Mails abrufen, dem Kollegen via WhatsApp eine Sprachnachricht schicken, dem Controlling im internen Chat eine Frage stellen, eine Kleinigkeit im Browser suchen, das gerade herumgemailte PDF ansehen... und unsere App wurde aus dem Speicher geräumt.
    Da läuft dann natürlich nichts weiter, der Zähler zählt nicht, die Daten sind falsch.
    Natürlich könnten wir einen Service anbinden, der im Hintergrund die Zeit mitzählt und die App kann dann darauf zugreifen. Aber gibt es diese Services nicht schon zu Hauf?
    Uhr. Stoppuhr. Eieruhr. Wecker.
    Ist es wirklich eine gute Idee, noch so ein Ding ins System zu packen?


    Schauen wir uns dazu einmal den Vorgänger der elektronischen Zeiterfassung an:
    [Blockierte Grafik: https://upload.wikimedia.org/w…C3%BCrk_%28um_1930%29.jpg]
    Okay, ist ne Uhr die permanent mitläuft. Unser Ansatz stimmt also. Schauen wir uns zur Sicherheit noch einmal das personalisierte Gegenstück an.
    [Blockierte Grafik: http://www.megzeit.de/_pics/83…-ER-1500-Stempelkarte.jpg]
    Die alten Systeme haben also gar nicht permanent gezählt, sondern einfach nur den jeweils aktuellen Zeitpunkt gestempelt.
    Zeitpunkt gestempelt... Zeitstempel... Timestamp... Das kenne ich doch von irgendwo her... Timestamp in MSSQL
    Das Rad muss also nicht neu erfunden werden, irgendwo gibt es das schon.
    java.sql.Timestamp Gut, dafür jetzt SQL importieren, ich weiß nicht recht...
    java.lang.System.currentTimeMillis() Na bitte, das sieht doch prima aus. So ein Aufruf aus dem Sprachsystem kostet auch kaum Zeit bzw. liegt die Effizienz des Ganzen nicht in unserem Aufgabenbereich.


    Nun noch einmal die Karte ansehen, bevor wir loslegen.
    Sie stempelt Stunden und Minuten untereinander weg. Die Zeilen stehen wohl für das Datum. Das lässt sich ja an Hand des Zeitstempels wunderbar errechnen.
    Andererseits wird dieses hin- und herwandeln der Millisekunden in Datumsobjekte und die Berechnung von Zeitdifferenzen vielleicht ein bisschen viel Arbeit.
    Nein, den Spielkram mit den Millisekunden verwerfen wir wieder. Nehmen wir am Besten direkt java.util.Date.


    Mit diesem Wissen bewaffnet geht es sofort ans Werk, wir... Ja, was eigentlich?
    Da braucht nix im Hintergrund mitlaufen. Fürs Stempeln an sich müssen wir nichts berechnen. Eigentlich können wir den Kram doch auch gleich in die Methoden des Button stecken.
    Doch halt, wenn ich überhaupt keinen Bock habe jedes mal diese verfluchte App zu öffnen um mich zu stempeln? Wenn ich mir lieber ein Widget bauen möchte, das ganz unauffällig auf dem Homescreen liegt und mich auf Tastendruck ein- und ausstempeln soll?


    Das schreit geradezu nach einem Content Provider!


    Zitat von Stimme aus dem Off

    Moooooment mal. Einen Contentprovider? Nicht erst einmal eine Klasse und dann mal gucken was passiert und so? Test First!


    Jaaa, nee. Ich habe ja so ein bisschen im Hinterkopf, dass meine App diese Zeitstempel auch irgendwo lassen soll. Dass aus den generierten Zeitstempeln Berichte, Übersichten und Exporte erstellt werden sollen. Dass sie gesichert werden sollen. Auch im Backup auf einem Rechner oder synchronisiert mit Google.
    Es ist also bereits zu diesem frühen Punkt ersichtlich, dass es auf eine SQLite Datenbank hinaus läuft. Genau (aber nicht ausschließlich) dafür sind Content Provider bestens geeignet und wir werden im Laufe der Programmentwicklung auch sehen warum. Vor Allem werden wir es an den Unit Tests erkennen, die hier zum Einsatz kommen werden.

    Michael Hast Du für diesen Wert eine Quelle?


    ChampS Und Gifs und so, also auch Animationen. Nur dass bei einem Video die ganzen Informationen zu Dauer, Wiederholrate etc.pp. fest eincodiert sein sollten.

    Wo hat man denn FPS (frames per second) noch, die man auf 30 begrenzen möchte, wenn nicht bei irgendwie gearteten Animationen?
    Oder wofür könnte FPS im Zusammenhang mit 'auf 30 begrenzt' noch stehen?
    (30 First Person Shooter in einem View halte ich mal für unrealistisch...)


    Ich bin interessiert, weil ich gern über Tellerränder hinwegsehen möchte, mir aber nichts Missverständliches bekannt ist.

    Die Frage ist verhältnismäßig konkret. Ein View hat nur leider keine FPS. ^^
    Ich denke eher, Du möchtest die Animationen entsprechend 'verlangsamen', weil man bei einer Animationsgeschwindigkeit von 60 Einzelbildern pro Sekunde einfach keine Chance mehr hat auf das heranschießende Hindernis zu reagieren.
    (Viele Vögel hingegen würden sich noch langweilen... Aber man muss nicht wesentlich schneller gucken können als man sich fortbewegt.
    45km/h [schnellster Mensch auf ebener Erde aus eigener Kraft über 100 Meter] vs. 110km/h [Stadttaube, durchschnittliche Geschwindigkeit im Langstreckenflug])


    Für den Anfang sollte es vielleicht reichen, die Animationen auf eine gewisse Länge zu strecken.
    Du gibst leider keine konkreten Codeschnippsel, wie Du momentan die Animationen berechnest.
    Eventuell kann es schon helfen, statt alles in einer Schleife selbst zu platzieren die Schleife aus 30 Animationen zu je 1/30 Sekunde aufzubauen.


    Dann reagiert der Vogel zwar nicht mehr in Echtzeit, aber eventuell ist es zu vernachlässigen, weil man es eh nicht sieht.
    Falls das Timing nicht stimmt und er gefühlt eine halbe Sekunde zu spät reagiert gehen auch 60 Animationen zu 1/60 Sekunde oder 300 Animationen zu 1/300 Sekunde - irgendwie sowas halt.


    In OpenGL würde man 'einfach' die Zeit messen, die ins Land zieht wenn ein Animationszyklus durchlaufen ist, das auf die gewünschten Frames hochrechnen und anschließend eine entsprechende sleep(Millisekunden) Anweisung geschickt platzieren.
    Aber ich sehe keinen Grund hier nicht Androids Animationsmechanismus zu nutzen.


    (Dringend in den Developer Einstellungen des Testgerätes mal an den Animationswerten [Animation Duration Scale etc.) rumschrauben und im Code die Vorgaben überschreiben. Wäre sonst blöd, wenn man mit einer einfachen Selektion ganz entspannt 3 Frames pro Sekunde hat oder auf einem Dev-Gerät für UI Tests überhaupt nicht spielen kann.)

    Da es heute erfahrungsgemäß fast schwerer ist Graphiken überall gleich verpixelt aussehen zu lassen als gleich scharf:
    Was soll dieser Retro-Look? Gibts auf eBay keine 8-Bit Ur-NES mehr? (Hab nur den 16 Bit SNES, und der brennt schon auf Dauer in den Augen...)

    Zunächst mal ist es die Eigene.
    Bei den ganzen ThirdPartyEngines weißt Du nie, wann die mal die Muße haben zu optimieren oder zu patchen.


    Davon abgesehen ist eine eigene Engine so schwierig nicht (außer vielleicht in OpenGL ohne Hilfsframeworks - und auch da ist es eher Fleißarbeit und Mathematik denn Voodoo und Schwarze Magie) und diese dürfte sich auf Schwerkraft, Vorwärts- und Aufwärtsbewegung beim 'Flügelschlag' (Tab) zum Entgegenwirken der Schwerkraft und Kollisionsabfrage beschränken.
    Dafür 80MB Full-Feature-Physikengine für 3D mit Shadern, Weichzeichnern, Partikeleffekten und Anbindung an 7.1 Dolby Digital Soundgenerator im Paket mit herumzuschleppen ist dann vielleicht ein bisschen übertrieben.


    Da kann man richtig viel bei lernen und mächtig viel Spaß bei haben.
    Eine gewisse Frustrationstoleranz vorausgesetzt (zumindest in OpenGL ohne Hilfsframeworks...)

    Zwei gute Hinweise (und danke für's Pinnen).


    1) Aktuell kann der geübte Entwickler am Layout erkennen, dass das zwei ganz simple Systembuttons mit unterschiedlichen Texten sind, die auch noch weit voneinander entfernt sind.
    Nicht weit genug, um auf einem ldpi Display und ausladender Beschreibung auf Hebräisch das Überlappen der Buttons zu verhindern.
    Farben, Formen, Größe, Positionen sind allesamt (laut Test) vollkommen undefiniert. Fest steht nur:

    • 2 Buttons (wo auch immer platziert, wie auch immer gestaltet.)
    • Einer mit lokalisiertem Titel für 'Kommen', einer mit lokalisiertem Titel für 'Gehen'.
    • Beide sind nie gleichzeitig zu sehen.

    Alles Andere wurde vom UX Team noch nicht definiert (die UX Akzeptanztests in einer repräsentativen Gruppe von Freiwilligen laufen noch) und wird daher vom Test auch nicht abgedeckt.


    Man sieht schön: Tests verhindern Fehler nicht. Sie garantieren nur, dass das, was einmal galt, auch weiterhin gilt.


    2) Da hast Du natürlich völlig recht.
    So ein beherztes git log --graph --abbrev-commit --decorate --format=format:'%C(bold blue)%h%C(reset) - %C(bold cyan)%aD%C(reset) %C(bold green)(%ar)%C(reset)%C(bold yellow)%d%C(reset)%n'' %C(white)%s%C(reset) %C(dim white)- %an%C(reset)' --all hat die Übersicht schön und ist wesentlich performanter als die Apps.
    Ich hatte ursprünglich den Weg dieser Online-Maßnahme gewählt um es dem Interessenten möglichst einfach zu machen Änderungen auf Dateiebene verfolgen zu können.
    Leider hat mich meine Erinnerung da getrollt und mich die wirklich informative (und überladene) UI von BitBucket mit der simplen und zügigen (dafür leicht unübersichtlichen) UI von GitHub verwechseln lassen.


    Git-Profis, SCM-Freunde und alle, die täglich damit zu tun haben wissen auch, wie sie effektiv an die Informationen ran kommen.
    Als ich mit dem Kram angefangen habe, wurde CVS gerade von SVN abgelöst und keine Sau hat sich um Mercurial geschert. SVN war state-of-the-art und es war gelinde gesagt die Hölle, das irgendwie zum Laufen zu bekommen - trotz einiger fähiger UI Anwendungen.
    Sehen, benutzen, vergleichen ist da irgendwie einsteigerfreundlicher. Und das geht mit diesen Apps intuitiver als über GitHub (oder gar die Konsole.)

    Gut 24 Stunden nach dem ersten und 3 Stunden nach dem vorherigen Eintrag ist das Projekt so weit, dass diese doch recht simplen Tests reproduzierbar erfolgreich sind.


    Es ging mal wieder einiges schief. Die Syntax zum Feststellen der Nichtsichtbarkeit war falsch, beim Starten wurde der 'Gehen' Button noch mit angezeigt und die App setzte jedes Mal nach Beenden und neu starten seinen Buttonstatus zurück, so dass immer nur 'Kommen' angezeigt wurde und vormals laufende Tests fehl schlugen.
    Ich hätte die Tests natürlich dahingehend umbauen können, dass alle Buttoninteraktionen in einem Test abgearbeitet werden.
    Vielleicht mache ich das auch noch, um einen Stresstest zu simulieren oder so.
    In diesem Fall sehe ich aber ein, dass während so einem Arbeitstag eine Menge Dinge auf dem Endgerät passieren können, so dass die App durchaus auch mal weggeräumt werden kann.
    Also teste und implementiere ich das gleich richtig.


    So habe ich mit einem simplen UI Test auch gleich noch eine wichtige Kernfunktion implementiert, die auf dem ersten Blick nichts mit dem UI zu tun hatte.
    Hier sieht man auch, dass automatische UI Tests nur so gut sind wie sie geschrieben wurden. Es muss also nach wie vor im Entwicklungsprozess alles genau manuell getestet werden - danach dann aber nicht mehr.


    --


    Leider musste ich feststellen, dass GitHub nicht die gewünschte Oberflächengestaltung hat, wie sie beispielsweise BitBucket liefert.
    Das ist ärgerlich, denn so bekommt ihr nur über Umwege einen Überblick darüber, welche Änderungen an den Dateien vorgenommen wurden.


    Immerhin können die GitHub Desktop App, SourceTree oder GitKraken das ganze veranstaltete Chaos einigermaßen übersichtlich darstellen.


    Der große Vorteil dergestalter Darstellungen ist, dass sich niemand durch die kompletten Sourcen kämpfen muss, um die Änderungen selbst zu suchen sondern sie werden aufbereitet dargestellt.
    Beispielhafte Screenshots der Apps anbei, so könnt ihr euch ein eigenes Bild davon machen und entscheiden, ob ihr eine der genannten Apps nutzen wollt.


    GitHub App


    SourceTree


    GitKraken

    Sooo. Für sämtliche Verzögerungen entschuldige ich mich schon mal im Voraus.
    (Mein ordentliches Notebook mit einem reinen 64 Bit Linux kann nicht zur Entwicklung genutzt werden, da Teile des SDK zwingend 32 Bit erfordern und die notwendigen Bibliotheken nicht in 32 Bit bei der Distribution vorliegen. Also quäle ich mich mit Windows 10 auf 1.33GHz, 2GB RAM und 32GB SD rum. Kann man mit arbeiten, wenn man auf AVDs verzichtet. Dauert allerdings alles ein wenig länger.)


    Um mit dem UI Test zu beginnen, brauche ich persönlich nicht einmal ein existierendes UI, obwohl natürlich eine Defaultactivity angelegt wurde.
    Ich teste immer erst alles, bevor ich es baue.


    Klingt unlogisch, denn der Test kann ja nur fehlschlagen, wenn ich noch nichts programmiert habe. Aber so kann ich sehen, dass mein Test auch wirklich ausgeführt wird. Fehlschlagende Tests sind kein Problem, solange sie irgendwann wieder laufen. Nur vor Auslieferung müssen alle Tests durchlaufen.


    Zunächst fange ich mit drei Tests an:

    • Ist auch wirklich die richtige Activity offen?
    • Verhält sich der 'Kommen' Knopf so, wie ich es erwarte?
    • Verhält sich der 'Gehen' Knopf so, wie ich es erwarte?


    Wie ihr euch vorstellen könnt, machen die implementierten Tests (Branch 01UITest) noch nichts sinnvolles. Die wichtigen Ausgaben aus dem Log:


    Sieht nach unnützen Informationen aus, aber ich sehe, dass die Tests aufgerufen werden und funktionieren (a.k.a. fehlschlagen).


    Nun fange ich an zu prüfen, ob die Activity die richtige ist. Mangels Kreativität vergleiche ich den Titel der Activity mit dem hinterlegten Titel in der Sprachdatei.
    So vermeide ich auch Probleme beim Testen auf unterschiedlich lokalisierten Geräten.


    Zusammengezimmert sieht das Ganze dann so aus:

    Java
    @Test
        public void correctActivityOpened() {
    
    
            String activityTitle = mActivityRule.getActivity().getTitle().toString();
    
    
             assertEquals( mActivityRule.getActivity().getResources().getString(R.string.app_name), activityTitle );
        }


    Zitat

    22:51:33 Tests Failed: 1 passed, 2 failed


    Und schon ist 1/3 der Fehler beseitigt. :)


    Wahnsinn, seit ungefähr 3 Stunden gewerkelt, Null Produktivcode geschrieben und schon einen "Fehler" beseitigt. Wenn das nur immer so einfach wäre.
    Ist es aber natürlich nicht. Denn jetzt geht es um die Überlegung, wie die Buttons implementiert werden.
    Ja, die Buttons.


    Ich weiß nach wie vor nur, dass es prima ist, wenn der Button seine Form entsprechend ändert, ich weiß aber nicht, ob er seine Position ändern soll. Auch finde ich es übersichtlicher, wenn ein Button für genau eine Aktion da ist. Es ist auch weniger fehleranfällig, als das Austauschen von gesendeten Nachrichten zur Laufzeit. Hinzu kommt, dass auch blinde Menschen die App nutzen können sollen.
    (Ich kenne mehrere blinde Personen, die voll erwerbstätig sind und ein Smartphone benutzen. Meist ein iPhone, aber das liegt sicherlich nur daran, dass die 08-15 Entwickler unter Android Sehbehinderte einfach ignorieren.)
    Entsprechend tausche ich also nicht nur Aussehen und Nachricht des Buttons, sondern auch noch seinen via Talkback ausgegebenen Hinweistext.
    Das sind dann viel zu viele Änderungen, die im Nachhinein nur für Verwirrung sorgen und das ganze Programm anfällig für Flüchtigkeitsfehler machen.


    Also, da ich weiß, dass ich zwei Buttons habe und auch ungefähr weiß, wie ich diese Buttons nennen werde, kann ich auch die beiden Tests fertigstellen.


    Damit das kompiliert dürfen wir endlich ein UI erstellen. Aber wirklich nur zwei Buttons $irgendwo mit den beiden IDs vergeben.
    An der Positionierung wird erst geschraubt wenn wir wissen, wie sie sein soll!


    Nachdem das UI so kurz erledigt ist können wir das gewünschte zu testende Verhalten implementieren.
    OnClick Listener, die einfach sich selbst sichtbar und 'den Anderen' unsichtbar machen sollten reichen.


    Im zugehörigen Branch könnt ihr auch nachverfolgen, wie das Refactoring so vor sich ging. 8)

    "Kostenlos" bedeutet ja nur, dass jemand Anderes es zahlt. Ob Google (Firebase gehört zu Google) mit Euren Einnahmen und Ausgaben was Sinnvolles anfangen kann oder nicht, kann und will ich nicht beurteilen.


    Die genannten Services sind definitiv für größere Projekte gedacht. Andere Datenschutzrichtlinien, anderes Load Balancing, andere Reaktionszeiten. Sicherlich ein bisschen überdimensioniert für Dein Projekt.