Mục lục trang
1 Các loại ads
Tài liệu này dùng đúng tên tiếng Anh mà cả team (kể cả bên đối tác quảng cáo) đang dùng — bảng thuật ngữ ngay dưới đây. Ba nhóm chính, phân biệt theo cảm nhận của người dùng — còn cơ chế request/load/show/cache của từng loại thì ở mục 3.
| Thuật ngữ | Nghĩa |
|---|---|
| ads | Quảng cáo, nói chung. |
| inter ads interstitial | Ads che kín màn hình, chèn giữa 2 thao tác; có nút đóng (✕) hoặc tự đóng. |
| AOA App Open Ads | Ads che kín màn hình lúc mở app hoặc quay lại app; tự đóng khi xem xong, không có nút đóng riêng. |
| native ads | Ô ads nằm trong nội dung, giống một thẻ của app, không che màn. |
| native full ads | Một native ads nhưng chiếm cả màn hình, show ngay sau khi một inter ads vừa đóng. Tắt mặc định. |
| reward ads rewarded | Người dùng tự bấm xem để đổi lấy quyền lợi. Không bao giờ tự bật lên. |
| full-screen ads | Cách gọi gộp 3 loại che kín màn: inter ads · AOA · native full ads. |
| request | App hỏi Google "có ad cho vị trí này không". Mỗi lần request = 1 lượt ad_request. |
| load | Google trả ad về máy. Có ad = fill, hết ad = no-fill. |
| show | Ad thật sự hiện lên cho người dùng thấy. Load xong chưa chắc show được. |
| cache | Ad đã load về mà chưa/không show được thì giữ lại cho lượt sau. Từ chiều 06/08/2026 không loại ads nào còn cache — thuật ngữ này chỉ còn dùng khi nói về lịch sử/thay đổi, không phải cơ chế đang chạy. |
| timeout | Trần thời gian chờ load. Quá timeout là app đi tiếp, không đợi ad nữa. |
| fallback | Đường lùi khi không show được ad chính. Hiện chỉ còn 1 dạng: reward ads hết ad thì thay bằng inter ads. Native ads không còn fallback nào — load fail là ẩn ô. |
| capping | Giãn cách — khoảng thời gian tối thiểu giữa 2 lần show cùng một loại ads (mục 5). |
| fill / no-fill | Request về có ad để show / không có ad nào (Google hết ad cho vị trí đó). |
Full-screen ads
Gồm inter ads (có nút đóng / tự đóng khi hết đếm ngược) và AOA (tự đóng ngay khi xem xong). Hiện ở: mở app lần đầu, mở mini-app, quay lại app sau khi thoát ra ngoài, bấm nút đóng trên các mini-app, và một số thao tác trong Cài đặt. Có thêm 1 biến thể đặc biệt — native full ads ngay sau một lần inter ads — đang tắt mặc định.
Nguồn: ads/InterstitialAdManager.kt, ads/SplashAppOpenAdLoader.kt, ads/NativeFullAdManager.ktNative ads
Có mặt ở 28 vị trí khác nhau trong app: Cài đặt và các màn con, chọn ngôn ngữ, mọi màn "đang tải", Thư viện ảnh, kho Theme Store/Hình nền… Mỗi lần vào màn có ô này, ô request ad mới (cuộn ra/vào hay đổi tab thì KHÔNG request lại — mục 10). Trong lúc load: ô hiện khung mờ (shimmer). Load fail thì ô tự ẩn gọn ngay — không có cache dự phòng nào để mượn.
Nguồn: ads/NativePlacements.kt (28 placement), ads/NativeAdLoader.kt, ui/components/AdMobNativeAd.kt (NativeSlotState)Reward ads
2 nơi dùng: Chat AI (hết lượt chat miễn phí trong ngày → xem để +1 lượt ngay) và Theme Store (mở khoá vĩnh viễn 1 theme/gói icon/hình nền/widget miễn phí, hoặc xem để +1 xu trong Ví xu). Không giới hạn số lần xem, trừ xem-lấy-xu (tối đa 3 lần/ngày). Không có reward ads để hiện thì app thay bằng 1 inter ads và vẫn trả thưởng — mục 11.
Nguồn: ads/RewardedAdManager.kt, data/chat/ChatCreditStore.kt, data/themestore/CoinStore.ktDanh sách 28 vị trí native ads (tham số placement)
Tra khi lọc Firebase theo tham số placement của ad_request/ad_loaded/
ad_load_failed/ad_clicked/ad_displayed (mục 4 trang Events).
placement = native_default
ở 4 sự kiện đầu funnel (ad_request/ad_loaded/ad_load_failed/ad_clicked),
trong khi sự kiện ad_displayed lại ghi đúng tên màn — nên KHÔNG lọc được matchrate/showrate/ctr
riêng cho từng màn (tử số và mẫu số đếm ở hai nhãn khác nhau). Giờ cả 5 sự kiện của một vị trí đều dùng
đúng một nhãn — lọc placement ra số nào là đúng luồng của riêng màn đó.
| Màn hình / vị trí trong app | Giá trị placement |
|---|---|
| Giới thiệu (Intro) lần đầu mở app | native_intro |
| Cài đặt — trang chính + màn Ứng dụng | settings |
| Cài đặt — các màn con (General/Đổi icon/Control Center…) | settings_sub |
| Cài đặt — Ẩn ứng dụng (Hidden Apps) | settings_hidden_apps |
| DailyOpen (kéo xuống từ Home) | daily_open |
| Thư viện ứng dụng (App Library) | app_library |
| Spotlight (kéo xuống tìm kiếm) | spotlight |
| Tối ưu ứng dụng (App Optimize) — màn chính | app_optimize |
| Tối ưu ứng dụng — bảng cài app mới (sheet) | app_optimize_sheet |
| Thư viện ảnh — màn chính (Library) | gallery_library |
| Thư viện ảnh — màn xin quyền truy cập | gallery_permission |
| Thư viện ảnh — danh sách Album | gallery_albums |
| Thư viện ảnh — chi tiết 1 Album | gallery_album_detail |
| Thư viện ảnh — Thùng rác (Recently Deleted) | gallery_trash |
| Kho Theme Store — item trong grid gói Icon | store_iconpack_item |
| Kho Theme Store — màn chi tiết cuối (Xem thử theme / Áp dụng thành công) | theme_store_bottom |
| Kho Theme Store — tab Trending | theme_store_trending |
| Kho Theme Store — tab Hình nền (Wallpaper) | theme_store_wallpaper_tab |
| Kho Theme Store — tab Theme | theme_store_themes_tab |
| Kho Theme Store — tab Icon | theme_store_icons_tab |
| Kho Theme Store — tab Widget | theme_store_widgets_tab |
| Kho Theme Store — màn "Của tôi" (Theme/Icon/Hình nền/Widget đã có) | theme_store_my_content |
| Kho Theme Store — chi tiết danh mục Hình nền | theme_store_wallpaper_category_detail |
| Kho Theme Store — chi tiết danh mục Theme | theme_store_theme_category_detail |
| Kho Theme Store — chi tiết danh mục gói Icon | theme_store_icon_category_detail |
| Kho Theme Store — chi tiết danh mục Widget | theme_store_widget_category_detail |
| Kho Theme Store — Hình nền nổi bật ("Xem tất cả" của Trending) | theme_store_featured_wallpapers |
| Ô native ads dạng thẻ lồng ghép, dùng chung khi màn không có vị trí riêng | programmatic |
NativePlacement), ui/components/AdMobNativeAd.kt (đối chiếu slot hiển thị)
- Mọi màn "đang tải" (splash, mở mini-app, mở popup Premium…) →
native_loading. - Màn Chọn ngôn ngữ có 2 ô, đổi khi chọn dòng đầu →
native_language_1/native_language_2.
2 Bảng format chi tiết
Bảng tra khi làm việc với đối tác quảng cáo hoặc khi cần đối chiếu doanh thu theo từng loại.
| Loại | Người dùng thấy gì | Show ở đâu | Bật/tắt từ xa |
|---|---|---|---|
|
Full-screen inter ads |
Che kín màn hình, có nút đóng (✕) hoặc tự đóng khi thao tác xong. | Có — công tắc riêng inter_ad_on_off |
|
|
Full-screen AOA |
Che kín màn hình lúc mở app / quay lại app; tự đóng ngay khi xem xong, không có nút đóng riêng. |
|
Có — công tắc riêng open_ad_on_off (+ isShowAOAResume riêng cho lúc quay lại) |
|
In-content native ads |
Một ô nội dung giống phong cách app thật, có gắn nhãn "Ad" nhỏ. |
|
Có — công tắc riêng native_ad_on_off |
|
Full-screen native full ads |
Một native ads chiếm toàn bộ màn hình, show ngay sau khi vừa đóng 1 inter ads; có đếm ngược trước khi cho đóng (hoặc nút "Mở cửa hàng"). | Ngay sau mọi lần show inter ads (nếu bật) — trừ inter ads thay cho reward ads (mục 11) | Tắt mặc định — bật qua native_full_button.onoff |
|
Reward reward ads |
Người dùng chủ động bấm "Xem quảng cáo"; xem xong nhận thưởng ngay, không tự bật lên. |
|
Có — công tắc riêng reward_ad_on_off |
| Đã bỏ banner ads |
Không dùng trong app — không có dải ads cố định ở đầu/cuối màn hình nào cả. | ||
ads/AdUnitIds.kt — khi bật, bản nội bộ luôn
chạy ads mẫu của Google (không phải ads thật), nên đừng báo lỗi "nội dung ads sai" trên bản đó.
Nguồn: ads/AdUnitIds.kt (
adsEnabled() / interEnabled() / rewardEnabled() / appOpenEnabled() — nhánh isDevMode()) · utils/Utils.kt (isDevMode() = debug hoặc releaseDev)
3 Vòng đời từng loại ads — request · load · show · cache
Mục này trả lời đúng 4 câu cho mỗi loại: request khi nào · timeout bao lâu · điều kiện gì mới show · load xong mà không show được thì đi đâu (fallback). Đọc mục này là hiểu vì sao có lúc bấm mà không thấy ads.
- request — app hỏi Google "có ad cho vị trí này không". Mỗi lần request đếm 1 lượt
ad_request. - load — Google trả ad về. Có ad = fill; hết ad = no-fill. Mỗi loại có timeout riêng, quá timeout là app đi tiếp, không đợi nữa.
- show — ad thật sự đập vào mắt người dùng. Load xong chưa chắc show được (xem cột "điều kiện show").
- cache — ad load về mà chưa/không show được thì giữ lại cho lượt sau dùng (fallback). Từ chiều 06/08/2026: KHÔNG loại ads nào còn cache nữa — native ads có dùng cache 50 phút trong một khoảng ngắn buổi sáng cùng ngày, nhưng đã bị gỡ. Giờ mọi loại đều là request → load → show hoặc bỏ, không giữ ad lại cho lượt sau.
Bảng so sánh 5 loại — xem nhanh
| Loại | Request khi nào | Timeout | Điều kiện mới show | Fallback (load xong không show được) |
|---|---|---|---|---|
| inter ads | Ngay lúc cần show (bấm nút đóng mini-app, chuyển màn Cài đặt, mở mini-app…). Không request trước. | 30 giây | Công tắc bật · qua capping 20s · không có full-screen ads nào đang hiện | Bỏ luôn — không có cache, lượt sau request lại từ đầu |
| AOA | Mở app (màn logo) · mở mini-app · quay lại app từ nền | 15 giây (quay lại mini-app: 6 giây) | Công tắc bật · qua capping 25s (màn logo miễn) · không có full-screen ads nào đang hiện | Bỏ luôn — không có cache |
| native ads | Mỗi lần vào màn có ô ads. Cuộn ra/vào, đổi tab: không request lại | Không có timeout cứng — trong lúc chờ ô hiện khung mờ | Chỉ cần công tắc bật (không capping, không tranh khe với full-screen) | Không có cache — load fail thì ẩn ô, không mượn ad nơi khác |
| native full ads | Ngay lúc một inter ads bắt đầu show (load nền trong lúc người dùng đang xem inter) | 15 giây khung mờ (shimmer) sau khi inter đóng | Bật từ xa (mặc định tắt) · inter ads phía trước đã show thật | Không có cache — load fail thì bỏ luôn lượt native full này |
| reward ads | Chỉ khi người dùng bấm nút xem | 45 giây | Công tắc bật · không có capping · không có full-screen ads nào đang hiện | Không có cache — nhưng no-fill thì thay bằng inter ads (mục 11) |
LOAD_TIMEOUT_MS=30s) · ads/SplashAppOpenAdLoader.kt · ads/RewardedAdManager.kt (LOAD_TIMEOUT_MS=45s) · ads/NativeAdLoader.kt (không cache) · ads/NativeFullAdManager.kt (SHIMMER_TIMEOUT_MS=15s, không borrow) · firebase/AppConfig.kt
inter ads — request đúng lúc cần, không cache sẵn
ad_capped).ad_dropped kèm reason (timeout /
lost_race / activity_dead / show_failed_<mã lỗi>) — xem
mục 4 trang Events.AOA — gắn với việc mở app
ad_dropped kèm reason — cùng cơ chế với inter ads ở trên.native ads — direct-load, không còn cache
native full ads — load nền trong lúc người dùng xem inter
native_full_button.onoff.reward ads — chỉ chạy khi người dùng bấm
reward_complete
(mới 06/08) — tách riêng khỏi ad_displayed/reward_show
(bắn lúc ad vừa hiện ra, chưa chắc xem hết). Đóng ads giữa lúc xem → không nhận thưởng, không bắn
reward_complete (đúng luật Google).- Full-screen ads (inter · AOA · native full) — request đúng lúc cần, tranh nhau 1 khe duy nhất, bị capping theo giây, load xong mà trượt thì bỏ luôn — không loại nào trong 3 loại này còn cache để mượn.
- Native ads (kể cả native full) — request mỗi lần vào màn, không tranh khe, không capping, và không có cache đỡ cho lúc load fail — fail là ẩn ô ngay.
- Reward ads — chỉ chạy khi người dùng bấm, không capping, hết ad thì mượn inter ads để vẫn trả được thưởng.
4 Kịch bản từng luồng
Mỗi luồng đọc theo 4 dòng: người dùng làm gì → sẽ thấy gì → quy tắc thời gian → sau đó đi đâu.
A · Mở app (màn logo)
aoa_resume) đè lên Home. Từ 06/08/2026 nhánh đó bị bỏ:
mọi lần mở app từ icon/Recents sau khi tiến trình đã bị hệ điều hành dọn hẳn — kể cả user đã
onboarding xong — đều đi qua đúng màn logo (consent → native → AOA nhãn
aoa_splash, không bị capping) như mục này mô tả; chỉ khác là sau khi xong màn logo, user
đã onboarding thì bỏ qua thẳng Giới thiệu/Chọn ngôn ngữ/Chọn hình nền, vào ngay Màn hình chính. Overlay
"Welcome back" + AOA nhãn aoa_resume (có capping 25 giây) giờ chỉ còn dùng cho lúc
quay lại app khi tiến trình vẫn còn sống — xem mục 4C ngay dưới, đừng nhầm hai luồng này khi test.
Nguồn: bug_pool/misc/BP-0174, BP-0175 · ui/LauncherViewModel.kt (scene init luôn = SPLASH) ·
MainActivity.kt (startSplashFlow / onSplashComplete)
B · Mở mini-app
C · Quay lại app
aoa_resume (không phụ thuộc cấu hình inter/AOA của
lúc mở app). Nếu đang ở màn hình chính: chỉ show khi app chưa được đặt làm launcher mặc định.D · Đóng mini-app bằng nút trên màn
E · Điều hướng trong Cài đặt
F · Xem reward ads để nhận thưởng
Không tự bật. Chỉ show khi người dùng chủ động bấm nút "Xem quảng cáo".
💬 Chat AI — hết lượt
Mỗi ngày được 5 lượt chat miễn phí (số này chỉnh được từ xa). Hết lượt → hộp thoại "Hết lượt chat hôm nay" với 2 nút: Xem quảng cáo (xem xong +1 lượt ngay, cho phép chat tiếp) hoặc Nâng cấp Premium. Không giới hạn số lần xem reward ads trong ngày — hết lượt lại xem tiếp được.
Nguồn: data/chat/ChatCreditStore.kt, ui/chat/ChatAiViewModel.kt (onWatchRewardForCredit)🎨 Theme Store — mở khoá
Áp dụng cho món miễn phí nhưng chưa sở hữu (theme / gói icon / hình nền / widget). Bấm món đó → hộp thoại "Xem quảng cáo để mở khoá" hoặc "Mở khoá tất cả" (Premium). Xem xong → mở khoá vĩnh viễn đúng món vừa bấm, lần sau dùng lại không cần xem nữa. Riêng màn Ví xu có thêm mục xem reward ads để +1 xu, tối đa 3 lần/ngày (xu dùng để mua các món tính phí bằng xu thay vì xem ads).
Nguồn: data/themestore/CoinStore.kt, .code_index/modules/theme_store/coin.md (ThemeStoreViewModel.gateApply)5 Quy tắc capping (giãn cách)
"Capping" = khoảng thời gian tối thiểu giữa 2 lần show ads cùng loại. Mọi con số ở đây chỉnh được từ xa — xem Tần suất & thời gian chờ.
| Loại ads | Capping | Áp dụng cho |
|---|---|---|
| inter ads | 20 giây — đổi từ xa bằng inter_ad_capping_time |
Dùng 1 đồng hồ chung cho MỌI nơi hiện inter ads trong toàn app (mở mini-app, đóng mini-app, Cài đặt, đóng Premium…) — tính từ lúc người dùng thực sự thấy lại màn, không phải lúc bấm đóng. |
| AOA (lúc quay lại app) | 25 giây — đổi từ xa bằng open_ad_capping_time |
Đồng hồ riêng, chỉ áp cho lúc quay lại app. Lúc mở app lần đầu (màn logo) hoặc mở mini-app lần đầu KHÔNG bị capping này — luôn được phép hiện. |
| native ads | Không giới hạn | Mỗi lần vào 1 màn có ô này, ô tự request ad mới — không đợi hết giờ. Cuộn ra/vào, đổi tab thì không request lại (mục 10). Load fail thì ô tự ẩn gọn — không còn cache nào để mượn. |
| reward ads | Không giới hạn theo giây | Xem được nhiều lần liên tiếp. Ngoại lệ: "xem lấy xu" trong Ví xu giới hạn 3 lần/ngày, reset lúc sang ngày mới. Inter ads thay cho reward ads bỏ qua capping 20 giây (mục 11). |
| native full ads (sau inter ads) | Không có đồng hồ riêng | Dùng chung 1 "khe" full-screen với inter ads/AOA — nên không bao giờ chồng lên 2 loại kia (mục 8). Đồng hồ capping của inter ads được đóng dấu lại lúc native full ads đóng, không phải lúc inter ads đóng. |
inter_ad_capping_time = 20, open_ad_capping_time = 25) · ads/InterstitialAdManager.kt · ads/SplashAppOpenAdLoader.kt
- Capping chỉ tính theo giây, không có giới hạn "mỗi ngày N lần" hay "mỗi phiên N lần" cho inter ads/AOA — 2 ngoại lệ theo-ngày duy nhất là Chat AI (5 lượt chat free/ngày) và xem-lấy-xu (3 lần/ngày), cả hai đều không phải full-screen ads.
- Tắt app rồi mở lại: đồng hồ capping KHÔNG reset nếu tiến trình app vẫn còn sống trong bộ nhớ máy (chuyển app nền rồi quay lại). Nếu hệ thống đã giết hẳn tiến trình (mở lại như lần đầu) thì đồng hồ coi như về 0 vì nó chỉ tồn tại trong bộ nhớ, không lưu file.
6 Màn nào có ads gì
Bảng tra nhanh khi test hoặc khi giải thích cho khách. Full-screen Native Reward
| Màn hình | Full-screen (inter / AOA) | Native ads | Reward ads |
|---|---|---|---|
| Màn logo (Splash) | Có — AOA | Có — ô nhỏ lúc chờ | Không |
| Giới thiệu (Intro) | Không | Có | Không |
| Chọn ngôn ngữ | Không | Có — 2 ô, đổi khi chọn dòng đầu | Không |
| Màn hình chính (Home) | Có — chỉ khi chưa đặt làm launcher mặc định | Có — Thư viện ứng dụng, DailyOpen, Spotlight | Không |
| Cài đặt (trang chính + ~12 màn con) | Có — mỗi lần chuyển màn/back/về Home | Có — hầu hết màn con | Không |
| Ẩn ứng dụng (Hidden Apps) | Có (kế thừa Cài đặt) | Có | Không |
| Tối ưu ứng dụng (App Optimize) | Cố tình không (chặn AOA lúc quay lại) | Có | Không |
| Thời tiết | Có — mở + đóng bằng nút | Có | Không |
| Đồng hồ | Có — mở + đóng bằng nút + lưu báo thức | Cố tình không (đề xuất, đã từ chối) | Không |
| Máy tính | Có — mở + đóng bằng nút | Không | Không |
| Thư viện ảnh (Gallery, album, thùng rác) | Có — chỉ lúc mở, đóng bằng nút KHÔNG có ads | Có — danh sách album, chi tiết album, thùng rác | Không |
| Chat AI | Có lúc mở — đóng bằng nút cố tình không có ads | Không | Có — thêm lượt chat |
| Theme Store / Hình nền (Trending, Themes, Wallpaper, Icons, Widgets…) | Có — mở + đóng bằng nút | Có — rất nhiều vị trí | Có — mở khoá món miễn phí |
| Ví xu (My Coin) | Có (kế thừa mở Theme Store) | Cố tình không (đã gỡ bỏ) | Có — xem lấy xu |
| Control Center (thiết lập + tuỳ chỉnh) | Có lúc mở — đóng do Cài đặt tự lo, không lặp | Cố tình không (giống overlay hệ thống, tránh bấm nhầm) | Không |
| Popup mời nâng cấp Premium (mọi nơi) | Có — chỉ lúc đóng ("Tiếp tục với quảng cáo") | Không | Không |
| Xem ảnh/video toàn màn, cắt ảnh trong Thư viện ảnh | Không | Cố tình không (giữa các ảnh) | Không |
7 Khi nào KHÔNG có ads
QA gặp mấy trường hợp này là đúng, không phải lỗi.
- Người dùng Premium — sạch ads ở mọi màn.
- Tắt từ xa bằng công tắc tổng
ads_enabled— tắt là sạch cả app, không ngoại lệ. Xem Công tắc bật / tắt. - Chưa đồng ý popup riêng tư — chưa đồng ý thì chưa show ads nào.
- Bản debug / releaseDev — tắt cứng trong code, xem cảnh báo ở mục 2.
- Nút "Xem quảng cáo" khi công tắc tổng tắt: ẩn/mất tác dụng, vì công tắc tổng tắt là tắt hết — không có ngoại lệ riêng cho reward ads. Reward ads có công tắc bật/tắt riêng tách khỏi 3 loại kia, có thể tắt độc lập từ xa.
- Popup xin đồng ý riêng tư chỉ hiện ở những khu vực luật yêu cầu (Google tự nhận diện, chủ yếu EU/Anh và các nước có luật tương tự) — hiện ngay từ lần mở app đầu tiên, TRƯỚC khi bất kỳ ads nào được yêu cầu. Ở các khu vực không yêu cầu, app bỏ qua popup và chạy ads bình thường.
- Cố tình không có ads ở một số vị trí dù công tắc đang bật: Đồng hồ (không có native ads), Ví xu (không có native ads), Control Center overlay (không có native ads — tránh bấm nhầm/vi phạm chính sách), Thư viện ảnh + Chat AI + Cài đặt + Control Center (đóng bằng nút KHÔNG kèm ads — xem mục 6), trình xem ảnh/video toàn màn (không chèn ads giữa các ảnh).
8 Hai quy tắc bất di bất dịch
Hai điều này luôn đúng. Thấy sai là bug, báo ngay.
9 Checklist test nhanh
Danh sách rút gọn để QA chạy một vòng, đã kèm số giây/số lượt cụ thể lấy từ code. Phải chạy trên bản release — bản debug/releaseDev đang tắt hết ads (mục 2).
- Premium: bật Premium → rà đủ các màn ở mục 6, phải không còn ads nào.
- Công tắc tổng: tắt từ xa → cả app sạch ads.
- Mở app: vào được app kể cả mạng yếu — timeout cứng của màn logo là 20 giây, không treo quá mốc này.
- Mở mini-app: màn "đang tải" giữ tối thiểu 3 giây rồi mới tới ads, chờ tối đa 15 giây, không che ngay lập tức.
- Capping: làm 2 thao tác liền nhau trong vòng 20 giây (inter ads) / 25 giây (AOA lúc quay lại app) → chỉ show 1 lần.
- Không đè nhau: không bao giờ thấy 2 full-screen ads cùng lúc.
- Nút back hệ thống: back bằng cử chỉ / nút máy → không có ads.
- Không kẹt: đóng ads giữa một thao tác → thao tác vẫn hoàn tất bình thường.
- Reward ads: Chat AI xem xong +1 lượt ngay (mặc định 5 lượt free/ngày); Theme Store mở khoá vĩnh viễn đúng món đã bấm, không hỏi lại; Ví xu +1 xu/lần, tối đa 3 lần/ngày.
- Reward ads no-fill: nếu hiện ra inter ads thay vì reward ads thì vẫn phải nhận thưởng (mục 11). Không nhận được = bug.
- Native ads: đang load thấy ô mờ, lỗi mạng thì ô tự ẩn, không để ô trống hay crash.
- Native ads không request lại: cuộn ô ads ra khỏi màn rồi cuộn lại, hoặc đổi tab rồi quay lại trong vòng 3 phút → vẫn đúng ad cũ, không thấy khung mờ load lại (mục 10).
- Popup riêng tư: máy mới cài (ở khu vực yêu cầu) → hiện popup trước, chưa đồng ý thì chưa có ads.
10 Cách app load ads — cập nhật chiều 06/08/2026
Bản cập nhật này không thêm vị trí ads nào và không tăng tần suất show — chỉ đổi cách load để mỗi ad show ra chắc chắn tính được tiền (không còn bind ad đã show/đã hết hạn ở nơi khác). Đọc mục này để không báo sai bug và để hiểu vì sao request tăng nhưng số lần ads đập vào mắt người dùng không tăng (thậm chí no-fill thì ẩn ô hẳn, không còn ad nào để show thay).
❌ Trước đây (tới sáng 06/08)
- Ô native ads preload trước rồi giữ lại dùng sau, hoặc mượn ad từ một kho cache chung.
- Ad về muộn (người dùng đã rời màn) → từng có lúc mất trắng, có lúc được cache lại cho màn sau.
- Load fail thì có lúc mượn ad từ cache để vẫn show được — nhưng ad mượn có thể đã show ở màn khác rồi, Google không tính thêm lượt xem lần mượn lại.
- Cuộn ô ads ra khỏi màn rồi cuộn lại, hoặc đổi tab rồi quay lại → load lại từ đầu, tốn một lượt request mà không thêm lượt show.
✅ Bây giờ (từ chiều 06/08)
- Mỗi lần vào màn, ô request ad mới và gắn ngay khi ad về. Không preload, không cache, không mượn ad của ô khác.
- Load fail / no-fill → ô tự ẩn gọn ngay, không có gì để lấy ra show thay.
- Ad về muộn (ô đã dispose) → destroy luôn, không giữ lại cho màn sau dùng.
- Ô vẫn tự giữ đúng ad của chính nó trong lúc còn ở màn/tab đó — cuộn ra/vào và đổi tab trong thời gian ngắn không request lại (đây KHÔNG phải cache dùng chung, chỉ là ô chưa bị dispose) — xem phần dưới. Ở lại quá 50 phút thì ô tự refresh ad mới (né hạn thật ~1 giờ của native AdMob).
- (06/08) Ad chỉ được gắn vào ô khi ô đó thật sự lọt vào màn hình — xem phần dưới.
2 trường hợp — bảng tra cho QA
Không còn cache nên chỉ còn đúng 2 nhánh: load về có ad hay không.
| # | Load trực tiếp | Người dùng thấy |
|---|---|---|
| 1 | fill | Show ad mới ngay khi về |
| 2 | no-fill / load fail | Ẩn ô — không để khung trống, không show ad nào khác thay |
Vì sao bỏ cache — trade-off chủ nhân đã chọn
| Mục | Giá trị / nghĩa là |
|---|---|
| Lịch sử ngắn | Sáng 06/08/2026 app từng đổi sang 1 cache CHUNG 50 phút cho native ads + native full ads
(ad load trực tiếp ghi đè ad trong cache, load fail thì mượn cache). Chiều cùng ngày, chủ nhân
yêu cầu gỡ hẳn — file giữ cache đó (ads/NativeFallbackPool.kt) đã bị xoá khỏi code.
Đừng cấu hình/tài liệu nào nhắc lại kho cache 50 phút này như đang chạy — nó không còn tồn tại. |
| Đổi lại là gì | Mỗi placement tự sở hữu ad nó load, tự destroy khi rời màn. Không còn khái niệm "kho dự phòng dùng chung" — inter ads, AOA, native ads, native full ads đều không có cache. |
| Cái được | Không ad nào bị bind 2 lần, không có màn nào show lại một ad đã show ở màn khác (mất tiền lượt đó) — mỗi lượt show chắc chắn là một lượt impression mới, tính tiền được. |
| Cái đánh đổi | Số lượt request tăng (mỗi lần vào màn là 1 request mới, không có cách nào tránh) và no-fill thì màn đó show hẳn không có ads (trước còn có thể mượn cache để vẫn có gì hiện ra). Đây là lựa chọn có chủ đích của chủ nhân, không phải bug. |
Không request lại khi cuộn hoặc đổi tab
Đây là phần thay đổi nhiều nhất cho QA — trước đây cứ cuộn/đổi tab là thấy khung mờ load lại.
| Người dùng làm gì | Ads xử lý thế nào |
|---|---|
| Cuộn ô ads ra khỏi màn rồi cuộn lại | Dùng lại đúng ad cũ, không request ad mới, không show khung mờ (shimmer). |
| Đổi tab rồi quay lại (Theme Store 5 tab · Thư viện ảnh Library/Albums · Cài đặt Chính/Tính năng) | Dùng lại đúng ad cũ nếu quay lại trong 3 phút. Rời tab quá 3 phút → app nhả ad ra cho đỡ tốn bộ nhớ, quay lại thì request ad mới. |
| Rời hẳn màn / đóng app con rồi mở lại | Request ad mới. |
| Ở lại một màn rất lâu (> 50 phút) rồi quay lại ô ads | Ad đã quá hạn → request ad mới. |
| Lần load trước bị fail | Chờ 30 giây mới retry, không request dồn dập. |
| Vuốt giữa DailyOpen ⟷ Thư viện ứng dụng ở màn hình chính | Vẫn request lại — trường hợp đã biết, chủ động chưa sửa. Không phải bug mới. |
native_tab_idle_seconds (giây, mặc định 180). Đặt 0 = nhả ad ngay khi
rời tab → tab nào quay lại cũng request ad mới (quay về hành vi cũ, dùng khi cần cứu bộ nhớ). Chi tiết ở
Tần suất & thời gian chờ.
Chỗ bắn ad_displayed của native ads đổi — vẫn cùng thời điểm, đúng hơn khi ad bị dùng lại nhiều nơi (06/08/2026)
Khác thay đổi "vào màn hình" ngay dưới đây (đổi lúc nào gắn ad) — mục này chỉ đổi ai bắn log, không đổi thời điểm hay số lượng thật.
❌ Trước đây
Log bắn từ chỗ Compose gắn ad vào ô hiển thị (setNativeAdTracked trong
AdMobNativeAd.kt) — mỗi lần một NativeAdView nhận ad mới (hoặc đổi ad) là bắn.
Ô nào bị Compose recompose/rebind nhiều lần (đổi cấu hình, tái sử dụng view trong LazyList) có nguy cơ
tính dư.
✅ Bây giờ
Log bắn từ onAdImpression của GMA (AdListener trong
ads/NativeAdLoader.kt) — cùng chỗ bắn af_native_displayed. GMA tự đảm bảo mỗi
NativeAd chỉ bắn đúng 1 lần, bất kể Compose bind/rebind bao nhiêu lần. Chỗ Compose cũ giờ chỉ còn 1 counter dev-log
(NativeAdView BOUND) để debug, không liên quan analytics.
displayedCount + onAdImpression) · ui/components/AdMobNativeAd.kt (setNativeAdTracked, comment tại đầu file)
Chỉ tính "đã show" khi ô ads thật sự lọt vào màn hình (mới 06/08/2026)
Thay đổi này không đụng gì tới số lượt request và không đổi vị trí ads — chỉ đổi thời điểm app gắn ad vào ô và bắn sự kiện đã show.
❌ Trước đây
- Màn hình dựng sẵn cả những ô còn nằm dưới màn (danh sách dài, trang kế bên của Home) để cuộn cho mượt.
- Ô dựng sẵn đó gắn ad luôn và bắn
ad_displayed— dù người dùng chưa nhìn thấy. - Google không tính lượt xem kiểu đó → số
ad_displayedcủa app cao hơn số lượt hiển thị trên AdMob, nhìn như đang mất tiền.
✅ Bây giờ
- Ô chỉ gắn ad khi khung của nó thật sự chạm vào vùng nhìn thấy của màn hình.
- Chưa cuộn tới thì ô giữ nguyên chiều cao (không nhảy layout), chưa bắn sự kiện nào.
- Cuộn tới một lần rồi thì thôi theo dõi — cuộn qua cuộn lại không bắn thêm lần nữa.
ad_displayed/native_showcủa native ads giảm từ 06/08. Phần giảm là những lượt trước đây đếm thừa, Google vốn không tính tiền → doanh thu không giảm theo.ad_request/ad_loadedkhông đổi — app vẫn request y như cũ.- Tỷ lệ
displayed / loadedvì thế tụt xuống, và số của app từ nay sát với AdMob hơn. Muốn so với trước 06/08 thì so doanh thu, đừng soad_displayed.
- Cuộn xuống tới ô ads mới thấy ad hiện ra (trước đó là khung mờ) — đúng, ô chờ vào màn hình rồi mới gắn ad.
- Cuộn ra/vào hoặc đổi tab mà ad không đổi, không có khung mờ — cố tình như vậy để đỡ tốn lượt request (ô chưa bị dispose nên vẫn giữ đúng ad của chính nó, không phải cache dùng chung). Đúng.
- Đổi tab để lâu (>3 phút) rồi quay lại thì thấy ad mới — đúng, app đã nhả ad cũ.
- Ô native ads tự ẩn — xảy ra ngay khi load fail hoặc no-fill, vì không còn cache nào để mượn nữa. Vẫn đúng.
- Bấm "Xem quảng cáo" mà hiện ra inter ads — hết reward ads nên thay thế, vẫn nhận thưởng (mục 11). Đúng.
- Cùng một ad show hai lần liền trên cùng một màn mà không rời màn.
- Hai full-screen ads đè nhau (xem mục 8).
- Inter ads show trong vòng 20 giây kể từ lần trước (trừ inter ads thay reward ads — mục 11).
- Ô native ads để khung trống trắng mãi không ẩn cũng không show gì.
- Cuộn ra/vào một ô ads mà mỗi lần lại thấy khung mờ load lại.
native_isCacheLoading — trước dùng để bật/tắt việc giữ sẵn ad cho màn "đang tải". Cơ chế giữ sẵn
đó đã bị bỏ, nên đổi công tắc này không còn tác dụng gì. Xem
bảng công tắc theo màn.
11 Hết reward ads → thay bằng inter ads
Thay đổi ngày 05/08/2026. Trước đây hết reward ads thì người dùng nhận thông báo "chưa có quảng cáo, thử lại sau" và không nhận được thưởng — mất cả doanh thu lẫn thiện chí. Giờ app thay bằng một inter ads và vẫn trả thưởng.
inter_ad_on_off), hoặc đang có một
full-screen ads khác trên màn, hoặc màn hình vừa bị đóng. Lúc đó về đúng luồng cũ: báo "chưa có quảng cáo",
không trả thưởng.inter_reward_fallback trong các sự kiện ads
(ad_request / ad_loaded / ad_displayed…). Muốn biết "bao nhiêu lượt
reward bị hụt phải thay bằng inter" thì lọc placement = inter_reward_fallback — xem
nhóm sự kiện Ads.
reward_completeonShown) — code hiện
không gọi Analytics.rewardComplete() ở nhánh này (khác nhánh reward ads thật, có gọi ở
OnUserEarnedRewardListener). Nghĩa là reward_complete đang đếm thiếu đúng
số lượt fallback này — muốn đếm tổng "bao nhiêu lượt user thật sự nhận được thưởng" phải cộng thêm
ad_displayed lọc placement = inter_reward_fallback. Cân nhắc báo dev thêm
Analytics.rewardComplete() vào nhánh tryInterFallback (ads/RewardedAdManager.kt)
để số liệu gộp về đúng 1 sự kiện.
allowNativeFull=false, onShown) · ads/AdUnitIds.kt (INTER_REWARD_FALLBACK)