谷歌分析在Android应用-处理多个活动

时间:2021-04-10 15:21:19

I was pretty excited to see how easy it is to set up Google Analytics with my app, but the lack of documentation has me sitting with a few questions. The only information that I can find is right from the documentation here, which only looks at reporting PageViews and Events from one Activity. I want to report PageViews and Events across multiple Activities in my app.

我很兴奋地看到,用我的应用程序建立谷歌分析是多么容易,但是缺乏文档让我有一些问题。我能找到的唯一信息来自这里的文档,它只查看一个活动的报告页面视图和事件。我想在我的应用中报告多个活动的页面浏览量和事件。

Right now in the onCreate() of all of my activities, I am calling:

现在,在onCreate()中,我调用:

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

And in the onDestroy() of all of my activities:

在我所有的活动中:

    tracker.stop();

I then track PageViews and Events as needed, and Dispatch them along with another HTTP request I am performing. But I'm not so sure this is the best way. Should I be calling start() and stop() in each activity, or should I only call start() and stop() in my main launcher activity?

然后,根据需要跟踪页面视图和事件,并与另一个我正在执行的HTTP请求一起发送它们。但我不确定这是最好的方法。我应该在每个活动中调用start()和stop(),还是只在主启动程序活动中调用start()和stop() ?

7 个解决方案

#1


78  

The problem with calling start()/stop() in every activity (as suggested by Christian) is that it results in a new "visit" for every activity your user navigates to. If this is okay for your usage, then that's fine, however, it's not the way most people expect visits to work. For example, this would make comparing android numbers to web or iphone numbers very difficult, since a "visit" on the web and iphone maps to a session, not a page/activity.

在每个活动中调用start()/stop()的问题(如Christian所建议的)是,它导致用户导航到的每个活动都有一个新的“访问”。如果这对您的使用是合适的,那么这是可以的,然而,这并不是大多数人期望访问的方式。例如,将android号码与web或iphone号码进行比较非常困难,因为web上的“访问”和iphone映射到会话,而不是页面/活动。

The problem with calling start()/stop() in your Application is that it results in unexpectedly long visits, since Android makes no guarantees to terminate the application after your last activity closes. In addition, if your app does anything with notifications or services, these background tasks can start up your app and result in "phantom" visits. UPDATE: stefano properly points out that onTerminate() is never called on a real device, so there's no obvious place to put the call to stop().

在应用程序中调用start()/stop()的问题是,它会导致意外的长时间访问,因为Android不会保证在上次活动结束后终止应用程序。此外,如果你的应用程序使用通知或服务做任何事情,这些后台任务可以启动你的应用程序并导致“幻影”访问。更新:stefano正确地指出,onTerminate()在真正的设备上从来没有被调用,因此没有明显的位置来调用stop()。

The problem with calling start()/stop() in a single "main" activity (as suggested by Aurora) is that there's no guarantee that the activity will stick around for the duration that your user is using your app. If the "main" activity is destroyed (say to free up memory), your subsequent attempts to write events to GA in other activities will fail because the session has been stopped.

调用start()/停止的问题()在一个单一的“主要”活动(如极光)的建议是没有保证的活动会逗留期间,用户使用你的应用程序。如果“主要”活动破坏(比如释放内存),你的后续尝试写事件GA在其他活动将失败,因为会话已经停止。

In addition, there's a bug in Google Analytics up through at least version 1.2 that causes it to keep a strong reference to the context you pass in to start(), preventing it from ever getting garbage collected after its destroyed. Depending on the size of your context, this can be a sizable memory leak.

此外,谷歌分析中至少在1.2版本中有一个bug,它使它保持对您传入到start()的上下文的强引用,防止它在被销毁后收集垃圾。根据上下文的大小,这可能是一个相当大的内存泄漏。

The memory leak is easy enough to fix, it can be solved by calling start() using the Application instead of the activity instance itself. The docs should probably be updated to reflect this.

内存泄漏很容易修复,可以通过使用应用程序而不是活动实例本身调用start()来解决。应该更新文档以反映这一点。

eg. from inside your Activity:

如。在你的活动:

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

instead of

而不是

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

Regarding when to call start()/stop(), you can implement a sort of manual reference counting, incrementing a count for each call to Activity.onCreate() and decrementing for each onDestroy(), then calling GoogleAnalyticsTracker.stop() when the count reaches zero.

关于何时调用start()/stop(),您可以实现一种手动引用计数,为每个对Activity.onCreate()的调用递增一个计数,并为每个onDestroy()进行递减,然后在计数为0时调用GoogleAnalyticsTracker.stop()。

The new EasyTracker library from Google will take care of this for you.

谷歌的新EasyTracker库将为您处理这个问题。

Alternately, if you can't subclass the EasyTracker activities, you can implement this manually yourself in your own activity base class:

另外,如果您不能子类化EasyTracker活动,您可以在您自己的活动基类中手动实现这一点:

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

#2


17  

The SDK now has a external library which takes care of all of this. Its called EasyTracker. You can just import it and extend the provided Activity or ListActivity, create a string resource with your code and you are done.

SDK现在有一个外部库来处理所有这些。它叫EasyTracker。您可以导入它并扩展所提供的活动或ListActivity,使用代码创建一个字符串资源,您就完成了。

#3


5  

The tracker will only track the activity where it's executed. So, why don't you subclass an Activity which start it every time on onCreate:

跟踪器将只跟踪它执行的活动。那么,为什么不将每次在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
}

Then, you extends that class for every activity you use:

然后,对您所使用的每个活动扩展这个类:

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

#4


1  

The approach I am using is to use a Bound Service (I happen to be using one already so was spared the creation of extra boiler plate code.)

我正在使用的方法是使用绑定服务(我碰巧已经使用了绑定服务,所以不需要创建额外的锅炉板代码)。

A Bound Service will only last as long as there are Activities bound to it. All the activities in my app bind to this service, so it lasts only as long as the user is actively using my application - therefore very much a real 'session'.

绑定服务只有在绑定的活动存在时才能持续。我的应用程序中的所有活动都绑定到这个服务,所以只要用户在积极地使用我的应用程序,它就会持续,因此非常非常的“会话”。

I start the tracker with a singleton instance of Application which I have extended and added a static getInstance() method to retrieve the instance:

我使用一个应用程序的单例实例启动跟踪器,我扩展了该实例并添加了一个静态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();
}

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

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

#5


1  

I did a time based split between visits in my app, working like this:

我在我的应用程序中做了一个基于时间的分割,这样工作:

I've build a wrapper singleton Tracker object for the GoogleAnalyticsTracker where i keep the last time something got tracked. If that time's more then x seconds i treat it as a new visit.

我为GoogleAnalyticsTracker构建了一个包装器单例跟踪器对象,在这个对象中,我保存了上次跟踪的内容。如果时间超过了x秒,我就把它当作一次新的拜访。

Of course this is only useful if you track everything in your app, and may not be the best solution in every situation, works well for my app though.

当然,这只在你跟踪应用程序中的所有内容时才有用,而且在任何情况下都不是最好的解决方案,但对我的应用程序来说效果很好。

It only supports trackPageView, but setCustomVar and trackEvent should be easily implemented..

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

Anywhere you need to track something just add the line:

任何你需要追踪的地方,只要加上一行:

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

I usually do it in the onResume of an activity

我通常在活动的onResume中这样做

Tracker gist

追踪要点

#6


1  

You will need something like this: http://mufumbo.wordpress.com/2011/06/13/google-analytics-lags-on-android-how-to-make-it-responsive/

你需要这样的东西:http://mufumbo.wordpress.com/2011/06/13/google-analytics- on-android-how-to- do -it- responve/。

That's on the previous version and used to work very well. Now I'm in the same struggle as you, as V2 doesn't seems to be very consistent.

这是在以前的版本中,并且以前工作得很好。现在我和你们处于同样的困境中,因为V2看起来不太一致。

#7


0  

I wonder if this is something that could be done using AOP.

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

Android can only use compile-time AOP methods so maybe something like AspectJ?

Android只能使用编译时AOP方法,比如AspectJ?

There's a little more info on using AspectJ in Android in this thread. The main issue being that you would still need to declare on classes you own.

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

#1


78  

The problem with calling start()/stop() in every activity (as suggested by Christian) is that it results in a new "visit" for every activity your user navigates to. If this is okay for your usage, then that's fine, however, it's not the way most people expect visits to work. For example, this would make comparing android numbers to web or iphone numbers very difficult, since a "visit" on the web and iphone maps to a session, not a page/activity.

在每个活动中调用start()/stop()的问题(如Christian所建议的)是,它导致用户导航到的每个活动都有一个新的“访问”。如果这对您的使用是合适的,那么这是可以的,然而,这并不是大多数人期望访问的方式。例如,将android号码与web或iphone号码进行比较非常困难,因为web上的“访问”和iphone映射到会话,而不是页面/活动。

The problem with calling start()/stop() in your Application is that it results in unexpectedly long visits, since Android makes no guarantees to terminate the application after your last activity closes. In addition, if your app does anything with notifications or services, these background tasks can start up your app and result in "phantom" visits. UPDATE: stefano properly points out that onTerminate() is never called on a real device, so there's no obvious place to put the call to stop().

在应用程序中调用start()/stop()的问题是,它会导致意外的长时间访问,因为Android不会保证在上次活动结束后终止应用程序。此外,如果你的应用程序使用通知或服务做任何事情,这些后台任务可以启动你的应用程序并导致“幻影”访问。更新:stefano正确地指出,onTerminate()在真正的设备上从来没有被调用,因此没有明显的位置来调用stop()。

The problem with calling start()/stop() in a single "main" activity (as suggested by Aurora) is that there's no guarantee that the activity will stick around for the duration that your user is using your app. If the "main" activity is destroyed (say to free up memory), your subsequent attempts to write events to GA in other activities will fail because the session has been stopped.

调用start()/停止的问题()在一个单一的“主要”活动(如极光)的建议是没有保证的活动会逗留期间,用户使用你的应用程序。如果“主要”活动破坏(比如释放内存),你的后续尝试写事件GA在其他活动将失败,因为会话已经停止。

In addition, there's a bug in Google Analytics up through at least version 1.2 that causes it to keep a strong reference to the context you pass in to start(), preventing it from ever getting garbage collected after its destroyed. Depending on the size of your context, this can be a sizable memory leak.

此外,谷歌分析中至少在1.2版本中有一个bug,它使它保持对您传入到start()的上下文的强引用,防止它在被销毁后收集垃圾。根据上下文的大小,这可能是一个相当大的内存泄漏。

The memory leak is easy enough to fix, it can be solved by calling start() using the Application instead of the activity instance itself. The docs should probably be updated to reflect this.

内存泄漏很容易修复,可以通过使用应用程序而不是活动实例本身调用start()来解决。应该更新文档以反映这一点。

eg. from inside your Activity:

如。在你的活动:

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

instead of

而不是

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

Regarding when to call start()/stop(), you can implement a sort of manual reference counting, incrementing a count for each call to Activity.onCreate() and decrementing for each onDestroy(), then calling GoogleAnalyticsTracker.stop() when the count reaches zero.

关于何时调用start()/stop(),您可以实现一种手动引用计数,为每个对Activity.onCreate()的调用递增一个计数,并为每个onDestroy()进行递减,然后在计数为0时调用GoogleAnalyticsTracker.stop()。

The new EasyTracker library from Google will take care of this for you.

谷歌的新EasyTracker库将为您处理这个问题。

Alternately, if you can't subclass the EasyTracker activities, you can implement this manually yourself in your own activity base class:

另外,如果您不能子类化EasyTracker活动,您可以在您自己的活动基类中手动实现这一点:

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

#2


17  

The SDK now has a external library which takes care of all of this. Its called EasyTracker. You can just import it and extend the provided Activity or ListActivity, create a string resource with your code and you are done.

SDK现在有一个外部库来处理所有这些。它叫EasyTracker。您可以导入它并扩展所提供的活动或ListActivity,使用代码创建一个字符串资源,您就完成了。

#3


5  

The tracker will only track the activity where it's executed. So, why don't you subclass an Activity which start it every time on onCreate:

跟踪器将只跟踪它执行的活动。那么,为什么不将每次在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
}

Then, you extends that class for every activity you use:

然后,对您所使用的每个活动扩展这个类:

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

#4


1  

The approach I am using is to use a Bound Service (I happen to be using one already so was spared the creation of extra boiler plate code.)

我正在使用的方法是使用绑定服务(我碰巧已经使用了绑定服务,所以不需要创建额外的锅炉板代码)。

A Bound Service will only last as long as there are Activities bound to it. All the activities in my app bind to this service, so it lasts only as long as the user is actively using my application - therefore very much a real 'session'.

绑定服务只有在绑定的活动存在时才能持续。我的应用程序中的所有活动都绑定到这个服务,所以只要用户在积极地使用我的应用程序,它就会持续,因此非常非常的“会话”。

I start the tracker with a singleton instance of Application which I have extended and added a static getInstance() method to retrieve the instance:

我使用一个应用程序的单例实例启动跟踪器,我扩展了该实例并添加了一个静态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();
}

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

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

#5


1  

I did a time based split between visits in my app, working like this:

我在我的应用程序中做了一个基于时间的分割,这样工作:

I've build a wrapper singleton Tracker object for the GoogleAnalyticsTracker where i keep the last time something got tracked. If that time's more then x seconds i treat it as a new visit.

我为GoogleAnalyticsTracker构建了一个包装器单例跟踪器对象,在这个对象中,我保存了上次跟踪的内容。如果时间超过了x秒,我就把它当作一次新的拜访。

Of course this is only useful if you track everything in your app, and may not be the best solution in every situation, works well for my app though.

当然,这只在你跟踪应用程序中的所有内容时才有用,而且在任何情况下都不是最好的解决方案,但对我的应用程序来说效果很好。

It only supports trackPageView, but setCustomVar and trackEvent should be easily implemented..

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

Anywhere you need to track something just add the line:

任何你需要追踪的地方,只要加上一行:

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

I usually do it in the onResume of an activity

我通常在活动的onResume中这样做

Tracker gist

追踪要点

#6


1  

You will need something like this: http://mufumbo.wordpress.com/2011/06/13/google-analytics-lags-on-android-how-to-make-it-responsive/

你需要这样的东西:http://mufumbo.wordpress.com/2011/06/13/google-analytics- on-android-how-to- do -it- responve/。

That's on the previous version and used to work very well. Now I'm in the same struggle as you, as V2 doesn't seems to be very consistent.

这是在以前的版本中,并且以前工作得很好。现在我和你们处于同样的困境中,因为V2看起来不太一致。

#7


0  

I wonder if this is something that could be done using AOP.

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

Android can only use compile-time AOP methods so maybe something like AspectJ?

Android只能使用编译时AOP方法,比如AspectJ?

There's a little more info on using AspectJ in Android in this thread. The main issue being that you would still need to declare on classes you own.

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