Treasure Data Android SDK

This article get you started sending data from your Android app to Treasure Data, using our Android SDK library. Using these steps, you don’t need to install anything server-side to track your mobile application’s activities.

Table of Contents


  • Basic knowledge of Android Development
  • Android 2.3 , or later
  • Basic knowledge of Treasure Data.

How to install Android SDK?

This video demonstrates how to install Android SDK in 5 minutes.

Step 1: Install the Library


If you use gradle, add the following dependency to dependencies directive in the build.gradle.

dependencies {
  compile 'com.treasuredata:td-android-sdk:0.1.16'


If you use maven, add the following directives to your pom.xml under <dependencies>. pom.xml in an example Android application project could be a good reference.


Jar File

If you don’t use Maven, please put td-android-sdk-x.x.x-shaded.jar (get the latest here) into (YOUR_ANDROID_PROJECT)/libs.

Step 2: Enable Required Android Permissions

If it’s not already present, add the INTERNET permission to your AndroidManifest.xml file. The entry below should appear between the <manifest> .. </manifest> tags. The example is here.

<manifest xmlns:android=""
  android:versionName="1.0" >

  <!-- required permission -->
  <uses-permission android:name="android.permission.INTERNET"/>

Step 3: Initialize the Library at onCreate()

Please create the TreasureData object at the onCreate() method of your main activity. Here’s an example code:

public class ExampleActivity extends Activity {
  private TreasureData td;

  protected void onCreate(Bundle savedInstanceState) {

    // Initialize Treasure Data Android SDK
    // @see
    td = TreasureData.initializeSharedInstance(this, "YOUR_WRITE_ONLY_API_KEY");

You can get the API key from the (Treasure Data Console). It’s recommended to use a write-only API key for SDKs.

Step 4: Buffer the events locally

Next, call the addEvent(String table, Map<String, Object> record) function at the appropriate timing within your applications. This example sends an event to the button_clicks table, when the button is clicked.

We generally recommend to use application's name as a database name, and event name as a table name.
View v = findViewById(;
v.setOnClickListener(new OnClickListener() {
  public void onClick(View v) {
    final Map event = new HashMap<String, Object>();
    event.put("name", "foo bar");
    event.put("age", 42);
    event.put("comment", "hello world");
    td.addEvent("button_clicks", event);

With this call, the record like below will be immediately bufferred on the disk.

  "name":"foo bar",

By default, all events will be bufferred on the disk. You need to explicitly flush the bufferred data to the cloud. It won’t be uploaded automatically. Next section will describe how to flush the buffer.

Step 5: Session Tracking and Events Upload at onStart() / onStop()

Finally, please call TreasureData.startSession() and TreasureData.endSession() at onStart() and onStop(). Then, please call uploadEvents() at onStop() too. As we described, uploadEvents() function will flush the buffer and send the data to the cloud. In case upload fails, it will automatically retry in a background thread.

protected void onStart(Bundle savedInstanceState) {
  td.addEvent("on_start_calls", ...); // optional but recommended
  Log.i(TAG, "onStart(): Session ID=" + TreasureData.getSessionId(this)); // You can get the current session ID like this

protected void onStop() {
  td.addEvent("on_stop_calls", ...); // optional but recommended

It depends on the characteristic of your application when to upload and how often to upload buffered events. But we recommend the followings at least as good timings to upload.

  • When the current screen is closing or moving to background
  • When closing the application

As long as there is any Context that has called startSession() but not endSession(), the session will be continued. Also, if a new Context calls startSession() within 10 seconds of the last Context calling endSession(), then the session will be resumed, instead of a new session being created.

IP whitelist won't be applied to any import from Android SDK. Also we've seen a lot of cases where a lot of Android devices have an invalid timestamp (like 1970/01/01), so we're currently ignoring the log which has a timestamp older than 7 days, and newer than 3 days ahead.

Step 6: Track the App Installation

You can collect the first run event of your application, which can be used to track app installation event. isFirstRun() and clearFirstRun() methods would be useful.

protected void onCreate(Bundle savedInstanceState) {
  if (td.isFirstRun(this)) {
    Map<String, Object> event = new HashMap<String, Object>();
    event.put("first_run", true);

    td.addEventWithCallback("events", event, new TDCallback() {
      public void onSuccess() {
      public void onError(String errorCode, Exception e) {
        Log.w(TAG, "TreasureData.addEvent:onError errorCode=" + errorCode + ", ex=" + e);

Parameters Captured

Here is a list of parameters captured by the Android SDK:

- td_session_id
- td_uuid : unique ID for the combination of application and device
- td_board : android.os.Build#BOARD
- td_brand : android.os.Build#BRAND
- td_device : android.os.Build#DEVICE
- td_display : android.os.Build#DISPLAY
- td_model : android.os.Build#MODEL
- td_os_ver : android.os.Build.VERSION#SDK_INT
- td_os_type : "Android"
- td_app_ver : (from Context.getPackageManager().getPackageInfo())
- td_app_ver_num : (from Context.getPackageManager().getPackageInfo())
- td_locale_country : java.util.Locale.getCountry() (from Context.getResources().getConfiguration().locale)
- td_locale_lang : java.util.Locale.getLanguage() (from Context.getResources().getConfiguration().locale)

Advanced Tips

Access shared TreasureData instance

You can use a shared instance from anywhere with TreasureData.sharedInstance() method after calling TreasureData.initializeSharedInstance().

public class MainActivity extends Activity {
  protected void onCreate(Bundle savedInstanceState) {
    TreasureData.initializeSharedInstance(this, "YOUR_WRITE_ONLY_API_KEY");

public class OtherActivity extends Activity {
  public void onDataLoadSomethingFinished(long elapsedTime) {
    Map<String, Object> event = new HashMap<String, Object>();
    event.put("data_type", "something");
    event.put("elapsed_time", elapsedTime);
    TreasureData.sharedInstance().addEvent("events", event);

Sending data with Array type

Android SDK can handle java.util.List or java.lang.String[] to send data with Array type. But, it doesn’t handle JSONArray since internal library jackson.databind doesn’t deal with JSONArray.

event.put("array", Arrays.asList("one", "two", "three"));

Retry uploading and deduplication

This SDK imports events in exactly once style with the combination of these features.

  • This SDK keeps buffered events with adding unique keys and retries to upload them until confirming the events are uploaded and stored on server side (at least once)
  • The server side remembers the unique keys of all events within the past 1 hours by default and prevents duplicated imports (at most once)

As for the deduplication window is 1 hour by default, so it’s important not to keep buffered events more than 1 hour to avoid duplicated events.

Next Steps

For transparency, we’re open sourcing our iOS SDK on Github. Please check the repository if necessary.

Last modified: Dec 01 2017 14:58:07 UTC

If this article is incorrect or outdated, or omits critical information, let us know. For all other issues, access our support channels.