Hallo! Ich heiße Sosnin Ilya. Ich arbeite bei Lamoda Android Entwickler. Ich male Schaltflächen, überspringe Listen und schreibe leider Analysen ...
Lamoda ist ein datengesteuertes Unternehmen, bei dem alle Entscheidungen auf der Grundlage des Benutzerverhaltens getroffen werden. Zuerst beobachten wir und ziehen erst dann Schlussfolgerungen. Daher ist es leicht zu erraten, dass wir Analysen haben und diese wirklich brauchen.
Wenn ich meinen
Bericht von Mitap Mosdroid # 18 Argon entschlüssele
, erkläre ich Ihnen, wie unser SDK
funktioniert und warum Reflexion nicht immer schlecht ist. Außerdem beantworte ich die Hauptfrage zu diesem Thema: "Wie implementiere ich Analysen, ohne die Anwendung zu beschädigen?".

Zunächst stelle ich Ihnen eine einfache Frage: "Wie denken Sie, wie viele Installationen haben wir bei Google Play?"
10 Millionen Installationen!
Indikator Anfang Juli 2019.
Neben der Tatsache, dass wir auf der Grundlage der Benutzer Schlussfolgerungen ziehen, haben wir auch interne Kunden, die sich auch für Analysen interessieren.

Zuallererst benötigt Marketing Analysen für seine eigenen Forschungszwecke. F & E kontrolliert unsere Suchanfragen auf Kosten der Produkte, und Produkte werden mit neuen Funktionen ausgeführt.
Zum Beispiel hatten wir eine Funktion, mit der wir das gesamte Bild als Ganzes erfassen konnten. Das heißt, Sie können nicht nur das Hemd kaufen, das Ihnen am Modell gefallen hat, sondern auch Hosen vom selben Bild. Wir haben uns entschlossen, es zunächst für IOS zu schreiben und dann darüber nachzudenken, ob wir es überhaupt brauchen. Sie schrieben, schauten und stellten sicher, dass es den Benutzern nicht gefiel.

Was sollte Ihrer Meinung nach mit Funktionen getan werden, die die Benutzer nicht benötigen?
Richtig, wirf sie weg! Dies ist besonders dann sinnvoll, wenn die Funktion an externe Dienste gebunden ist, da diese Probleme erhalten oder möglicherweise bezahlt werden. Dies geschah mit dieser Funktion. Wir haben weder Android noch Desktop implementiert, sondern beschlossen, es weiterzuentwickeln. (Vielleicht geht sie eines Tages in perfekterer Form zum Produkt).
Was ist die Schwierigkeit?
Unabhängig davon, wie lustig dies klingen mag, kann es bei der Einführung von Analysen
schwierig sein, mit den Analysten selbst zusammenzuarbeiten . In den meisten Fällen treten Konflikte auf, weil Sie nach Daten gefragt werden, über die Sie nicht verfügen. Und es endet immer damit, dass Sie immer noch eine Reihe von Parametern durch 10 Bildschirme ziehen müssen, um ihnen ein kleines Ereignis zu senden. Und das passiert ziemlich oft.
Die zweite Herausforderung ist das
Sammeln von Analysen . Wir haben diesen Prozess in 7 Systemen durchgeführt.

Einige Ereignisse gehen an ein System, andere an mehrere gleichzeitig ... Und es gibt eine solche Besonderheit, dass Ereignisse mit unterschiedlichen Parametern und in unterschiedlichen Formaten an unterschiedliche Systeme gesendet werden können. Natürlich möchten wir all diese Abhängigkeiten nicht wirklich auflösen.
LStat ist unser eigenes SDK (Lamoda Statistics). Dies ist ein massives System, das mehr als 60% der verschiedenen Ereignisse umfasst. Die Ereignisse, die weiter zu Google, Adjust, gehen, wurden häufig zunächst nur in LStat erfasst.
SDK
Unser SDK lautet wie folgt.

Es ragt ein sauberer LStat heraus, der innen aus zwei Teilen besteht: Lagerung und Versand. Wenn wir eine Veranstaltung abholen, senden wir sie nicht sofort. Andernfalls würde es zu viele Ereignisse und Anfragen geben, was nicht sehr praktisch ist. Deshalb legen wir alles in unsere kleine SQLite-Datenbank, in der wir alles speichern. In einigen Abständen zieht unsere Netzwerkschicht die Daten aus der Datenbank und sendet sie.
Nachdem wir vom Server eine Bestätigung erhalten haben, dass die Ereignisse eingetroffen sind, löschen wir unsere Datenbank. Dieser Vorgang findet regelmäßig statt. Dank dessen wächst unsere Basis nicht und wir garantieren die Lieferung aller Veranstaltungen. Wenn das Ereignis aus irgendeinem Grund nicht eingetroffen ist, wird es in unserer Datenbank gespeichert, bis eine Antwort vom Server empfangen wird.
Sammler
Wie ich bereits sagte, haben wir 7 Sammler. Sie bestehen aus solchen Methoden: benutzerdefinierte Annotation, EventHandler und AppStartEvent. Was denkst du, verfolgt diese Veranstaltung?

Dies ist natürlich ein Kaltstart für die Anwendung. Und die Hauptsache hier ist, dass wir eine AppStartEvent-Klasse haben, die von einer Ereignisschnittstelle erbt. Und warum brauchen wir das, werde ich etwas später erzählen.
Wie geht es dir Hier beginnt der Schlag, das Brennen und das Nachdenken.

Zuerst gehen wir alle unsere 7 Sammler durch. Dann ziehen wir die Java-Klasse und den CollectorName heraus, die wir später zum Speichern benötigen.
Außerdem nehmen wir aus dieser Java-Klasse alle unsere Methoden heraus, die in diesem Code enthalten sind. Jetzt müssen wir überprüfen und sicherstellen, dass unsere Methode eine Track-Event-Methode ist, die für die Speicherung verantwortlich ist. Zu diesem Zweck haben wir mehrere Parameter: Der erste ist, dass wir die Annotation @EventHandler haben, keine leere Liste von Parametern haben und ein Ereignis zur Eingabe kommt.
Alle Bedingungen sind erfüllt, sodass wir davon ausgehen können, dass diese Funktion bei uns ein Ereignis sein wird. Ich wickle es einfach in eine Hülle und sende es an unsere Sammlung.
Reflexion ist nicht immer schlecht
Ja, viele von Ihnen werden sagen, dass Reflexion schlecht, langsam und schrecklich ist.

Zunächst kann es entweder langsam oder schnell sein. Es gibt Methoden wie getFields, getConstructors, die im Vergleich zum Rest der Reflexion sehr schnell arbeiten. Und es gibt zum Beispiel den Konstruktor newInstance, der sehr langsam arbeitet. Mit dem Wort "langsam" meine ich den Unterschied zwischen der linken und der rechten Spalte in der obigen Tabelle um mehrere Größenordnungen (ungefähr hundertfacher Unterschied). Wenn Sie also verstehen, was Sie tun, und im Voraus wissen, worauf Sie vorbereitet sein müssen, ist nicht alles so beängstigend.
Wir ziehen mehr als 500 Methoden aus 7 Klassen . Und wir machen das nur einmal pro Sitzung. Die Zeit für eine Passage beträgt 40 Millisekunden. Dies sind weniger als 3 Frames (im Splash-Screen-Stadium). Und es war weit entfernt von einem Top-End-Gerät, sondern ein einfaches NTC für Android 6, das es schon seit vielen Jahren gibt.
Natürlich funktioniert auf einem Top-End-Gerät alles schneller. Und wenn wir über alte chinesische Telefone sprechen, beträgt die dort verbrachte Zeit 100 Millisekunden. Benutzer solcher Telefone sind bereits daran gewöhnt, dass für sie alles langsam funktioniert, sodass ihnen 40 Millisekunden oder 100 Millisekunden dort zutiefst gleichgültig sind. Was ist der Unterschied? Sie werden immer langsamer :)
Und jetzt die Hauptfrage: Wie kann man Analysen implementieren, um die Architektur nicht zu beschädigen?
Architekt
Unsere Anwendung verwendet MVP.

Dies ist unsere Art von Gottessenz, die auf ApplicationScope „lebt“ und genau dort injiziert wird, wo wir sie brauchen. Zum Beispiel müssen wir onClick () einzahlen. Um die Architektur nicht zu beschädigen, leiten wir das Ereignis nicht von der Ansichtsebene an Presenter weiter, damit es später irgendwohin geht. Stattdessen erledigen wir alles direkt aus der Ansicht und übergeben den Track an den AnalyticsManager.
Und jetzt ein bisschen über das Senden. Bei AnalyticsManager sticht eine Methode hervor - dies ist die Track-Methode, die jede Ereignisklasse als Eingabe akzeptiert. Und dann passiert schwarze Magie.

Diese Methode kann alle unsere Probleme lösen.
Erstens wird es hilfreich sein,
in mehreren verschiedenen Systemen einzuzahlen . Handler sind alle unsere Ereignisse, die jemals gesammelt werden. Als nächstes suchen wir hier nach der gewünschten Methode. Wenn wir also ein Track-Ereignis beispielsweise in 4 Sammlern geschrieben haben, wird es in 4 Kopien gespeichert. Das heißt, in 4 Durchgängen des Zyklus werden wir es finden und an alle 4 Systeme mit den entsprechenden Parametern senden.
Zweitens
hilft es, das Problem mit einmaligen Ereignissen zu lösen . Dies sind solche Ereignisse, die für den gesamten Zyklus der Anwendung nur einmalig verpfändet werden müssen. Markieren Sie e.once, die übliche boolesche Variable. Wenn wir sagen, dass dies ein einmaliges Ereignis ist, löschen wir es einfach aus der Sammlung. Was passiert als nächstes, wenn wir versuchen, es erneut zu verpfänden? Offensichtlich werden wir es in dieser Sammlung einfach nicht finden. Sie können versuchen, sich so viel in den Fuß zu schießen, wie Sie möchten, während Sie weiterhin analyseManager.track (AppStartEvent ()) schreiben. Es wird immer noch einmal gehen und es wird keine mehr geben.
Was ist der Gewinn?
1.
Wir brechen die Architektur unserer Anwendung nicht , da der AnalyticsManager außerhalb der Architektur liegt und arbeitet. Dies ermöglicht es uns, es in einen beliebigen Teil der Anwendung einzubetten.
2.
Ermöglicht das Sammeln von Ereignissen in einer Zeile in einer beliebigen Anzahl von Analysesystemen. Dazu schreiben wir einfach: analyseManager.track (Event ()). Denn dann entscheidet er selbst wo, in welcher Menge, mit welchen Parametern, wann und so weiter.
3.
Löst das Problem der "einmaligen Ereignisse" . Jetzt müssen wir nicht mehr verschiedene Arten von Überprüfungen durchführen. Einmal gesendet, entfernt, und wir werden ihn nicht wieder treffen.
4.
Löst das Problem, dasselbe Element in verschiedenen Systemen zu sammeln . Aufgrund der Tatsache, dass wir alles in verschiedenen Sammlern geschrieben haben, ging es an verschiedene Sammler. Sie müssen also nicht auf unnötige Gesten zurückgreifen. Meiner Meinung nach ist das wunderbar.
Testen ...
Wir testen manuell. Warum nicht automatisieren? Und dann stellt sich eine traurige Sache heraus.
Erstens werden Sie dies normalerweise normalerweise nicht mit einem Komponententest abdecken. Da die meisten Probleme mit Ereignissen in der Analyse nicht auftreten, weil einige der Parameter nicht erfasst wurden. Nach meiner persönlichen Erfahrung treten 90% der Probleme auf, weil Sie die Veranstaltung nicht dorthin gesendet haben, wo sie hingehört. Solche Fälle können nur mit UI-Tests abgefangen werden, die wir für all dies noch nicht geschrieben haben.
Und zweitens ruhen wir uns vorerst ein wenig auf der Tatsache aus, dass Analysten Ereignisse in einem ziemlich strengen Format (im Zusammenfluss) beschreiben, aber dieses Format ist kein striktes Spezifikationsformat wie Swagger. Dementsprechend gibt es geringfügige Abweichungen, es gibt Duplikate (obwohl in diesem Fall häufig nur ein Link zu einer anderen Seite erstellt wird). Dies schränkt uns bisher in den Möglichkeiten der Automatisierung von Testanalysen ein. Aber wir arbeiten daran.
Schlussfolgerungen
Was kann mit dieser Entscheidung noch getan werden?
1.
Schreiben Sie Autotests für die Analyse . Dies erfordert viel Vorarbeit. Man kann aber nicht sagen, dass dies unmöglich ist.
2. Die Plus-Minus-Track-Methoden sind ziemlich ähnlich. In jedem generieren wir einige „universelle“ Parameter, die jeder benötigt. Und sammeln Sie einfach die Karte der Werte. Im Allgemeinen kann man
entweder ein Plug-In oder ein Dienstprogramm schreiben . Nicht wichtig. Hauptsache sie generiert ein Track Event für die jeweiligen Klassen.
Hier kann jedoch ein kleines Problem auftreten, wenn die Ereignisse etwas anders verschwinden (z. B. für verschiedene Analysesysteme). Aus diesem Grund kann nicht alles angemessen gelöst werden. Außerdem möchte ich keine unnötige Codegenerierung in einem Projekt erzeugen, das es in vielen Projekten auf Android so viele gibt (hallo Dagger, Moksi und andere, die an Codegen arbeiten).
3.
Integration der Anwendung in die Spezifikationen der Analysten . Wahrscheinlich ist dies ein zu transzendentaler Traum, aber dennoch ... Wir möchten wirklich, dass unsere Analysten in einem strengen Format schreiben, und wir könnten ihre Kunstwerke analysieren und integrieren. Dann würde Frieden und Harmonie kommen. Jeder würde sich freuen :)
Was möchte ich zu all dem sagen?
Erstens ist noch eine Analyse erforderlich. Weil Sie damit Geld, Ressourcen und Aufwand sparen können. Dies erspart Ihnen das Schreiben von unnötigem Code oder das Löschen von Code, der nicht alt zu sein scheint, aber hier immer noch nicht benötigt wird. Weniger Vermächtnis ist immer gut.
Zweitens ist Reflexion nicht immer schlecht. Ja, es ist langsam. Aber manchmal verlieren wir sehr wenig an Leistung, aber wir bekommen viel, zum Beispiel im Bereich der Entwicklung und Fehlerbehandlung.
Und drittens legen wir die Analyse in die Phase der Feature-Planung. Aus diesem Grund können wir viel früher mit Analysten verhandeln und einen Kompromiss erzielen. Außerdem haben wir die Möglichkeit, den Zeitaufwand für das Schreiben von Analysen im Voraus abzuschätzen.