Android应用中的Google Analytics(分析) – 处理多个活动

我很高兴看到用我的应用程序设置Google Analytics是多么容易,但缺less文档让我坐在几个问题上。 我能find的唯一信息就是来自这里的文档,它只查看来自一个Activity的报表PageViews和Events。 我想在我的应用程序的多个活动中报告PageViews和事件。

现在在我的所有活动的onCreate()中,我打电话给:

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

在我的所有活动的onDestroy()中:

  tracker.stop(); 

然后,我会根据需要跟踪PageViews和事件,并将其与我正在执行的另一个HTTP请求一起分发。 但我不太确定这是最好的方法。 我应该在每个活动中调用start()和stop(),还是只在我的主要启动器活动中调用start()和stop()?

在每个活动中调用start()/ stop()的问题(正如Christian所build议的那样)是导致用户导航到的每个活动的新“访问”。 如果您的使用情况没有问题,那么这很好,但是,这并不是大多数人期望的访问方式。 例如,这将使得比较android号码到web或iphone号码非常困难,因为web和iphone上的“访问”映射到会话而不是页面/活动。

在你的应用程序中调用start()/ stop()的问题是,它会导致意外长时间的访问,因为Android不保证在你的上一次活动closures后终止应用程序。 另外,如果您的应用程序对通知或服务做任何事情,这些后台任务可以启动您的应用程序,并导致“幻影”访问。 更新:stefano正确地指出, onTerminate()永远不会被称为一个真正的设备,所以没有明显的地方把电话停止()。

在单个“主”活动(如Aurorabuild议的)中调用start()/ stop()的问题在于,不能保证在用户使用您的应用程序的过程中活动将保持不变。 如果“主”活动被破坏(比如释放内存),那么随后在其他活动中将事件写入GA的尝试将失败,因为会话已经停止。

另外,Google Analytics(分析)中至less存在1.2版本的一个错误,这个错误会导致它强烈地引用您传递给start()的上下文,从而阻止它在被销毁后被垃圾回收。 根据你的上下文的大小,这可能是一个巨大的内存泄漏。

内存泄漏很容易修复,可以使用Application调用start()来代替activity本身。 该文档可能应该更新,以反映这一点。

例如。 从你的活动内部:

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

代替

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

关于何时调用start()/ stop(),您可以实现一种手动引用计数,对每个调用Activity.onCreate()增加一个计数,然后递减每个onDestroy(),然后调用GoogleAnalyticsTracker.stop()计数达到零。

Google提供的新EasyTracker库将为您提供帮助。

或者,如果您不能inheritanceEasyTracker活动,则可以在您自己的活动基类中手动实现:

 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; } } 

SDK现在有一个外部库,负责所有这些。 它被称为EasyTracker。 你可以导入它并扩展提供的Activity或者ListActivity,用你的代码创build一个string资源,你就完成了。

跟踪器只会跟踪执行的活动。 那么,为什么不分类一个每次onCreate都启动它的Activity:

 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 } 

然后,您为每个使用的活动扩展该类:

 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 } } 

我正在使用的方法是使用绑定服务(我碰巧已经使用了一个,所以没有创build额外的锅炉板代码。)

只要有与之相关的活动,绑定服务将持续。 我的应用程序中的所有活动都绑定到此服务,所以只要用户正在使用我的应用程序,它就会持续下去 – 因此非常真实的“会话”。

我使用一个单例实例来启动跟踪器,我已经扩展并添加了一个静态getInstance()方法来检索实例:

 // 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(); } 

请参阅: http : //developer.android.com/guide/topics/fundamentals/bound-services.html

我在我的应用程序之间进行了访问时间分隔,如下所示:

我为GoogleAnalyticsTracker构build了一个包装单体追踪器对象,在那里我保持最后一次被追踪的东西。 如果那个时间多于x秒我把它作为一个新的访问。

当然,这只有在你跟踪应用程序中的所有内容时才有用,可能并不是在任何情况下都是最好的解决scheme。

它只支持trackPageView,但setCustomVar和trackEvent应该很容易实现..

任何你需要跟踪的东西只需添加一行:

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

我通常在一个活动的onResume中做

追踪器的要点

您将需要这样的东西: http : //mufumbo.wordpress.com/2011/06/13/google-analytics-lags-on-android-how-to-make-it-responsive/

这是在以前的版本,并习惯于工作得很好。 现在我和你一样挣扎,因为V2似乎并不一致。

我想知道这是否可以使用AOP来完成。

Android只能使用编译时AOP方法,所以也许像AspectJ?

在这个线程中有更多关于在Android中使用AspectJ的信息。 主要的问题是你仍然需要在你自己的类上声明。