Hola Me llamo Sosnin Ilya. Trabajo en el desarrollador de Android Lamoda. Pinto botones, salto listas y, desafortunadamente, escribo análisis ...
Lamoda es una empresa basada en datos en la que todas las decisiones se toman en función del comportamiento del usuario. Primero observamos y solo luego sacamos conclusiones. Por lo tanto, es fácil adivinar que tenemos análisis y realmente lo necesitamos.
Al descifrar mi
informe de mitap Mosdroid # 18 Argon, le diré cómo funciona nuestro SDK y por qué la reflexión no siempre es mala. Y también responderé la pregunta principal de este tema: "¿Cómo implementar análisis y no romper la aplicación?".

Para comenzar, te haré una pregunta simple: "¿Cómo piensas, cuántas instalaciones tenemos en Google Play?"
¡10 millones de instalaciones!
Indicador a principios de julio de 2019.
Además del hecho de que sacamos conclusiones basadas en los usuarios, también tenemos clientes internos que también están interesados en el análisis.

En primer lugar, el marketing necesita análisis para sus propios fines de investigación. R&D controla nuestras consultas de búsqueda a expensas de esto, y los productos se ejecutan con nuevas funciones.
Por ejemplo, teníamos una función que nos permitía recopilar toda la imagen como un todo. Es decir, puede comprar no solo la camisa que le gustó en el modelo, sino también pantalones de la misma imagen. Decidimos que, para empezar, lo escribiremos para iOS, y luego pensaremos si lo necesitamos. Escribieron, buscaron y se aseguraron de que a los usuarios no les gustara.

¿Qué crees que debería hacerse con las funciones que las personas no necesitan?
Bien, ¡tíralos! Esto vale especialmente cuando la función está vinculada a servicios externos, ya que tienden a recibir problemas o pueden ser pagados. Esto sucedió con esta característica. No implementamos Android ni Desktop, sino que decidimos evolucionarlo. (Tal vez algún día ella irá al producto en una forma más perfecta).
Cual es la dificultad?
No importa cuán divertido pueda sonar esto, al introducir análisis, puede ser
difícil trabajar con los propios analistas . La mayoría de las veces, surgen conflictos porque solicitan datos que usted no tiene. Y siempre termina con el hecho de que todavía tiene que arrastrar un grupo de parámetros a través de 10 pantallas para enviarles un pequeño evento. Y esto sucede con bastante frecuencia.
El segundo desafío es
recopilar análisis . Tenemos este proceso llevado a cabo en 7 sistemas.

Algunos eventos entran en un sistema, otros en varios a la vez ... Además, existe tal característica que los eventos con diferentes parámetros y en diferentes formatos pueden ir a diferentes sistemas. Por supuesto, no queremos resolver todas estas dependencias.
LStat es nuestro propio SDK (estadísticas de Lamoda). Este es un sistema masivo, que toma más del 60% de varios eventos. Los eventos que van más allá de Google, Adjust, a menudo se recopilaron inicialmente solo en LStat.
SDK
Nuestro SDK es el siguiente.

Sobresale un LStat limpio, que en su interior consta de dos partes: almacenamiento y envío. Cuando recopilamos un evento, no lo enviamos de inmediato. De lo contrario, habría demasiados eventos y solicitudes, lo que no es muy conveniente. Por lo tanto, ponemos todo en nuestra pequeña base de datos SQLite, donde almacenamos todo. Luego, en algunos intervalos, nuestra capa de red extrae los datos de la base de datos y los envía.
Después de recibir la confirmación del servidor de que los eventos han llegado, borramos nuestra base de datos. Este proceso ocurre regularmente. Gracias a esto, nuestra base no crece y garantizamos la entrega de todos los eventos. Si por alguna razón el evento no ha llegado, se almacenará en nuestra base de datos hasta que se reciba una respuesta del servidor.
Coleccionistas
Como dije anteriormente, tenemos 7 coleccionistas. Consisten en tales métodos: anotación personalizada, EventHandler y AppStartEvent. ¿Qué crees que sigue este evento?

Por supuesto, este es un comienzo en frío para la aplicación. Y lo principal aquí es que tenemos una clase AppStartEvent que hereda de alguna interfaz de eventos. Y por qué necesitamos esto, lo contaré un poco más tarde.
Como va Aquí es donde comienza el golpe, el ardor y la reflexión.

Primero revisamos todos nuestros 7 coleccionistas. Luego sacamos la clase Java y collectorName, que necesitamos más tarde para el almacenamiento.
Además, de esta clase de Java, sacamos todos nuestros métodos que están en este código. Ahora tenemos que verificar y asegurarnos de que nuestro método sea un método de seguimiento de eventos que será responsable del almacenamiento. Para hacer esto, tenemos varios parámetros: el primero es que tenemos la anotación @EventHandler, no tenemos una lista vacía de parámetros y algún evento llega a la entrada.
Se cumplen todas las condiciones, por lo que podemos suponer que esta función será un evento con nosotros. Simplemente lo envuelvo en un contenedor y lo envío a nuestra colección.
La reflexión no siempre es mala.
Sí, muchos de ustedes dirán que la reflexión es mala, lenta, terrible.

Para empezar, puede ser lento o rápido. Existen métodos como getFields, getConstructors que funcionan muy rápidamente en relación con el resto de la reflexión. Y hay, por ejemplo, Constructor newInstance, que funciona muy lentamente. Por la palabra "lento" me refiero a la diferencia entre las columnas izquierda y derecha en la tabla de arriba por varios órdenes de magnitud (aproximadamente una diferencia de cien veces). Por lo tanto, si comprende lo que está haciendo y sabe de antemano para qué necesita estar preparado, entonces no todo es tan aterrador.
Estamos sacando más de 500 métodos de 7 clases . Y lo hacemos solo una vez por sesión. El tiempo necesario para completar un pasaje es de 40 milisegundos. Esto es menos de 3 cuadros (en la etapa de pantalla de bienvenida). Y estaba lejos de ser un dispositivo de gama alta, sino un simple NTC en Android 6, que ha existido durante muchos años.
Por supuesto, que en un dispositivo de gama alta todo funcionará más rápido. Y si hablamos de teléfonos chinos antiguos, entonces el tiempo que pase allí será de 100 milisegundos condicionales. Los usuarios de estos teléfonos ya están acostumbrados al hecho de que todo funciona lentamente para ellos, por lo que son profundamente indiferentes a 40 milisegundos o 100 allí. ¿Cuál es la diferencia? Todavía se ralentizan :)
Y ahora la pregunta principal: ¿cómo implementar análisis para no romper la arquitectura?
Arquitecto
Nuestra aplicación usa MVP.

Este es nuestro tipo de esencia divina, que "vive" en ApplicationScope y se inyecta exactamente donde la necesitamos. Por ejemplo, necesitamos depositar en Click (). Para no romper la arquitectura, no reenviaremos el evento desde la capa Vista al Presentador, para que luego vaya a algún lado. En cambio, hacemos todo directamente desde la Vista y pasamos la pista al Administrador de análisis.
Y ahora un poco sobre enviar. En AnalyticsManager se destaca un método: este es el método de seguimiento, que acepta cualquier clase de evento como entrada. Y luego sucede la magia negra.

Este método es capaz de resolver todos nuestros problemas.
En primer lugar, ayudará a
depositar en varios sistemas diferentes . Los controladores son todos nuestros eventos que alguna vez se recopilarán. A continuación, buscamos el método deseado aquí. En consecuencia, si tenemos un evento de pista escrito, por ejemplo, en 4 recopiladores, se almacenará en 4 copias. Es decir, en 4 pasadas del ciclo lo encontraremos y lo enviaremos a los 4 sistemas con los parámetros correspondientes.
En segundo lugar,
ayuda a resolver el problema con eventos únicos . Estos son eventos que se deben prometer estrictamente 1 vez para todo el ciclo de la aplicación. Marque una vez, la variable booleana habitual. Si decimos que se trata de un evento único, simplemente lo eliminamos de la colección. ¿Qué sucede después si intentamos empeñarlo nuevamente? Obviamente, simplemente no lo encontraremos en esta colección. Puedes intentar dispararte en el pie tanto como quieras mientras continúas escribiendo analyticsManager.track (AppStartEvent ()), todavía se ejecutará una vez y no habrá más.
¿Cuál es el beneficio?
1.
No rompemos la arquitectura de nuestra aplicación , ya que AnalyticsManager se encuentra y funciona fuera de la arquitectura. Esto nos permite incrustarlo en cualquier parte de la aplicación.
2.
Le permite recopilar cualquier evento en una línea en cualquier número de sistemas de análisis. Para hacer esto, simplemente escribimos: analyticsManager.track (Event ()). Porque entonces él mismo resuelve dónde, en qué cantidad, con qué parámetros, cuándo, etc.
3.
Resuelve el problema de los "eventos únicos" . Ahora no necesitamos hacer varios tipos de controles. Una vez enviado, eliminado, y no volveremos a verlo.
4.
Resuelve el problema de recopilar el mismo elemento en diferentes sistemas . Debido al hecho de que escribimos todo en diferentes coleccionistas, fue a diferentes coleccionistas. Por lo tanto, no tiene que recurrir a gestos innecesarios. En mi opinión, esto es maravilloso.
Prueba ...
Probamos manualmente. ¿Por qué no automatizar, preguntas? Y luego resulta una cosa triste.
En primer lugar, desafortunadamente, normalmente no cubrirá esto con una prueba unitaria. Debido a que la mayoría de los problemas con los eventos en análisis no surgen debido al hecho de que algunos de los parámetros que no reunió. En mi experiencia personal, el 90% de los problemas surgen porque no enviaste el evento a donde se suponía que debía ir. Tales casos solo pueden detectarse con pruebas de IU, que aún no hemos escrito para todo esto.
Y en segundo lugar, por el momento descansamos un poco en el hecho de que los analistas describen los eventos en un formato bastante estricto (en confluencia), pero este formato no es un formato de especificación estricto, como Swagger. En consecuencia, hay ligeras discrepancias, hay duplicados (aunque en este caso, a menudo solo se hace un enlace a otra página). Hasta ahora, esto nos limita en las posibilidades de automatización de las pruebas analíticas. Pero estamos trabajando en eso.
Conclusiones
¿Qué más se puede hacer con esta decisión?
1.
Escribir autotests para análisis . Esto requerirá mucho trabajo preparatorio. Pero no se puede decir que esto es imposible.
2. Los métodos de seguimiento más-menos son bastante similares. En cada uno, generamos algunos parámetros "universales" que todos necesitamos. Y solo recolecta el mapa de valores. En general, se podría
escribir un complemento o una utilidad . No importa Lo principal es que ella genera un evento de seguimiento para las clases respectivas.
Pero aquí puede surgir un pequeño problema si los eventos comienzan a desaparecer de manera un poco diferente (por ejemplo, para diferentes sistemas de análisis). Debido a esto, no todo se puede resolver adecuadamente. Y además, no quiero producir generación de código innecesaria en un proyecto, que hay tantos en muchos proyectos en Android (hola Dagger, Moksi y otros que trabajan en codegen).
3.
Integración de la aplicación con especificaciones de analistas . Probablemente, este es un sueño demasiado trascendental, pero aún así ... Realmente nos gustaría que nuestros analistas escribieran en un formato estricto, y pudiéramos analizar sus obras de arte e integrarlas. Entonces vendría la paz y la armonía. Todos serían felices :)
Entonces, ¿qué quiero decir con todo esto?
Primero, todavía se necesita análisis. Porque le permite ahorrar dinero, recursos y esfuerzo. Esto le evita escribir código innecesario o eliminar código que parece no ser antiguo, pero que todavía no es necesario aquí. Menos legado siempre es bueno.
En segundo lugar, la reflexión no siempre es mala. Si, es lento. Pero a veces perdemos muy poco en rendimiento, pero obtenemos mucho, por ejemplo, en el campo del desarrollo y el manejo de errores.
Y en tercer lugar, estamos colocando el análisis en la etapa de planificación de características. Debido a esto, podemos negociar con analistas mucho antes, llegando a un compromiso. Y también nos da la oportunidad de estimar el tiempo requerido para escribir análisis por adelantado.