Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
142 views
in Technique[技术] by (71.8m points)

java - Google Analytics - NetworkOnMainThreadException on send in AsyncTask

I have to implement Google Analytics in the app I'm working on. I'm using Android Studio.

I'm not quite sure yet if I should implement sending tracker from every Activity, or if doing it once in Application class is enough, but that's another story. At the moment Google Analytics is implemented in the Application class. It tries to connect to something (although I've set dryRun), fails, and then says nothing unless I cause my app to crash. Then I get NetworkOnMainThreadException error when it tries to dispatch events.

Where is it trying to dispatch the events while on dryRun, causing NetworkOnMainThreadException no less? Shouldn't it dispatch everything into Logcat while on dryRun? And how should I deal with it both on dryRun and not?

At first I tried to implement it just like that, without an additional AsyncTask class, assuming I'll get everything dumped to Logcat, but when I got the NetworkOnMainThreadException error for the first time, I implemented the AsyncTask class.

LogCat:

01-15 13:06:21.835    1787-1800/com.example.app W/GAV4﹕ Thread[GAThread,5,main]: Service unavailable (code=1), will retry.
01-15 13:06:26.847    1787-1808/com.example.app W/GAV4﹕ Thread[Service Reconnect,5,main]: Service unavailable (code=1), using local store.
01-15 13:07:12.695    1787-1787/com.example.app E/GAV4﹕ Thread[main,5,main]: Error dispatching all events on exit, giving up: android.os.NetworkOnMainThreadException
        at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1117)
        at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.close(OpenSSLSocketImpl.java:926)
        at org.apache.http.impl.SocketHttpClientConnection.shutdown(SocketHttpClientConnection.java:183)
        at org.apache.http.impl.conn.DefaultClientConnection.shutdown(DefaultClientConnection.java:150)
        at org.apache.http.impl.conn.AbstractPooledConnAdapter.shutdown(AbstractPooledConnAdapter.java:169)
        at org.apache.http.impl.conn.AbstractClientConnAdapter.abortConnection(AbstractClientConnAdapter.java:378)
        at org.apache.http.impl.client.DefaultRequestDirector.abortConnection(DefaultRequestDirector.java:1031)
        at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:530)
        at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:555)
        at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:509)
        at com.google.android.gms.analytics.h.a(Unknown Source)
        at com.google.android.gms.analytics.h.a(Unknown Source)
        at com.google.android.gms.analytics.ag.dispatch(Unknown Source)
        at com.google.android.gms.analytics.w.eD(Unknown Source)
        at com.google.android.gms.analytics.w.dispatch(Unknown Source)
        at com.google.android.gms.analytics.x$b.run(Unknown Source)
        at com.google.android.gms.analytics.x.dY(Unknown Source)
        at com.google.android.gms.analytics.GoogleAnalytics.dY(Unknown Source)
        at com.google.android.gms.analytics.ExceptionReporter.uncaughtException(Unknown Source)
        at java.lang.ThreadGroup.uncaughtException(ThreadGroup.java:693)
        at java.lang.ThreadGroup.uncaughtException(ThreadGroup.java:690)
        at dalvik.system.NativeStart.main(Native Method)

Application class (relevant parts):

public class App extends Application {

private static App context = null;

private static final String PROPERTY_ID = "UA-xxxxxxxx-1";

private GoogleAnalytics analytics;

private Tracker t;

public void onCreate() {
    super.onCreate();
    if (context == null)
        context = (App) getApplicationContext();
    try {
        analytics = GoogleAnalytics.getInstance(context);
        t = analytics.newTracker(R.xml.app_tracker);
        new SendTrackerInBg(t);
    }
    catch (Exception e) {e.printStackTrace();}


}

SendTrackerInBg:

public class SendTrackerInBg extends AsyncTask<Tracker, Void, Integer> {

    SendTrackerInBg(Tracker t)
    {
        this.execute(t);
    }

    @Override
    protected Integer doInBackground(Tracker... trackers) {
        try {
            trackers[0].send(new HitBuilders.AppViewBuilder().build());
            return 0;
        }
        catch (Exception e) {
            e.printStackTrace();
            return -1;
        }
    }
}

app_tracker.xml (relevant parts):

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <integer name="ga_sessionTimeout">300</integer>

    <!-- Enable automatic Activity measurement -->
    <bool name="ga_autoActivityTracking">true</bool>

    <!-- The screen names that will appear in reports -->
    <screenName name="com.example.app.App">
        Application
    </screenName>
    <string name="ga_sampleFrequency">300.0</string>
    <string name="ga_trackingId">UA-xxxxxxxx-1</string>
    <bool name="ga_reportUncaughtExceptions">true</bool>
</resources>

analytics_global_config.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <bool name="ga_dryRun">true</bool>
    <string name="ga_logLevel">verbose</string>
</resources>

Manifest (relevant parts):

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.app"
android:versionCode="00"
android:versionName="0.00" >

<uses-sdk
    android:minSdkVersion="14"
    android:targetSdkVersion="19" />

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />

<permission
    android:name="com.example.app.permission.C2D_MESSAGE"
    android:protectionLevel="signature" />

<uses-permission android:name="com.example.app.permission.C2D_MESSAGE" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />

<meta-data
    android:name="com.google.android.gms.analytics.globalConfigResource"
    android:resource="@xml/analytics_global_config" />

<application
    android:name=".App"
    android:allowBackup="true"
    android:hardwareAccelerated="true"
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name"
    android:theme="@style/AppTheme" >

UPD: Thanks to @Bart Hofma, I realised that I probably made it confusing about my app crashing. My app doesn't crash when GA tries to dispatch events. On the contrary, GA tries to dispatch events only when my app crashes, otherwise it stays silent. And I crash my app deliberately to see what GA might say. I have a custom video player that works fine on real devices but crashes on VMs, so when I want my app to crash, I just go to the player Activity on a VM.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

You must call the google analytics from thread. Here i'm sharing the code what i'm working with.

Put this code inside a class(MBGoogleAnalyticsManager.java)

 import android.content.Context;
 import com.google.android.gms.analytics.GoogleAnalytics;
 import com.google.android.gms.analytics.HitBuilders;
 import com.google.android.gms.analytics.Tracker;


public class MBGoogleAnalyticsManager  
{
    private static final String TAG = "MBGoogleAnalyticsManager";
    private static MBGoogleAnalyticsManager instance = null;
    public String PROPERTY_ID = "UA-28454545-2";

    private Context context;
    private Tracker dataTracker;

    public static MBGoogleAnalyticsManager getInstance(Context c) 
        {
            if(instance == null) 
                {
                    System.out.println("MBGoogleAnalyticsManager new instance is created");
                    instance = new MBGoogleAnalyticsManager(c);
                }
            else
                {
                    System.out.println("MBGoogleAnalyticsManager instance is already avaiable");
                }
            return instance;
        }

    private MBGoogleAnalyticsManager(Context c)     
        {
            //super(c);
            context = c;
            GoogleAnalytics analytics = GoogleAnalytics.getInstance(context);   
            dataTracker = analytics.newTracker(PROPERTY_ID);
        }




      //==============================================================================
     //========================== Send a screen view =================================
    //================================================================================
    public synchronized void sendGoogleAnalyticsSreenView(String screenName)
        {               
            dataTracker.setScreenName(screenName);    // Set screen name.
            //dataTracker.setAppName("MovieBuddy");  // We can send the application name [String](Optional)
            //dataTracker.setAppVersion("2.2");     // We can send the application version [String](Optional)
            //dataTracker.setLanguage("Eng");      // We can send the language [String](Optional)
            //dataTracker.setSessionTimeout(300); // We can set the time for the session time out [Long](Optional)

            dataTracker.send(new HitBuilders.AppViewBuilder()    
                                            //.setNewSession()   // If want to start a new session (Optional)
                                            .build());
            System.out.println("Screen: "+screenName);
        }



      //==============================================================================
     //========================== Send A Click Event Hit =============================
    //================================================================================
    public synchronized void sendGoogleAnalyticsHitEvents(String screenName,String category,String action,String label)
        {       
            dataTracker.setScreenName(screenName);
            dataTracker.send(new HitBuilders.EventBuilder()         
                                            .setCategory(category) // Set the category of the event [String]
                                            .setAction(action)    // Set the action has taken for the event [String]
                                            .setLabel(label)     // Set the label of for the event [String]
                                            .build());
            System.out.println("Hit Label: "+label);
        }



      //==============================================================================
     //========================== Send A Exception Event =============================
    //================================================================================

    public synchronized void sendGoogleAnalyticsException(String screenName,String ExceptionMethodName,String ExceptionLocation,boolean ExceptionFatal)
        {   
            dataTracker.setScreenName(screenName);
            dataTracker.send(new HitBuilders.ExceptionBuilder()                                         
                                            .setDescription(ExceptionMethodName + ":" + ExceptionLocation) // Send the exception details [String]
                                                                                                          //(Never send the e.message, as it contains personal info)
                                            .setFatal(ExceptionFatal)                                    // Send true or false if fatal exception error [boolean]
                                            .build());
        }



      //==============================================================================
     //================ Send A Social Event Interaction With target ==================
    //================================================================================
    public synchronized void sendGoogleAnalyticsSocialInteractionWithTarget(String screenName,String SocialNetworkName,String SocialAction,String SocialTarget)
        {       
            dataTracker.setScreenName(screenName);
            dataTracker.send(new HitBuilders.SocialBuilder()                 
                                            .setNetwork(SocialNetworkName)  // Set the Social Network Name [String]
                                            .setAction(SocialAction)       // Set the action [String]
                                            .setTarget(SocialTarget)      // Set the target [String](Not a mandatory field)
                                            .build());
            System.out.println("Target: "+SocialTarget);
        }




      //==============================================================================
     //============== Send A Social Event Interaction Without target =================
    //================================================================================

    public synchronized void sendGoogleAnalyticsSocialInteraction(String SocialNetworkName,String SocialAction)
        {       
            dataTracker.send(new HitBuilders.SocialBuilder()
                                            .setNetwork(SocialNetworkName)// Set the Social Network Name [String]
                                            .setAction(SocialAction)     // Set the action [String]
                                            .build());
        }
} 

Now you have to call this class instance,method and provide the required data from you calling class like this:

private void sendScreenView(final String screenName)
    {   
        new Thread(new Runnable() 
            {
                @Override
                public void run() 
                    {                           
                        analyticsManager = MBGoogleAnalyticsManager.getInstance(getApplicationContext());
                        analyticsManager.sendGoogleAnalyticsSreenView(screenName);
                    }
            }).start();
    }

And Call this method:

sendScreenView("Home Activity");

Hope you will find it helpful.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

2.1m questions

2.1m answers

60 comments

57.0k users

...