Menu
ESC

Nhập từ khóa để tìm kiếm

↑↓ Di chuyển
Enter Mở
ESC Đóng

Đang tải...

Bài 36 — Mobile App Specific Tracking

Product Analytics Mixpanel and Amplitude Bài 36/60

Mở đầu — vì sao bài này quan trọng

Nếu bạn đã đi qua các bài về event taxonomy, funnel, retention, bạn có thể nghĩ rằng tracking trên mobile app cũng giống hệt tracking trên web — chỉ khác cái SDK. Đó là một trong những hiểu lầm tốn kém nhất mà tôi thấy các PM Việt Nam mắc phải. Trên web, người dùng luôn online, session có ranh giới rõ ràng, và bạn kiểm soát được toàn bộ luồng qua trình duyệt. Trên mobile, mọi giả định đó đều sụp đổ.

Người dùng ở Việt Nam đi thang máy mất sóng, đi xe bus qua hầm, ngồi quán cà phê Wi-Fi chập chờn. Họ mở app rồi bấm nút Home để nhận cuộc gọi, mười phút sau quay lại. Họ cài app từ một quảng cáo TikTok nhưng bạn không biết chắc quảng cáo nào mang họ về. Họ nhận push notification lúc 8h tối và bạn cần biết push đó có thực sự kéo họ mở app hay không.

Nếu tracking của bạn không xử lý được những tình huống đặc thù này, dữ liệu bạn nhìn thấy trong Mixpanel hay Amplitude sẽ sai — không phải sai kiểu thiếu vài phần trăm, mà sai kiểu ra quyết định ngược. DAU tụt vì event bị mất khi offline chứ không phải vì người dùng bỏ đi. Conversion attribution gán nhầm về organic vì bạn không đo được install source. Bài này tập trung đúng vào bốn thách thức riêng của mobile: buffer event khi offline, xử lý background/foreground, install attribution, và đo push notification engagement. Đây là phần mà không bài nào khác trong khóa dạy thay bạn được.

Khái niệm cốt lõi

1. Offline buffering — event không được phép biến mất

Trên mobile, SDK không gửi event ngay lập tức từng cái một. Nếu làm vậy, mỗi lần mất mạng là event bay mất, và pin điện thoại cũng cạn vì liên tục mở kết nối. Thay vào đó, cả Mixpanel SDK lẫn Amplitude SDK đều dùng cơ chế local queue + batching: event được ghi vào một hàng đợi lưu trên bộ nhớ thiết bị (SQLite hoặc file trên disk), rồi gửi theo lô (batch) khi có mạng và khi đủ điều kiện flush.

Điều bạn cần nắm với vai trò PM:

  • Flush interval: SDK gom event và gửi theo chu kỳ (mặc định Amplitude khoảng 30 giây hoặc khi đủ 30 event; Mixpanel cũng có ngưỡng tương tự). Bạn chỉnh được thông số này.
  • Persistence khi offline: khi mất mạng, event nằm yên trong queue trên thiết bị, không mất. Khi có mạng lại, SDK tự flush.
  • Giới hạn queue: queue có trần (ví dụ 1000 event). Nếu người dùng offline quá lâu và sinh quá nhiều event, event cũ nhất bị loại bỏ.
Hệ quả quan trọng nhất: thời điểm event xảy ra (time) khác thời điểm server nhận được. Một người dùng đi tàu điện Cát Linh - Hà Đông có thể sinh event lúc 8h05 nhưng event chỉ tới server lúc 8h20 khi ra khỏi hầm. Nếu bạn không dùng đúng timestamp phía client, biểu đồ theo giờ của bạn sẽ lệch.

2. Background / Foreground — session không tự nhiên như web

Trên web, đóng tab là hết session. Trên mobile, app không "chết" khi người dùng bấm Home — nó chuyển sang background và có thể sống nhiều giờ. Vòng đời một app gồm các trạng thái: foreground (đang dùng), background (ẩn nhưng còn sống), suspended/terminated (bị hệ điều hành thu hồi).

Các SDK định nghĩa session dựa trên ngưỡng thời gian: nếu app ở background quá lâu (mặc định thường 5 phút với Amplitude) rồi quay lại foreground, đó được tính là session mới. Dưới ngưỡng đó thì vẫn là session cũ. Con số này ảnh hưởng trực tiếp đến các metric như session length, sessions per user, và cả stickiness.

Bạn cũng cần chủ động gửi event khi trạng thái đổi: Application BackgroundedApplication Foregrounded. Nhiều SDK tự bắt, nhưng bạn nên xác nhận vì nó là nền tảng để phân tích hành vi "người dùng rời app đi đâu, quay lại lúc nào".

3. Install attribution — ai mang người dùng này về?

Đây là bài toán mobile không có trên web. Khi ai đó bấm quảng cáo trên Facebook/TikTok rồi được đưa tới App Store hoặc Google Play, có một khoảng đứt gãy: store không cho phép truyền tham số URL trực tiếp vào app như web truyền query string. Vì vậy cần một lớp Mobile Measurement Partner (MMP) như AppsFlyer, Adjust, hoặc Branch làm trung gian đo lường.

Cơ chế phổ biến:

  • Deferred deep linking: MMP dùng kỹ thuật fingerprint hoặc click ID để ghép cú click quảng cáo với lần mở app đầu tiên sau khi cài.
  • SKAdNetwork / Privacy Sandbox: sau khi Apple ra ATT (App Tracking Transparency), attribution trên iOS chủ yếu qua SKAdNetwork với dữ liệu tổng hợp, độ trễ và ẩn danh cao hơn.
Trong Mixpanel/Amplitude, dữ liệu attribution thường được đẩy vào qua tích hợp với MMP, gắn vào event Install hoặc First Open dưới dạng properties như utm_source, media_source, campaign.

4. Push notification engagement — đo cả vòng đời của một cú push

Push là kênh re-engagement lớn nhất trên mobile, nhưng nhiều team chỉ đo "đã gửi bao nhiêu". Để phân tích đúng, bạn cần đo cả phễu push: Push SentPush DeliveredPush Opened → hành động sau khi mở. Đặc biệt cần bắt được Push Opened với property định danh chiến dịch push (campaign_id) để nối được push với hành vi downstream trong app.

Tình huống thực tế

Ví dụ 1 — App gọi xe ở TP.HCM và cái "hố" DAU trong hầm

Một startup gọi xe (gọi là XanhGo) tích hợp Amplitude vào app tài xế. Sau một tuần, đội data hoảng vì biểu đồ event theo giờ có những cú tụt bất thường vào khung 7-9h sáng — đúng giờ cao điểm, đáng lẽ phải nhiều event nhất. Họ nghi ngờ có bug làm mất event.

Điều tra ra: rất nhiều tài xế chạy qua các đoạn hầm, cầu vượt, khu vực sóng yếu. Event vẫn được buffer đúng trên thiết bị, nhưng cả đội đang nhìn biểu đồ dựng theo thời điểm server nhận thay vì event_time phía client. Khi tài xế ra khỏi vùng mất sóng lúc 9h30, một loạt event dồn lên server, tạo ra cú "phình" giả lúc 9h30 và cú "hụt" giả lúc 8h.

Cách xử lý: đội chuyển toàn bộ phân tích theo giờ sang dùng client-side timestamp, đồng thời thêm một property network_type (wifi/4g/offline_replayed) để phân biệt event gửi realtime và event replay từ buffer. Sau khi sửa, đường DAU và event-per-hour phẳng đúng như thực tế.

Bài học: Offline buffering không làm mất dữ liệu, nhưng nếu bạn phân tích theo sai timestamp, nó làm méo dữ liệu — nguy hiểm hơn cả mất dữ liệu vì bạn không hề nghi ngờ.

Ví dụ 2 — App học tiếng Anh và session bị thổi phồng

Một app học tiếng Anh (gọi là EngUp) báo cáo với nhà đầu tư rằng "session length trung bình 18 phút, người dùng rất gắn kết". Nhưng khi một PM mới vào soi kỹ, con số không khớp với cảm nhận thực tế: bài học chỉ dài 5-7 phút.

Nguyên nhân nằm ở cấu hình session. App có tính năng phát audio, và nhiều người dùng mở bài nghe rồi bấm Home để nghe trong lúc làm việc khác. App chạy background, nhưng ngưỡng "session timeout" của SDK bị để quá dài (30 phút thay vì mặc định 5 phút). Kết quả: người dùng nghe 5 phút rồi thoát, 20 phút sau quay lại được tính là cùng một session kéo dài, thổi session length lên gấp ba.

Đội đã làm hai việc: đưa ngưỡng session về 5 phút cho đúng bản chất học tập ngắt quãng, và tách riêng thời gian foreground thực (dùng cặp event Application Backgrounded/Foregrounded để trừ đi khoảng background). Sau đó session length "thật" về mức 6 phút — thấp hơn nhưng trung thực, và giúp họ nhận ra vấn đề retention thật sự thay vì tự ru ngủ bằng con số ảo.

Bài học: Định nghĩa session trên mobile là một quyết định sản phẩm, không phải mặc định kỹ thuật. Sai ngưỡng là sai toàn bộ metric engagement phía dưới.

Ví dụ 3 — Sàn TMĐT và push notification bị đo sai công

Một sàn thương mại điện tử ở Đông Nam Á gửi 2 triệu push flash sale mỗi đợt. Đội growth khoe "push mang về 15% doanh số flash sale". Nhưng đội analytics phát hiện họ đang gán công sai.

Vấn đề: họ chỉ đo Push Sent và sau đó đo tổng số đơn trong 2 giờ sau push. Nhưng nhiều đơn trong đó đến từ người dùng vào app qua cách khác (mở trực tiếp, click banner Facebook), không phải do push. Họ thiếu event Push Opened gắn campaign_id, nên không phân biệt được ai thật sự vào app push.

Sau khi triển khai đúng phễu Push Sent → Delivered → Opened → Purchase với campaign_id xuyên suốt, con số thật rơi xuống còn khoảng 6% — vẫn tốt, nhưng chỉ bằng non nửa con số cũ. Quan trọng hơn, họ phát hiện tỉ lệ Delivered/Sent trên Android chỉ 82% (do một số hãng máy Trung Quốc kill background aggressive), một vấn đề hạ tầng mà trước đó hoàn toàn không thấy.

Bài học: Không đo Push Opened với định danh chiến dịch thì mọi con số ROI của push đều là phỏng đoán. Và tỉ lệ delivered là một chỉ số vàng thường bị bỏ quên.

Hướng dẫn từng bước

Đây là quy trình tôi khuyên bạn áp dụng khi thiết lập tracking cho một mobile app mới:

  • Xác định timestamp chuẩn: Thống nhất phân tích theo client event_time, không phải server receive time. Ghi rõ điều này trong tracking plan để mọi dashboard đều nhất quán.
  • Cấu hình offline buffering có chủ đích: Xác nhận SDK bật persistence (mặc định thường bật). Chọn flush interval hợp lý — không quá ngắn để đỡ hao pin/data, không quá dài để đỡ mất event khi app bị kill. Với app dùng nhiều ở nơi sóng yếu, cân nhắc thêm property đánh dấu event replay.
  • Định nghĩa session rõ ràng: Chọn ngưỡng session timeout theo bản chất sản phẩm (app đọc tin ngắn khác app xem phim dài). Bật event Application Foregrounded/Backgrounded.
  • Tích hợp MMP cho attribution: Cắm AppsFlyer/Adjust/Branch, và đảm bảo dữ liệu attribution (media_source, campaign, utm_*) được đẩy vào Mixpanel/Amplitude gắn với First Open. Trên iOS, lên kế hoạch cho ATT và SKAdNetwork từ đầu.
  • Dựng phễu push đầy đủ: Instrument Push Sent, Push Delivered, Push Opened với campaign_id, push_type. Nối Push Opened tới các event chuyển đổi downstream để đo công thật.
  • Kiểm định trên thiết bị thật: Test bằng cách bật chế độ máy bay giữa chừng, bấm Home rồi quay lại, cài lại app từ một link quảng cáo test. Đừng bao giờ tin dữ liệu chỉ từ giả lập.

Lỗi thường gặp & mẹo

  • Phân tích theo server time: lỗi phổ biến nhất, làm méo mọi biểu đồ theo thời gian như đã thấy ở ví dụ XanhGo. Luôn dùng client event_time.
  • Để session timeout mặc định mà không suy nghĩ: mỗi loại app cần ngưỡng khác nhau. Xem lại nó như một quyết định sản phẩm.
  • Chỉ đo push đã gửi: bỏ qua DeliveredOpened khiến bạn không thấy vấn đề giao push (đặc biệt trên Android tại thị trường nhiều máy Trung Quốc) và gán sai công cho push.
  • Quên xử lý duplicate khi replay: khi mạng chập chờn, event có thể bị gửi hai lần. Đảm bảo mỗi event có insert_id (Amplitude) hoặc $insert_id (Mixpanel) để hệ thống tự khử trùng lặp.
  • Bỏ qua độ trễ attribution trên iOS: SKAdNetwork trả dữ liệu tổng hợp và trễ. Đừng kỳ vọng attribution realtime như Android; điều chỉnh báo cáo tương ứng.
  • Mẹo pin & data: với người dùng gói data rẻ, batch lớn hơn và flush thưa hơn giúp tiết kiệm; nhưng đánh đổi bằng rủi ro mất event nếu app bị kill. Tìm điểm cân bằng theo hành vi người dùng thật.
  • Mẹo cho máy Android bị kill background: đừng phụ thuộc hoàn toàn vào flush định kỳ; ép flush tại các event quan trọng (như purchase) để không mất dữ liệu doanh thu.

Bài tập thực hành

  • Kiểm tra timestamp: Mở dashboard event-per-hour hiện tại của một app bạn đang làm (hoặc app demo). Xác định nó đang dùng client time hay server time. Nếu không rõ, dựng hai biểu đồ song song và so sánh — bạn sẽ thấy ngay độ lệch ở khung giờ di chuyển.
  • Thiết kế phễu push: Viết ra tracking plan cho một chiến dịch push flash sale, liệt kê đầy đủ các event Push Sent/Delivered/Opened/Purchase và các property cần có (campaign_id, push_type, platform). Xác định bạn sẽ tính "công của push" bằng công thức nào.
  • Chọn ngưỡng session: Cho ba loại app — app đọc báo, app học ngôn ngữ có audio, app xem phim — hãy đề xuất ngưỡng session timeout cho từng loại và giải thích lý do dựa trên hành vi background/foreground.
  • Mô phỏng offline: Nếu có app thật, bật chế độ máy bay, thao tác 5-6 hành động, tắt chế độ máy bay và quan sát event dồn lên trong Mixpanel/Amplitude. Kiểm tra xem timestamp có đúng thời điểm bạn thao tác không, và có bị nhân đôi event nào không.

Tóm tắt

Tracking trên mobile app không phải là "tracking web nhưng đổi SDK". Bốn thách thức đặc thù mà bạn phải chủ động xử lý là: offline buffering (event được lưu và gửi theo lô, nên luôn phân tích theo client event_time để không méo dữ liệu); background/foreground (session là một quyết định sản phẩm, chọn sai ngưỡng là sai mọi metric engagement); install attribution (cần MMP như AppsFlyer/Adjust và kế hoạch riêng cho ATT/SKAdNetwork trên iOS); và push engagement (phải đo cả phễu Sent → Delivered → Opened → hành động với campaign_id để biết công thật của push).

Ba tình huống XanhGo, EngUp và sàn TMĐT cho thấy cùng một bài học lớn: trên mobile, dữ liệu ít khi "mất" một cách lộ liễu — nó bị méo một cách âm thầm. Người PM giỏi analytics là người biết những chỗ méo này nằm ở đâu và thiết kế tracking để nhìn xuyên qua chúng. Hãy kiểm định trên thiết bị thật, và luôn hỏi: con số này có phản ánh đúng hành vi con người, hay chỉ phản ánh đặc tính của mạng và vòng đời app?