segmentio / analytics-android Goto Github PK
View Code? Open in Web Editor NEWThe hassle-free way to add analytics to your Android app.
Home Page: https://segment.com/docs/sources/mobile/android/
License: MIT License
The hassle-free way to add analytics to your Android app.
Home Page: https://segment.com/docs/sources/mobile/android/
License: MIT License
e.g. if context is retrieved from a payload, cache the context object in the map so we can avoid deserializing it again, similarly for even custom types.
Running with the master branch of today..
03-26 14:58:50.411: E/Database(2825): close() was never explicitly called on database 'countly'
03-26 14:58:50.411: E/Database(2825): android.database.sqlite.DatabaseObjectNotClosedException: Application did not close the cursor or database object that was opened here
03-26 14:58:50.411: E/Database(2825): at android.database.sqlite.SQLiteDatabase.<init>(SQLiteDatabase.java:1864)
03-26 14:58:50.411: E/Database(2825): at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:822)
03-26 14:58:50.411: E/Database(2825): at android.database.sqlite.SQLiteDatabase.openOrCreateDatabase(SQLiteDatabase.java:856)
03-26 14:58:50.411: E/Database(2825): at android.database.sqlite.SQLiteDatabase.openOrCreateDatabase(SQLiteDatabase.java:849)
03-26 14:58:50.411: E/Database(2825): at android.app.ContextImpl.openOrCreateDatabase(ContextImpl.java:556)
03-26 14:58:50.411: E/Database(2825): at android.content.ContextWrapper.openOrCreateDatabase(ContextWrapper.java:203)
03-26 14:58:50.411: E/Database(2825): at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:118)
03-26 14:58:50.411: E/Database(2825): at android.database.sqlite.SQLiteOpenHelper.getReadableDatabase(SQLiteOpenHelper.java:187)
03-26 14:58:50.411: E/Database(2825): at ly.count.android.api.CountlyDB.getEvents(Countly.java:596)
03-26 14:58:50.411: E/Database(2825): at ly.count.android.api.EventQueue.<init>(Countly.java:378)
03-26 14:58:50.411: E/Database(2825): at ly.count.android.api.Countly.init(Countly.java:75)
03-26 14:58:50.411: E/Database(2825): at io.segment.android.integrations.CountlyIntegration.onCreate(CountlyIntegration.java:51)
03-26 14:58:50.411: E/Database(2825): at io.segment.android.integration.IntegrationManager$2.run(IntegrationManager.java:217)
03-26 14:58:50.411: E/Database(2825): at io.segment.android.integration.IntegrationManager.runOperation(IntegrationManager.java:182)
03-26 14:58:50.411: E/Database(2825): at io.segment.android.integration.IntegrationManager.onCreate(IntegrationManager.java:213)
03-26 14:58:50.411: E/Database(2825): at io.segment.android.Analytics.onCreate(Analytics.java:93)
Extra fields (Operating system, device info, etc. aren't being sent to
Mixpanel)
Got this error from segment.io library.
java.util.ConcurrentModificationException
at java.util.HashMap$HashIterator.nextEntry(HashMap.java:796)
at java.util.HashMap$KeyIterator.next(HashMap.java:823)
at android.database.sqlite.SQLiteDatabase.removeMeFromActive(SQLiteDatabase.java:950)
at android.database.sqlite.SQLiteDatabase.close(SQLiteDatabase.java:980)
at io.segment.android.db.PayloadDatabase.getEvents(PayloadDatabase.java:248)
at io.segment.android.db.PayloadDatabaseThread$2.run(PayloadDatabaseThread.java:52)
at android.os.Handler.handleCallback(Handler.java:587)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:130)
at io.segment.android.utils.LooperThreadWithHandler.run(LooperThreadWithHandler.java:32)
[2014-05-09 13:01:02 - MGMainActivity] Error generating final archive:
Found duplicate file for APK: AndroidManifest.xml
Origin 1: /home/rednamer/Work/aliceoid/Aliceoid/bin/resources.ap_
Origin 2:
/home/rednamer/Work/aliceoid/Aliceoid/libs/analytics-android-0.6.12.jar
One of our users reported an error that I was able to reproduce. Turns out it's an issue from the build system.
Opening it here so we can keep a track of it and as a pointer for other developers.
When inserts a string with ' (single quotes) like "Newell's Old Boys" the app crashes:
FATAL EXCEPTION: main
android.database.sqlite.SQLiteException: near "s": syntax error (code 1): , while compiling: INSERT OR REPLACE INTO EVENTS(ID, EVENT) VALUES(1, '{"events":[{"timestamp":1396537600,"sum":0,"segmentation":{"ACAO":"Create"},"count":0,"key":"ACCESS SCREEM ONE"},{"timestamp":1396537600,"sum":0,"segmentation":{},"count":0,"key":"ACCESS SCREEM ONE"},{"timestamp":1396537600,"sum":0,"segmentation":{},"count":0,"key":"ACCESS SCREEM ONE"},{"timestamp":1396537600,"sum":0,"segmentation":{},"count":0,"key":"ACCESS SCREEM ONE"},{"timestamp":1396537600,"sum":0,"segmentation":{"TIPO":"ACCESS SCREEM ONE"},"count":0,"key":"ACCESS SCREEM ONE"},{"timestamp":1396537600,"sum":0,"segmentation":{"ID":"712","NOME":"Newell's Old Boys"},"count":0,"key":"ACCESS SCREEM ONE"}]}');
at android.database.sqlite.SQLiteConnection.nativePrepareStatement(Native Method)
at android.database.sqlite.SQLiteConnection.acquirePreparedStatement(SQLiteConnection.java:1090)
at android.database.sqlite.SQLiteConnection.prepare(SQLiteConnection.java:663)
at android.database.sqlite.SQLiteSession.prepare(SQLiteSession.java:588)
at android.database.sqlite.SQLiteProgram.<init>(SQLiteProgram.java:58)
at android.database.sqlite.SQLiteStatement.<init>(SQLiteStatement.java:31)
at android.database.sqlite.SQLiteDatabase.executeSql(SQLiteDatabase.java:1769)
at android.database.sqlite.SQLiteDatabase.execSQL(SQLiteDatabase.java:1700)
at ly.count.android.api.CountlyDB.saveEvents(Countly.java:646)
at ly.count.android.api.EventQueue.recordEvent(Countly.java:496)
at ly.count.android.api.Countly.recordEvent(Countly.java:127)
at io.segment.android.integrations.CountlyIntegration.track(CountlyIntegration.java:89)
at io.segment.android.integration.IntegrationManager$9.run(IntegrationManager.java:332)
at io.segment.android.integration.IntegrationManager.runOperation(IntegrationManager.java:182)
at io.segment.android.integration.IntegrationManager.track(IntegrationManager.java:326)
at io.segment.android.Analytics.track(Analytics.java:1087)
at io.segment.android.Analytics.track(Analytics.java:969)
equivalent of whatever this is for android: segmentio/analytics-java#14
Can you add a maven artifact?
It has been known for quite some time that you should not use the Android Device ID.
Your OpenUUID class seem to use this:
Please read Google Blog
Tapstream needs an Application instance in the line 50 of TapstreamIntegration.java:
Tapstream.create(context, accountName, sdkSecret, config);
Like:
onfig config = new Config();
Tapstream.create(getApplication(), "TAPSTREAM_ACCOUNT_NAME", "TAPSTREAM_SDK_SECRET", config);
From: https://tapstream.com/developer/android-sdk-documentation/
Have a way for users to get the instance that we are using in our integration so they can share it.
Potential usability : If they turn of the integration on the server, we won't actually initialize it through our library, so if they call any methods for the integration later, users will have to make sure they initialize it first.
Foo foo = Analytics.getIntegrations().get("foo").getInstance();
// if integration was turned of in the server, then foo is actually not initalized, and may be null.
// This could lead to any following calls to it throw NPE.
// The burden of performing those null checks would be on the user.
Hi Folks,
Have you ever experienced this before.
02:58:28 // android.database.CursorWindowAllocationException: Cursor window allocation of 2048 kb failed.
02:58:28 // at android.database.CursorWindow.<init>(CursorWindow.java:104)
02:58:28 // at android.database.AbstractWindowedCursor.clearOrCreateWindow(AbstractWindowedCursor.java:198)
02:58:28 // at android.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:139)
02:58:28 // at android.database.sqlite.SQLiteCursor.getCount(SQLiteCursor.java:133)
02:58:28 // at android.database.AbstractCursor.moveToPosition(AbstractCursor.java:197)
02:58:28 // at android.database.AbstractCursor.moveToNext(AbstractCursor.java:245)
02:58:28 // at io.segment.android.db.PayloadDatabase.getEvents(PayloadDatabase.java:230)
02:58:28 // at io.segment.android.db.PayloadDatabaseThread$2.run(PayloadDatabaseThread.java:52)
02:58:28 // at android.os.Handler.handleCallback(Handler.java:725)
02:58:28 // at android.os.Handler.dispatchMessage(Handler.java:92)
02:58:28 // at android.os.Looper.loop(Looper.java:137)
02:58:28 // at io.segment.android.utils.LooperThreadWithHandler.run(LooperThreadWithHandler.java:32)
The Cursor is closed in the finally as expected
finally {
try {
if (cursor != null) cursor.close();
if (db != null) db.close();
} catch (SQLiteException e) {
Logger.e("Failed to close db cursor: " +
Log.getStackTraceString(e));
}
}
Hi, I found that the documentantion of Android Library its wrong, on step 4 you make a call to Static method reset() but the signature of that method on Analytics.java its not Static, showing an error when try to follow the documentation.
Thanks for your time.
César Pardo
internal ticket here: https://secure.helpscout.net/conversation/33750057/24541/
Hi,
As mentioned in the title, events from Analytics.screen() are ignored on Mixpanel.
I know that mixpanel does not support specifically screen() events. However in the segmentio iOS SDK, screen() events are sent to Mixpanel as track() events.
Wouldn't it be logical to have the same behaviour in the Android SDK ?
Thanks for the help,
Lukas
Either the class name (EasyJSONObject) needs to match the file name (EasyJsonObject) or vice versa :-)
v1's count is 1100, currently v2 is at 800 but I think we can cut it down even more.
helps with dedup, logging, and debugging (once implemented server-side)
java.lang.NullPointerException
at ly.count.android.api.DeviceInfo.getResolution(Countly.java:300)
at ly.count.android.api.DeviceInfo.getMetrics(Countly.java:345)
at ly.count.android.api.ConnectionQueue.beginSession(Countly.java:187)
at ly.count.android.api.Countly.onStartHelper(Countly.java:93)
at ly.count.android.api.Countly.onStart(Countly.java:81)
at io.segment.android.integrations.CountlyIntegration.onActivityStart(CountlyIntegration.java:58)
at io.segment.android.integration.IntegrationManager$3.run(IntegrationManager.java:229)
at io.segment.android.integration.IntegrationManager.runOperation(IntegrationManager.java:182)
at io.segment.android.integration.IntegrationManager.onActivityStart(IntegrationManager.java:225)
at io.segment.android.Analytics.activityStart(Analytics.java:169)
at com.movile.futebol.activity.SplashActivity.onStart(SplashActivity.java:124)
at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1164)
at android.app.Activity.performStart(Activity.java:5148)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2237)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2314)
at android.app.ActivityThread.access$600(ActivityThread.java:144)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1317)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:150)
at android.app.ActivityThread.main(ActivityThread.java:5147)
at java.lang.reflect.Method.invokeNative(Method.java)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
at dalvik.system.NativeStart.main(NativeStart.java)
It would be awesome to integrate the Mixpanel 4.2.0 library into segment.io to fix some issues we're having.
Had reports of this happening.
We're looking up the wrong key (https://github.com/segmentio/analytics-android/blob/master/core/src/main/java/com/segment/android/integrations/FlurryIntegration.java#L48), it should be sessionContinueSeconds
, which is what is returned by the server.
Affects version 0.6.2
I'm not extending TrackableActivity.
Here's how to reproduce...
public abstract class BootstrapActivity extends IckleFragmentActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
boolean isDebug = 0 != ( getApplicationInfo().flags &= ApplicationInfo.FLAG_DEBUGGABLE );
Analytics.onCreate(this, Constants.App.SEGMENT_IO_WRITE_KEY, new Options().setDebug(isDebug));
}
@Override
public void onStart() {
super.onStart();
Analytics.activityStart(this);
}
@Override
protected void onResume() {
super.onResume();
Analytics.activityResume(this);
}
@Override
protected void onPause() {
super.onPause();
Analytics.activityPause(this);
}
@Override
protected void onStop() {
super.onStop();
Analytics.activityStop(this);
}
...
Producing some logs:
03-26 13:14:35.632 10795-10795/tv.pluto.android I/analytics﹕ [Google Analytics] onActivityStart finished in : 0 milliseconds.
03-26 13:14:35.632 10795-10795/tv.pluto.android I/analytics﹕ [Mixpanel] onActivityStart finished in : 0 milliseconds.
03-26 13:14:35.632 10795-10795/tv.pluto.android I/analytics﹕ [All Providers] onActivityStart finished in : 1 milliseconds.
03-26 13:14:35.632 10795-10795/tv.pluto.android I/analytics﹕ [Google Analytics] onActivityResume finished in : 0 milliseconds.
03-26 13:14:35.632 10795-10795/tv.pluto.android I/analytics﹕ [Mixpanel] onActivityResume finished in : 0 milliseconds.
03-26 13:14:35.632 10795-10795/tv.pluto.android I/analytics﹕ [All Providers] onActivityResume finished in : 0 milliseconds.
....
03-26 13:14:38.515 10795-10817/tv.pluto.android I/analytics﹕ [Google Analytics] Flush finished in : 0 milliseconds.
03-26 13:14:38.515 10795-10817/tv.pluto.android I/analytics﹕ [Mixpanel] Flush finished in : 0 milliseconds.
03-26 13:14:38.515 10795-10817/tv.pluto.android I/analytics﹕ [All Providers] Flush finished in : 3 milliseconds.
...and when app is backgrounded....
03-26 13:16:46.620 10795-10795/tv.pluto.android I/analytics﹕ [Google Analytics] onActivityPause finished in : 0 milliseconds.
03-26 13:16:46.620 10795-10795/tv.pluto.android I/analytics﹕ [Mixpanel] onActivityPause finished in : 0 milliseconds.
03-26 13:16:46.620 10795-10795/tv.pluto.android I/analytics﹕ [All Providers] onActivityPause finished in : 0 milliseconds.
03-26 13:16:47.270 10795-10795/tv.pluto.android I/analytics﹕ [Google Analytics] onActivityStop finished in : 0 milliseconds.
03-26 13:16:47.270 10795-10795/tv.pluto.android I/analytics﹕ [Mixpanel] onActivityStop finished in : 0 milliseconds.
03-26 13:16:47.270 10795-10795/tv.pluto.android I/analytics﹕ [All Providers] onActivityStop finished in : 1 milliseconds.
03-26 13:16:48.632 10795-10817/tv.pluto.android I/analytics﹕ [Google Analytics] Flush finished in : 0 milliseconds.
03-26 13:16:48.632 10795-10817/tv.pluto.android I/analytics﹕ [Mixpanel] Flush finished in : 0 milliseconds.
03-26 13:16:48.632 10795-10817/tv.pluto.android I/analytics﹕ [All Providers] Flush finished in : 0 milliseconds.
We support in the browser in Analytics.js
And soon through our servers.
We should probably add on Android too, GA docs here
I've had 2-3 people ask about this and it'd be great to just support GA commerce everywhere :)
Hi there, I'm trying to use the code below (taken from Tapstream's page) but the method is "undefined". Could it be because the Tapstream.jar you provide is outdated?
Impossible to run an application on Android 2.3.6 with analytics-android-0.6.5.jar.
INSTALL_FAILED_DEXOPT occurs on adb install
The library is included with build.gradle:
dependencies {
...
compile files('libs/analytics-android-0.6.5.jar')
...
}
The app is installed correctly if I remove this line
Tried with 0.6.11 also with no success. (could't try with other versions, because I couldn't even build with them, but it's another issue)
similarly:
device.put("brand", android.os.Build.BRAND);
here's the spec developed for the iOS SDK:
https://gist.github.com/reinpk/7bd33d29694578b06cce
Repro test case by @kellyfj, thanks!
@Test
public void testTriggerTimerInteraction() throws InterruptedException {
AnalyticsStatistics stats = Analytics.getStatistics();
int flushAfter = Analytics.getOptions().getFlushAfter();
int flushAt = Analytics.getOptions().getFlushAt();
int flushAttempts = stats.getFlushAttempts().getCount();
int flushes = stats.getFlushTime().getCount();
for (int i = 0; i < flushAt + 5; i++) {
Analytics.enqueue(TestCases.random());
}
// the flush after the trigger should have triggered a flush here
flushAttempts += 1;
flushes += 1;
try {
Thread.sleep(flushAfter + 250);
} catch (InterruptedException e) {
e.printStackTrace();
}
// the flush after timer should have triggered a flush here
flushAttempts += 1;
flushes += 1;
Assert.assertEquals(flushes, stats.getFlushTime().getCount());
//FJK: Given current issue Flush attempts will be 5 more than expected e.g. 2 vs 7
Assert.assertEquals(flushAttempts, stats.getFlushAttempts().getCount());
}
Flush happens here: https://github.com/segmentio/analytics-android/blob/master/src/io/segment/android/flush/FlushThread.java#L80
and the removal from the db thread happens here: https://github.com/segmentio/analytics-android/blob/master/src/io/segment/android/flush/FlushThread.java#L109
There is a possible race condition where the same data can get flushed twice.
While running monkey test I got this log
android.database.sqlite.SQLiteException: unable to close due to unfinalised statements
at android.database.sqlite.SQLiteDatabase.dbclose(SQLiteDatabase.java)
at android.database.sqlite.SQLiteDatabase.onAllReferencesReleased(SQLiteDatabase.java:325)
at android.database.sqlite.SQLiteDatabase.close(SQLiteDatabase.java:1025)
at io.segment.android.db.PayloadDatabase.getEvents(PayloadDatabase.java:247)
at io.segment.android.db.PayloadDatabaseThread$2.run(PayloadDatabaseThread.java:52)
at android.os.Handler.handleCallback(Handler.java:587)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:130)
at io.segment.android.utils.LooperThreadWithHandler.run(LooperThreadWithHandler.java:32)
Hope it helps.
Caused by: java.lang.NullPointerException
at ly.count.android.api.DeviceInfo.getCarrier(Countly.java:331)
at ly.count.android.api.DeviceInfo.getMetrics(Countly.java:362)
at ly.count.android.api.ConnectionQueue.beginSession(Countly.java:191)
at ly.count.android.api.Countly.onStartHelper(Countly.java:90)
at ly.count.android.api.Countly.onStart(Countly.java:76)
at io.segment.android.integrations.CountlyIntegration.onActivityStart(CountlyIntegration.java:59)
at io.segment.android.integration.IntegrationManager$3.run(IntegrationManager.java:229)
at io.segment.android.integration.IntegrationManager.runOperation(IntegrationManager.java:182)
at io.segment.android.integration.IntegrationManager.onActivityStart(IntegrationManager.java:225)
at io.segment.android.Analytics.activityStart(Analytics.java:169)
For sending events to the server, we have an integrations
key in each payload that contains string-boolean pairs, each entry denoting whether the service is enabled for that integration.
Any integrations that are bundled, we'll have that key set to false so that we don't send via the server.
Unfortunately, we're re-using this integrations map to check whether we should also post the event to the bundled integration.
This means if we set mixpanel
to false, it won't be sent to Mixpanel from the server, and it won't be sent to the bundled integration either. If we set it to true, then it will be sent by both the bundled integration and the server integration - causing duplicate events.
For the future, we'll probably want to use some sort of triplean (backed by an enum) variable, so users can decide whether something needs to be sent via the server integration, the bundled integration, or neither (both can probably be eliminated).
I'm pretty sure the whole point of your service is that I am sending requests to your server which in turn forwards it on to the selected provider? Great!
But that seems to not be the case?
Resulting in a horrendously large Jar with so much third party code I really dont want.
Why is it that the other libs seem to just talk to your server and the android one uses providers?
If the app has the location permission, the Segment.io library seems to be triggering the GPS hardware on launch.
From our research, it seems to be coming from here:
The InfoManager tries to get a general location, and if the hardware doesn't have a good enough lock it may decide to start the GPS.
We're not using any location data in our analytics, is there a way we can configure the library to not do this? We've been getting complaints from our users saying the app is using location when they aren't using the map functionality of the app.
I was banging my head as to why Analytics.screen would not send any thing to Mixpanel thinking I was doing something wrong but then I read this in Mixpanel integration...
if (isMixpanelPeopleEnabled()) {
// consider the charge
if (properties != null && properties.has("revenue")) {
MixpanelAPI.People people = mixpanel.getPeople();
double revenue = properties.getDouble("revenue", 0.0);
people.trackCharge(revenue, properties);
}
}
Why does it only send screen view for revenue?!
Since it's a bit confusing for mobile libs (makes sense on server-side obviously):
For example if Mixpanel is enabled in settings, but not bundled, then we should log and warn them about it.
Consider whether to switch to using Advertising ID as a default, or just as a fallback in case Android ID is unavailable.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.