matomo-org / matomo-sdk-android

SDK for Android to measure your apps with Matomo. Works on Android phones, tablets, Fire TV sticks, and more!
BSD 3-Clause "New" or "Revised" License
393 stars 164 forks source link

Offline mode missing #16

Closed ClementHard closed 7 years ago

ClementHard commented 9 years ago

If the dispach of events occurs when the device is offline, the events queue is cleared before knowing if the request was a success.

d4rken commented 9 years ago

I'm probably going to tackle this before going into production with piwik in my Android app. Any ideas on a solution direction?

dotsbb commented 9 years ago

@d4rken I think there should be some FIFO container within Tracker instance so we could store here untracked views. It would be great if this container would be configurable (for example it might accept MAX_SIZE param).

d4rken commented 9 years ago

A few points that we should consider:

dotsbb commented 8 years ago

@d4rken Have You seen this fork? It does 0 of yours considered points at this moment but it can be something we can refactor and introduce features incrementally to it.

d4rken commented 8 years ago

I have now :wink: . It's one way to solve it but as you mentioned, it addresses none of the concerns. I would probably start from scratch, not refactor this.

@apc-kamezaki You might want to refactor restoreEventsFromFile . It is currently called in the constructor which means it's called from the UI thread (IO operation on the UI thread are bad). This also means if there are lots of cached events, it would block the main thread and as Piwik ususally initialises from Application.onCreate it would block the app start (huge impact on the app using Piwik).

suzukieng commented 8 years ago

I just had a look at both the iOS and Android tracking library codebases. The iOS tracking library uses CoreData (ultimately SQlite) to persistently queue a configurable number of events. Wouldn't it be logical to implement it in the same way for Android, i.e. using sqlite and optionally some lightweight ORM on top of it?

d4rken commented 8 years ago

@suzukieng That would be one good way.

But generally we don't have to focus on a specific implementation just because other piwik code bases use it (as long as it works sufficiently well). The public API should be similar and it should behave similar. "Under the hood" stuff like this is almost FFA :grin: . SQlite would be fine, but so would raw files or maybe fancy stuff like realm.io.

The primary goal is to have a low impact on the host app and as secondary goals to somehow achieve the points mentioned above.

suzukieng commented 8 years ago

@d4rken Completely agree with primary/secondary goals, low impact is absolutely key. What I find hard to judge is how much of a difference it makes if you store events persistently vs. buffer them in-memory, i.e. how often will the app be killed by the OS before events can be uploaded.

Android processes are in general pretty long-lived (see: http://developer.android.com/reference/android/app/Activity.html#ProcessLifecycle) and perhaps an Android Service can be persuaded to live on as long as there are events. But this would again probably go against the low-impact goal and also slightly complicates integration because you would have to add the Service to the manifest.

BTW: SharedPreferences are another light-weight mechanism for storing stuff persistently, I know the folks at count.ly use it in their Android SDK.

orian commented 8 years ago

Many Android devices despite having pretty decent version of system (4.3+, many 5.0.3) are short on resources. Thus I would not assume the processes are 'long lived' or the logs from the low-level devices will often be lost. Also keeping them alive just because we can sounds less user friendly than just dumping the data and sending on the next occasion.

Storage What about using Context.getCacheDir() or if WRITE_EXTERNAL_STORAGE persmission is granted than using Context.getExternalCacheDir(). This has the advantage that if a system really needs more storage it can decide to clear the directory. The SharedPreferences happen to go OutOfMemory for some bigger data, but I've never tried to store more data there.

d4rken commented 8 years ago

SharedPreferences would not be very suited for this as this isn't a good case of key/value based access.

In general we have to assume the app could be killed at any moment, so unless we can somehow get a callback for when the app is killed, we shouldn't keep data in memory for long. Something like batching a few failed requests and writing them into storage based on a timer, would be my idea.

The right location to store this would indeed be a subdir of the cache dir because these files are cached and can be lost without any grave repercussions. More specifically Context.getCacheDir() (private cache, i.e. /data/data//cache) because the analytics could be considered private information (depending on what the developer tracks).

mattab commented 8 years ago

Hello guys, FYI: we are also implementing Offline mode for the official Piwik JS Tracker code (in https://github.com/piwik/piwik/issues/9939). So far we make it super simple and you can see the idea here: https://github.com/piwik/piwik/compare/2.x-dev...offlinetracker?expand=1 Developer would do

tracker.setUserOffline({push: function (request) {
// eg localstorage.addItem(request);
}});

tracker.setUserOnline(localstorage.getItems());

(Just FYI in case we could maybe reuse same method name or similar. Fun to see that we are synchronised and implementing this important functionality at the same time in both projects!)

d4rken commented 8 years ago

Not sure yet about which approach to take.

Manually settings online/offline mode really is a simple approach, but i think the sdk should provide this on Android. Alternatively reacting only to successful/failed communication with Piwik would also allow clients to cache data in cases where the issue is on the server side. But permanent issues due to code errors would just max out the cache.... Maybe just checking connectivity and reacting to changes then...

As we don't want the authtoken to be used on Android (can't be stored securely), we should just have to cache data for up to 4 hours and discard older data. All already have the "cdt" parameter set, so sending them out of order should also not matter, right?

mattab commented 7 years ago

Hi @d4rken Hope you are well :-)

To answer a few points:

d4rken commented 7 years ago

the order of requests does matter for a given device: Piwik Tracking API needs to receive requests in the right chronological order for a given user / visit, but it's OK if requests from several users/devices arrive at different times.

Even if the cdt parameter is set? What happens if they are out of order? Are they just discarded?