Java SDK
Java SDK 主要用於伺服器端 Java 應用,對於 Android App,請使用 Android SDK。
1. 整合神策分析 SDK
在伺服器端 Java 應用中整合 Java SDK,使用神策分析採集並分析用戶行為。
我們推薦使用 Maven 管理 Java 專案,請在 pom.xml 文件中,增加以下依賴資訊,Maven 將自動取得神策分析 SDK 並更新專案設定。
<dependencies>
// ...
<dependency>
<groupId>com.sensorsdata.analytics.javasdk</groupId>
<artifactId>SensorsAnalyticsSDK</artifactId>
<version>3.1.16</version>
</dependency>
</dependencies>
若出現依賴衝突的問題(例如執行時找不到類別),可以使用 standalone 版本:將上面的 version 替換為 3.1.15-standalone。
如果不使用 Maven,也可以用下面任意一種方式整合:
- 直接從 GitHub 下載 Java SDK 的原始碼,並將其作為模組增加進專案中使用;
- 下載 SensorsAnalyticsSDK-3.1.16-standalone.jar 並增加到專案使用。
最新版本的神策 Java SDK 支援的 JDK 最低版本為 Java 7,神策 Java SDK 3.1.9 版支援 JDK 1.6。
2. 初始化神策分析 SDK
2.1. 取得設定資訊
首先從神策分析的主頁中,取得數據接收的 URL 和 Token(Cloud 版)。
如果使用神策分析 Cloud 服務,需取得的設定資訊為:
- 數據接收網址,建議使用不帶埠號的: http://{$service_name}.datasink.sensorsdata.cn/sa?project={$project_name}&token={$project_token}
- 數據接收網址,帶埠號的: http://{$service_name}.cloud.sensorsdata.cn:8106/sa?project={$project_name}&token={$project_token}
如果用戶使用單機版私有部署的神策分析,預設的設定資訊為:
- 數據接收網址: http://{$host_name}:8106/sa?project={$project_name}(注:神策分析 1.7 及之前的版本,單機版私有部署預設埠號為 8006)
如果用戶使用叢集版私有部署的神策分析,預設的設定資訊為:
- 數據接收網址: http://{$host_name}:8106/sa?project={$project_name}
其中 {$host_name} 可以是叢集中任意一台電腦。
如果私有部署的過程中修改了 Nginx 的預設設定,或透過 CDN 等連接神策分析,則請諮詢相關人員取得設定資訊。
2.2. 在程式中初始化 SDK
在程式啟動時(如 public static void main(String[] args) 方法中),呼叫建構函式 new SensorsAnalytics(Consumer) 初始化 Java SDK 執行個體。
// 使用 ConcurrentLoggingConsumer 初始化 SensorsAnalytics
final SensorsAnalytics sa = new SensorsAnalytics(new SensorsAnalytics.ConcurrentLoggingConsumer("您的 log 檔路徑"));
// 用戶的 Distinct ID
String distinctId = "ABCDEF123456789";
// 記錄用戶登入行為
sa.track(distinctId, true, "UserLogin");
// 使用神策分析記錄用戶行為數據
// ...
// 程式結束前,停止神策分析 SDK 所有服務
sa.shutdown();
在生產環境中使用 ConcurrentLoggingConsumer,並結合 LogAgent 工具完成數據採集,程式除錯時可以使用 DebugConsumer,詳細資訊請參考文件中 設定 Java SDK 第七節 。
至此,我們已經可以正常使用神策分析 SDK 採集用戶數據了。在開發多執行緒程式時,開發者可以在執行緒間重複使用神策分析執行個體用於記錄數據。
請注意:Debug 模式是為方便開發者除錯而設定的模式,該模式會逐條校驗數據並在校驗失敗時拋出異常,效能遠低於正常模式。
線上環境使用 Debug 模式會嚴重影響效能並存在崩潰風險,產品上線前請務必替換掉/關閉 Debug 模式。
3. 用戶識別
在伺服器端應用中,神策分析也要求為每個事件設定用戶的 Distinct ID,這有助於神策分析提供更準確的留存率等數據。
對於註冊用戶,推薦使用客戶業務系統中的用戶 ID 作為 Distinct ID,不建議使用用戶名、Email、手機號碼等可以被修改的資訊作為 Distinct ID;
對於未註冊的匿名用戶,取得用戶匿名 ID 的方法如下:
1. 後端取得前端 JavaScript SDK 產生的匿名 ID 的方式:
可以在 Cookie 裡面找到 key 為 sensorsdata2015jssdkcross 的 value 值然後進行 decodeURIComponent 解析,最後透過 JSON.parse 方法得到一個物件,物件裡面的 distinct_id 的值即為所需要的 ID
(注意,如果前端已經呼叫過 login 方法,那麼此時 distinct_id 為登入 ID,所以需要先取得 first_id 欄位的值,如果取不到,就會去取得 distinct_id 的值)。
2. 如果 App 中嵌入了 Login 方法,需要用戶端使用神策提供的取得匿名 ID 介面,將匿名 ID 傳給伺服器端,伺服器端使用用戶端傳過來的匿名 ID 作為 Distinct ID。
所有的 track 和 profile 系列方法都必須同時指定用戶 ID(distinctId)和用戶 ID 是否為登入 ID (isLoginId) 這兩個參數,以便明確告知神策分析用戶 ID 的類型。
3.1. 用戶註冊/登入
透過 trackSignUp() 將匿名 ID 和登入 ID 關聯,以確保用戶分析的準確性。例如:
// 前端的匿名 ID,取得匿名 ID 的方式參考上文
String anonymousId = "9771C579-71F0-4650-8EE8-8999FA717761";
String registerId = "0012345678";
// 用戶註冊/登入時,將用戶登入 ID 與匿名 ID 關聯
sa.trackSignUp(registerId, anonymousId);
注意以下問题:
- trackSignUp() 建議在用戶註冊/登入時呼叫。如果用戶端也有採集任意事件,在註冊/登入時,也需要在用戶端呼叫一次關聯方法 login() 將匿名 ID 和登入 ID 關聯。註冊/登入時,用戶端和伺服器端都各自呼叫一次關聯方法的原因如下:
- 一對一關聯機制下,避免出現用戶註冊/登入時,用戶端的關聯資訊$SignUp 事件沒有傳送成功/延遲傳送到神策系統,而伺服器端觸發的事件( $is_login_id=true )先傳送到神策系統中,導致登入ID 自關聯,從而導致登入ID 無法再和匿名ID 關聯,用戶端匿名行為和登入後的行為識別兩個用戶的行為。
- 用戶端呼叫一次關聯方法 login() 的作用,除了將匿名 ID 和 登入 ID 關聯之外,還會將用戶端標記用戶的 distinctId 值從匿名 ID 切換為登入 ID。這樣查看用戶行為序列時,可以很好的根據 distinctId 的值判斷用戶行為是登入後的行為還是匿名行為。因此強烈建議用戶登入/註冊時,在用戶端呼叫一次 login() 的同時,在伺服器端也呼叫一次 trackSignUp() 。
- 如果伺服器端只在用戶登入成功之後,才會採集相關事件或者設定用戶屬性,要確保track 事件/profileSet 設定用戶屬性($is_login_id 設定為true)的程式碼在 trackSignUp() 方法之後呼叫,從而可以確保先將匿名ID 和登入ID 關聯之後,再採集登入用戶的行為事件/設定用戶屬性。
- 在神策分析 1.13 版本之前,多次呼叫 trackSignUp() /login()時,只有第一次關聯行為是有效的。神策分析 1.13 版本之後提供了多裝置 ID 關聯的方法。更詳細的說明請參考 標識用戶,並在必要時聯繫我們的技術支援人員。
4. 追蹤事件
第一次接入神策分析時,建議先追蹤 3~5 個關鍵的事件,只需要幾行程式碼,便能體驗神策分析的分析功能。例如:
- 圖片社交產品,可以追蹤用戶瀏覽圖片和評論事件
- 電商產品,可以追蹤用戶註冊、瀏覽商品和下訂單等事件
神策分析 SDK 初始化成功後,可以透過 track() 記錄事件,必須包含用戶 ID(distinctId)、用戶 ID 是否為登入 ID (isLoginId)、事件名(eventName)這三個參數,同時可以傳入一個 Map<String, Object> 物件,為事件增加自定義事件屬性。以電商產品為例,可以這樣追蹤一次購物行為:
// 使用 ConcurrentLoggingConsumer 初始化 SensorsAnalytics
final SensorsAnalytics sa = new SensorsAnalytics(new SensorsAnalytics.ConcurrentLoggingConsumer("您的 log 檔路徑"));
// 用戶的 Distinct ID
String distinctId = "ABCDEF123456789";
// 用戶瀏覽商品
{
Map<String, Object> properties = new HashMap<String, Object>();
// '$time' 屬性是系統預設屬性,表示事件發生的時間,如果不填入該屬性,則預設使用系統當前時間
properties.put("$time", new Date());
// '$ip' 屬性是系統預設屬性,如果伺服器端中能取得用戶 IP 網址,並填入該屬性,神策分析會自動根據 IP 網址解析用戶的省份、城市資訊
properties.put("$ip", "123.123.123.123");
// 商品 ID
properties.put("ProductId", "123456");
// 商品類別
properties.put("ProductCatalog", "Laptop Computer");
// 是否加入收藏夾,Boolean 型別的屬性
properties.put("isAddedToFav", true);
// 記錄用戶瀏覽商品事件
sa.track(distinctId, true, "ViewProduct", properties);
}
// 用戶訂單付款
{
// 訂單中的商品 ID 列表
List<String> productIdList = new ArrayList<String>();
productIdList.add("123456");
productIdList.add("234567");
productIdList.add("345678");
Map<String, Object> properties = new HashMap<String, Object>();
properties.put("$ip", "123.123.123.123");
// 訂單 ID
properties.put("OrderId", "abcdefg");
// 商品 ID 列表,List<String> 型別的屬性
properties.put("ProductIdList", productIdList);
// 訂單金額
properties.put("OrderPaid", 12.10);
// 記錄用戶訂單付款事件
sa.track(distinctId, true, "PaidOrder", properties);
}
透過 除錯模式 ,可以校驗追蹤的事件及屬性是否正確。正常模式下,數據匯入後,在神策分析中稍等片刻,便能看到追蹤結果。
4.1. 事件屬性
如前文中的範例,追蹤的事件可以設定自定義的事件屬性,例如瀏覽商品事件中,將商品 ID、商品分類等資訊作為事件屬性。在後續的分析工作中,事件屬性可以作為統計過濾條件使用,也可以作為維度進行多維分析。對於事件屬性,神策分析有一些限制:
- 事件屬性是一个 Map<String, Object> 物件;
- Map<String, Object> 中每個元素描述一個屬性,Key 為屬性名稱,必需是 String 型別;
- Map<String, Object> 中,每個元素的 Value 是屬性的值,支援 String、Boolean、Number、List<String> 和 Date。
對於神策分析中事件屬性的更多限制,請參考 數據格式。在開發多執行緒程式時,開發者不能在執行緒間重複使用傳入的屬性物件。
4.1.1. 系统預設屬性
如前文中範例,事件屬性中以 '$' 開頭的屬性為系統預設屬性,在自定義事件屬性中填入對應 '$' 開頭的屬性值可以覆蓋這些預設屬性:
- $ip - 填入該屬性,神策分析會自動根據 IP 網址解析用戶的省份、城市資訊,該屬性值為 String 型別;
- $time - 填入該屬性,神策分析將事件時間設定為屬性值的時間,該屬性值必須為 Date 型別。請注意,神策分析預設會過濾忽略 2 年前或 1 小時後的數據,如需修改請聯繫我們;
- $project - 填入該屬性,神策分析某些匯入工具例如 LogAgent (LogAgent 的設定中未指定 project 參數時)會將數據匯入指定專案。
關於其他更多預設屬性,請參考 數據格式 中 '預設屬性' 一節。
4.1.2. 事件公共屬性
特别地,如果某個事件的屬性,在所有事件中都會出現,可以透過 registerSuperProperties() 將該屬性設定為事件公共屬性。例如將伺服器的應用版本及機房網址設定為事件的公共屬性,設定方法如下:
Map<String, Object> properties = new HashMap<String, Object>();
// 伺服器應用版本
properties.put("ServerVersion", "1.2");
// 伺服器機房網址
properties.put("Location", "BeiJing");
// 設定事件公共屬性
sa.registerSuperProperties(properties);
成功設定事件公共屬性後,再透過 track() 追蹤事件時,事件公共屬性會被增加進每個事件中,例如:
Map<String, Object> properties = new HashMap<String, Object>();
// 登入用戶端 IP 網址
properties.put("$ip", "123.123.123.123");
// 追蹤用戶登入事件
sa.track("ABCDEF123456789", true, "UserLogin", properties);
在設定事件公共屬性後,實際傳送的事件中會被加入 ServerVersion 和 Location 屬性,等同於
Map<String, Object> properties = new HashMap<String, Object>();
// 事件公共屬性
properties.put("ServerVersion", "1.2");
properties.put("Location", "BeiJing");
// 登入用戶端 IP 網址
properties.put("$ip", "123.123.123.123");
// 追蹤用戶登入事件
sa.track("ABCDEF123456789", true, "UserLogin", properties);
使用 clearSuperProperties() 會刪除所有已設定的事件公共屬性。
當事件公共屬性和事件屬性的 Key 衝突時,事件屬性優先級最高,它會覆蓋事件公共屬性。
5. 設定用戶屬性
為了更準確地提供針對人群的分析服務,神策分析 SDK 可以設定用戶屬性,如年齡、性別等。用戶可以在留存分析、分佈分析等功能中,使用用戶屬性作為過濾條件或以用戶屬性作為維度進行多維分析。
使用 profileSet() 設定用戶屬性:
String distinctId = "ABCDEF123456789";
// 設定用戶性別屬性(Sex)為男性
sa.profileSet(distinctId, true, "Sex", "Male");
Map<String, Object> properties = new HashMap<String, Object>();
// 設定用戶等級屬性(Level)為 VIP
properties.put("UserLv", "Elite VIP");
sa.profileSet(distinctId, true, properties);
對於不再需要的用戶屬性,可以透過 profileUnset() 介面將屬性刪除。
用戶屬性中,屬性名稱與屬性值的限制條件與事件屬性相同,詳細說明請參考 數據格式。
5.1. 記錄初次設定的屬性
對於只在首次設定時有效的屬性,我們可以使用 profileSetOnce() 記錄這些屬性。與 profileSet() 介面不同的是,如果被設定的用戶屬性已存在,則這條記錄會被忽略而不會覆蓋已有數據,如果屬性不存在則會自動建立。因此,profileSetOnce() 比較適用於為用戶設定首次啟用時間、首次註冊時間等屬性。例如
String distinctId = "ABCDEF123456789";
// 設定用戶管道屬性(AdSource)為 "App Store"
sa.profileSetOnce(distinctId, true, "AdSource", "App Store");
// 再次設定用戶管道屬性(AdSource),設定無效,屬性 "AdSource" 的值仍為 "App Store"
sa.profileSetOnce(distinctId, true, "AdSource", "Search Engine");
5.2. 數值型別的屬性
對於數值型的用戶屬性,可以使用 profileIncrement() 對屬性值進行累加。常用於記錄用戶付費次數、付費額度、積分等屬性。例如:
String distinctId = "ABCDEF123456789";
// 設定用戶遊戲次數屬性(GamePlayed),將次數累加1次
sa.profileIncrement(distinctId, true, "GamePlayed", 1);
5.3. 列表型別的屬性
對於用戶喜愛的電影、用戶評價過的餐廳等屬性,可以記錄列表型屬性。需要注意的是,列表型屬性中的元素必須為 String 型別,且元素的值會自動去重。關於列表類型限制請見 數據格式 1.7.4 屬性長度限制。
String distinctId = "ABCDEF123456789";
// 電影列表
List<String> movies = new ArrayList<String>();
movies.add("Sicario");
movies.add("Love Letter");
// 遊戲列表
List<String> games = new ArrayList<String>();
games.add("Call of Duty");
games.add("Halo");
// 用戶屬性
Map<String, Object> properties = new HashMap<String, Object>();
properties.put("movies", movies);
properties.put("games", games);
// 傳入properties,設定用戶喜歡的電影屬性(movies)和喜歡的遊戲屬性(games)
// 設定成功後,"movies" 屬性值為 ["Sicario", "Love Letter"];"games" 屬性值為 ["Call of Duty", "Halo"]
sa.profileAppend(distinctId, true, properties);
// 傳入屬性名稱和需要插入屬性的值,設定用戶喜歡的電影屬性(movies)
// 設定成功後 "movies" 屬性值為 ["Sicario", "Love Letter", "Dead Poets Society"]
sa.profileAppend(distinctId, true, "movies", "Dead Poets Society");
// 傳入屬性名稱和需要插入屬性的值,設定用戶喜歡的電影屬性(movies),
// 但屬性值 "Love Letter" 與已列表中已有元素重複,操作無效,
// "movies" 屬性值仍然為 ["Sicario", "Love Letter", "Dead Poets Society"]
sa.profileAppend(distinctId, true, "movies", "Love Letter");
6. 物品元數據上報
在神策推薦專案中,客戶需要將物品元數據上報,以開展後續推薦業務的開發與維護。神策分析 SDK 提供了設定與刪除物品元數據的方法。
item_id(物品 ID )與 item_type (物品所屬型別)共同組成了一個物品的唯一標識。所有的 item 系列方法都必須同時指定物品 ID 及物品所屬型別這兩個參數,來完成對物品的操作。
6.1. 設定物品
直接設定一個物品,如果已存在則覆蓋。除物品 ID 與物品所屬型別外,其他物品屬性需在 properties 中定義。
物品屬性中,屬性名稱與屬性值的限制條件與事件屬性相同,詳細說明請參考 數據格式。
public void itemSet(String itemType, String itemId, Map<String, Object> properties);
// 例如
Map<String, Object> properties = new LinkedHashMap<>();
properties.put("name", "C++ Primer");
properties.put("price", 31.54);
sensorsAnalytics.itemSet("book", "0321714113", properties);
6.2. 刪除一個物品
如果物品不被推薦而需要下線,刪除該物品即可,如不存在則忽略。
除物品 ID 與 物品所屬型別外,不解析其他物品屬性。
public void itemDelete(String itemType, String itemId, Map<String, Object> properties);
// 例如
sensorsAnalytics.itemDelete("book", "0321714113", null);
7. 立刻上報快取數據
如果想要事件數據、用戶數據或者物品數據立刻上報,可以呼叫 flush() 方法:
// 立刻上報快取數據
sa.flush();
8. 設定神策分析 SDK
以下內容說明如何更精細地控制神策分析 SDK 的行為。
8.1. 數據採集
Java SDK 主要由以下兩個元件構成:
- SensorsAnalytics: 用於傳送數據的介面物件,建構函式需要傳入一個 Consumer 執行個體
- Consumer: Consumer 會進行實際的數據傳送
為了讓開發者更靈活的接入數據,神策分析 SDK 實作了以下 Consumer。
8.1.1. DebugConsumer
用於校驗數據匯入是否正確,關於 除錯模式 的詳細資訊,請進入相關頁面查看。
請注意:Debug 模式是為方便開發者除錯而設定的模式,該模式會逐條校驗數據並在校驗失敗時拋出異常,效能遠低於正常模式。
線上環境使用 Debug 模式會嚴重影響效能並存在崩潰風險,產品上線前請務必替換掉/關閉 Debug 模式。
// 從神策分析取得的數據接收的 URL
final String SA_SERVER_URL = "YOUR_SERVER_URL";
// 使用 Debug 模式,並且匯入 Debug 模式下所傳送的數據
final boolean SA_WRITE_DATA = true;
// 使用 DebugConsumer 初始化 SensorsAnalytics
final SensorsAnalytics sa = new SensorsAnalytics(
new SensorsAnalytics.DebugConsumer(SA_SERVER_URL, SA_WRITE_DATA));
// 使用神策分析記錄用戶行為數據
// ...
// 程式結束前,停止神策分析 SDK 所有服務
sa.shutdown();
8.1.2. ConcurrentLoggingConsumer
用於將數據輸出到指定目錄並按天切割產生 log,並使用 LogAgent 等工具匯入,該工具能確保匯入不重複、不遺漏。 推薦在生產環境中使用 ConcurrentLoggingConsumer 匯入數據。支援多個程序寫同一個目錄(目錄不能是 nas、nfs 類別的檔案系統),產生的檔案始終是帶日期字尾的,每天一個。
- ConcurrentLoggingConsumer 內部有一個 8k 的快取佇列,當快取佇列寫滿時落地寫入磁碟 log 檔中(如果測試的數據量較少,達不到快取上限,則數據不會落地到 log )。快取佇列的長度可在建構函式中設定,也可以呼叫 flush() 方法強制落地。
- LogAgent 設定檔案中一定要註解掉 real_time_file_name 參數,否則無法正常匯入數據。
// 使用 ConcurrentLoggingConsumer 初始化 SensorsAnalytics
// 將數據輸出到 /data/sa 下的 access.log.2017-01-11 文件中,每天一個文件;對應目錄需要自己建立,log 檔會自動產生
final SensorsAnalytics sa = new SensorsAnalytics(
new SensorsAnalytics.ConcurrentLoggingConsumer("/data/sa/access.log"));
// !! 注意 !! 如果是在 Windows 環境下使用 ConcurrentLoggingConsumer 並使用 LogAgent 傳送數據,
// 需要額外透過建構函式的第二個參數指定一個檔案網址用於檔案加密,例如:
// SensorsAnalytics sa = new SensorsAnalytics(
// new SensorsAnalytics.ConcurrentLoggingConsumer("D:\\data\\service", "D:\\var\\sa.lock"));
// 若該檔案與數據檔案同目錄,設定 LogAgent 的 pattern 時請不要匹配到這個檔案。
// 使用神策分析記錄用戶行為數據
// ...
// 程式結束前,停止神策分析 SDK 所有服務
sa.shutdown();
8.1.3. LoggingConsumer
已不推薦在生產環境中使用,因為在多程序下檔案切分可能有問題。已使用 LoggingConsumer 的客戶建議按照如下步驟切換到 ConcurrentLoggingConsumer:
第 1 步 停掉 LogAgent,並註解掉 LogAgent 設定中的 real_time_file_name 參數。
第 2 步 將 log 下的 real_time_file_name 的檔案加上當前時間的字尾。
第 3 步 後端程式升級切換到 ConcurrentLoggingConsumer。
第 4 步 重新啟動 LogAgent。
關於 LogAgent 操作請參見 LogAgent
8.1.4. BatchConsumer
批量傳送數據的 Consumer,當數據達到指定的量時(預設50條,最多可指定1000條),才將數據進行傳送。也可以呼叫 flush() 方法去強制傳送。
通常用於匯入小規模歷史數據,或者離線 / 略過匯入數據的場景。由於是網絡直接傳送數據,如果網路出現異常可能會導致數據重發或遺失,因此不要用在任何線上服務中。
// 從神策分析取得的數據接收的 URL
final String SA_SERVER_URL = "YOUR_SERVER_URL";
// 當快取的數據量達到50條時,批量傳送數據
final int SA_BULK_SIZE = 50;
// 數據同步失敗不拋出異常
final boolean THROW_EXCEPTION = false;
// 記憶體中數據最大快取條數,如此值大於0,代表快取的數據會有條數限制,最小 3000 條,最大 6000 條。否則無條數限制。
final int MAX_CACHE_SIZE = 0;
// 使用 BatchConsumer 初始化 SensorsAnalytics
// 不要在任何線上的服務中使用此 Consumer
final SensorsAnalytics sa = new SensorsAnalytics(
new SensorsAnalytics.BatchConsumer(SA_SERVER_URL, SA_BULK_SIZE, MAX_CACHE_SIZE, THROW_EXCEPTION));
// 使用神策分析記錄用戶行為數據
// ...
// 程式結束前,停止神策分析 SDK 所有服務
sa.shutdown();
8.1.5. ConsoleConsumer
用於將數據輸出到特定 Writer,一般用於在生產環境的 Java 程式中處理歷史數據,產生 log 檔並使用 BatchImporter 等工具匯入
// 將數據輸出到標準輸出
final Writer writer = new PrintWriter(System.out);
// 使用 ConsoleConsumer 初始化 SensorsAnalytics
final SensorsAnalytics sa = new SensorsAnalytics(
new SensorsAnalytics.ConsoleConsumer(writer));
// 使用神策分析記錄用戶行為數據
// ...
// Flush the writer
writer.flush();
8.2. 關閉 SDK
如果您想要主動關閉 SDK,可以參考以下使用方式:
// 關閉神策分析 SDK 所有服務並銷毀執行個體
sa.shutdown();
8.3. 其它設定
匯入歷史數據:預設情況下,神策會過濾發生時間比較久遠數據(例如 10 天之前,實際情況取決於伺服器端設定),如果想匯入歷史數據,可以透過開啟 Time Free 選項來繞過這個限制。
// 初始化 SensorsAnalytics
final SensorsAnalytics sa = new SensorsAnalytics(...);
// 開啟 Time Free 以便匯入歷史數據
sa.setEnableTimeFree(true);