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