Google Analytics en la aplicación de Android: se trata de múltiples actividades

Estaba muy emocionado de ver lo fácil que es configurar Google Analytics con mi aplicación, pero la falta de documentación me dejó con algunas preguntas. La única información que puedo encontrar proviene directamente de la documentación aquí , que solo analiza informes de páginas y eventos de una actividad. Deseo informar vistas de página y eventos a través de múltiples actividades en mi aplicación.

En este momento en el onCreate () de todas mis actividades, estoy llamando:

tracker = GoogleAnalyticsTracker.getInstance(); tracker.start("UA-xxxxxxxxx", this); 

Y en el onDestroy () de todas mis actividades:

  tracker.stop(); 

Luego hago un seguimiento de PageViews y Eventos según sea necesario y los envío junto con otra solicitud HTTP que estoy realizando. Pero no estoy tan seguro de que esta sea la mejor manera. ¿Debería llamar a start () y stop () en cada actividad, o solo debería llamar a start () y stop () en mi actividad de inicio principal?

El problema con llamar a start () / stop () en cada actividad (como lo sugiere Christian) es que da como resultado una nueva “visita” para cada actividad a la que el usuario navega. Si esto está bien para su uso, está bien, sin embargo, no es la forma en que la mayoría de la gente espera que las visitas funcionen. Por ejemplo, esto haría que la comparación de los números de Android con los números de la web o del iphone sea muy difícil, ya que una “visita” en la web y el iPhone se correlaciona con una sesión, no con una página / actividad.

El problema al llamar a start () / stop () en su aplicación es que da como resultado visitas inesperadamente largas, ya que Android no garantiza la finalización de la aplicación una vez que se cierra su última actividad. Además, si su aplicación hace algo con notificaciones o servicios, estas tareas en segundo plano pueden iniciar su aplicación y dar como resultado visitas “fantasma”. ACTUALIZACIÓN: stefano correctamente señala que onTerminate () nunca se llama en un dispositivo real, por lo que no hay lugar obvio para poner la llamada en stop ().

El problema al llamar a start () / stop () en una única actividad “principal” (como lo sugiere Aurora) es que no hay garantía de que la actividad se mantenga durante el tiempo que su usuario esté usando su aplicación. Si la actividad “principal” se destruye (por ejemplo, para liberar memoria), sus bashs posteriores de escribir eventos en GA en otras actividades fallarán porque la sesión se ha detenido.

Además, hay un error en Google Analytics hasta al menos la versión 1.2 que hace que mantenga una fuerte referencia al contexto que pasa a start (), lo que evita que se recolecte basura después de su destrucción. Dependiendo del tamaño de su contexto, esto puede ser una pérdida de memoria considerable.

La pérdida de memoria es bastante fácil de solucionar, se puede resolver llamando a start () utilizando la aplicación en lugar de la instancia de actividad en sí. Los documentos probablemente deberían actualizarse para reflejar esto.

p.ej. desde dentro de tu actividad:

 // Start the tracker in manual dispatch mode... tracker.start("UA-YOUR-ACCOUNT-HERE", getApplication() ); 

en lugar de

 // Start the tracker in manual dispatch mode... tracker.start("UA-YOUR-ACCOUNT-HERE", this ); // BAD 

Con respecto a cuándo llamar a start () / stop (), puede implementar un tipo de recuento manual de referencias, incrementando un conteo para cada llamada a Activity.onCreate () y decrementando para cada onDestroy (), luego llamando a GoogleAnalyticsTracker.stop () cuando el recuento llega a cero.

La nueva biblioteca EasyTracker de Google se encargará de esto por usted.

Alternativamente, si no puede subclasificar las actividades de EasyTracker, puede implementarlo manualmente en su propia clase base de actividades:

 public abstract class GoogleAnalyticsActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Need to do this for every activity that uses google analytics GoogleAnalyticsSessionManager.getInstance(getApplication()).incrementActivityCount(); } @Override protected void onResume() { super.onResume(); // Example of how to track a pageview event GoogleAnalyticsTracker.getInstance().trackPageView(getClass().getSimpleName()); } @Override protected void onDestroy() { super.onDestroy(); // Purge analytics so they don't hold references to this activity GoogleAnalyticsTracker.getInstance().dispatch(); // Need to do this for every activity that uses google analytics GoogleAnalyticsSessionManager.getInstance().decrementActivityCount(); } } public class GoogleAnalyticsSessionManager { protected static GoogleAnalyticsSessionManager INSTANCE; protected int activityCount = 0; protected Integer dispatchIntervalSecs; protected String apiKey; protected Context context; /** * NOTE: you should use your Application context, not your Activity context, in order to avoid memory leaks. */ protected GoogleAnalyticsSessionManager( String apiKey, Application context ) { this.apiKey = apiKey; this.context = context; } /** * NOTE: you should use your Application context, not your Activity context, in order to avoid memory leaks. */ protected GoogleAnalyticsSessionManager( String apiKey, int dispatchIntervalSecs, Application context ) { this.apiKey = apiKey; this.dispatchIntervalSecs = dispatchIntervalSecs; this.context = context; } /** * This should be called once in onCreate() for each of your activities that use GoogleAnalytics. * These methods are not synchronized and don't generally need to be, so if you want to do anything * unusual you should synchronize them yourself. */ public void incrementActivityCount() { if( activityCount==0 ) if( dispatchIntervalSecs==null ) GoogleAnalyticsTracker.getInstance().start(apiKey,context); else GoogleAnalyticsTracker.getInstance().start(apiKey,dispatchIntervalSecs,context); ++activityCount; } /** * This should be called once in onDestrkg() for each of your activities that use GoogleAnalytics. * These methods are not synchronized and don't generally need to be, so if you want to do anything * unusual you should synchronize them yourself. */ public void decrementActivityCount() { activityCount = Math.max(activityCount-1, 0); if( activityCount==0 ) GoogleAnalyticsTracker.getInstance().stop(); } /** * Get or create an instance of GoogleAnalyticsSessionManager */ public static GoogleAnalyticsSessionManager getInstance( Application application ) { if( INSTANCE == null ) INSTANCE = new GoogleAnalyticsSessionManager( ... ,application); return INSTANCE; } /** * Only call this if you're sure an instance has been previously created using #getInstance(Application) */ public static GoogleAnalyticsSessionManager getInstance() { return INSTANCE; } } 

El SDK ahora tiene una biblioteca externa que se ocupa de todo esto. Se llama EasyTracker. Solo puede importarlo y extender la Actividad o la Actividad de lista proporcionadas, crear un recurso de cadena con su código y listo.

El rastreador solo rastreará la actividad donde se ejecuta. Entonces, ¿por qué no subclasifica una Actividad que la inicia cada vez en onCreate ?

 public class GAnalyticsActivity extends Activity{ public void onCreate(Bundle icicle){ super.onCreate(icile); tracker = GoogleAnalyticsTracker.getInstance(); tracker.start("UA-xxxxxxxxx", this); } // same for on destroy } 

Luego, extiende esa clase para cada actividad que usa:

 public class YourActivity extends GAnalyticsActivity{ public void onCreate(Bundle icicle){ super.onCreate(icile); // whatever you do here you can be sure // that the tracker has already been started } } 

El enfoque que estoy usando es usar un Bound Service (ya estoy usando uno, así que me ahorré la creación de un código de placa de caldera adicional).

Un servicio limitado solo durará mientras haya actividades vinculadas a él. Todas las actividades de mi aplicación se vinculan a este servicio, por lo que solo duran mientras el usuario esté utilizando activamente mi aplicación, por lo que se trata de una “sesión” real.

Inicié el rastreador con una instancia única de Aplicación que amplié y agregué un método estático getInstance () para recuperar la instancia:

 // Non-relevant code removed public IBinder onBind(Intent intent) { tracker = GoogleAnalyticsTracker.getInstance(); tracker.startNewSession(PROPERTY_ID, MyApp.getInstance()); } public boolean onUnbind(Intent intent) { tracker.stopSession(); } 

Ver: http://developer.android.com/guide/topics/fundamentals/bound-services.html

Hice una división basada en el tiempo entre visitas en mi aplicación, trabajando así:

Construí un objeto Tracker singleton contenedor para GoogleAnalyticsTracker donde guardo la última vez que se rastreó algo. Si ese tiempo es más de x segundos, lo trato como una nueva visita.

Por supuesto, esto solo es útil si haces un seguimiento de todo en tu aplicación, y puede que no sea la mejor solución en cada situación, aunque funciona bien para mi aplicación.

Solo es compatible con trackPageView, pero setCustomVar y trackEvent deben implementarse fácilmente.

En cualquier lugar que necesite para rastrear algo simplemente agregue la línea:

  Tracker.getInstance(getApplicationContext()).trackPageView("/HelloPage"); 

Normalmente lo hago en el onResume de una actividad

La esencia del rastreador

Necesitará algo como esto: http://mufumbo.wordpress.com/2011/06/13/google-analytics-lags-on-android-how-to-make-it-responsive/

Eso está en la versión anterior y solía funcionar muy bien. Ahora estoy en la misma lucha que tú, ya que V2 no parece ser muy consistente.

Me pregunto si esto es algo que podría hacerse usando AOP.

Android solo puede usar métodos de AOP en tiempo de comstackción así que tal vez algo como AspectJ?

Hay un poco más de información sobre el uso de AspectJ en Android en este hilo . El principal problema es que aún necesitarías declarar en las clases que posees.