Cold Start là chu trình khởi động hoàn chỉnh của một ứng dụng Android bắt đầu từ trạng thái không, khi tiến trình ứng dụng không tồn tại trong bộ nhớ và Activity chưa được tạo. Hệ thống tạo một tiến trình mới, tải các lớp, khởi tạo Application, tạo Activity và thực hiện vẽ đầu tiên. Theo Google, 2024, khởi động nguội trên thiết bị tầm trung có thể mất từ 1 đến 5 giây và cứ mỗi 100 ms trì hoãn làm giảm 3% khả năng giữ chân người dùng.
Những điểm chính
Cold Start (khởi động nguội) là một kịch bản trong đó ứng dụng Android được khởi động từ trạng thái ban đầu nhất: hệ điều hành tạo một tiến trình mới (fork từ Zygote), cấp phát bộ nhớ, tải mã DEX vào ART, khởi tạo các lớp và tạo một phiên bản Application, sau đó là Activity đầu tiên. Trước khi khởi động ứng dụng, không có dữ liệu về nó trong bộ nhớ thiết bị, ngoại trừ các hình ảnh lớp được lưu trong bộ nhớ đệm nếu Background Dexopt được sử dụng.
Khởi động nguội xảy ra trong ba trường hợp: khi khởi động lần đầu sau khi cài đặt ứng dụng, khi khởi động sau khi khởi động lại thiết bị và khi khởi động sau khi hệ thống đã xóa tiến trình do thiếu bộ nhớ. Trên các thiết bị có 2–4 GB RAM, hệ thống xóa các tiến trình nền khá mạnh mẽ, do đó Cold Start có thể xảy ra mỗi khi người dùng quay lại ứng dụng sau vài giờ không hoạt động. Trên Android 12+, hệ thống có thể giữ một tiến trình bị đóng băng (freeze / cached), nhưng với chế độ tiết kiệm bộ nhớ hoạt động (OOM-killer), tiến trình sẽ bị kết thúc.
Theo Google (báo cáo Find My Device, 2023), 65% người dùng đóng ứng dụng nếu nó không mở trong vòng 3 giây. Đối với mạng xã hội và tin nhắn, nơi người dùng quay lại hàng chục lần mỗi ngày, Cold Start ảnh hưởng trực tiếp đến khả năng giữ chân. Trong Google Play Console, chỉ số Cold Start là một phần của phần Android Vitals và được hiển thị như một trong các chỉ số ANR và hiệu suất. Ứng dụng vượt quá ngưỡng Cold Start “xấu” (hơn 5 giây trên 25% thiết bị) sẽ nhận được cảnh báo trong bảng điều khiển và có thể bị giảm hạng trong kết quả tìm kiếm.
Android phân biệt ba loại khởi động ứng dụng, mỗi loại có thời lượng, tác động đến UX và cách tiếp cận tối ưu khác nhau. Hiểu sự khác biệt là cần thiết để chọn chiến lược phân tích đúng đắn.
| Loại khởi động | Trạng thái tiến trình | Application.onCreate | Thời gian điển hình |
|---|---|---|---|
| Cold | Không có tiến trình | Được thực thi | 1–5 giây |
| Warm | Có tiến trình, không có Activity | Không thực thi | 200–600 ms |
| Hot | Tiến trình + Activity trong bộ nhớ | Không thực thi | < 200 ms |
Warm Start xảy ra khi tiến trình ứng dụng đã tồn tại trong nền, nhưng Activity đã bị hủy (ví dụ: người dùng quay lại sau một thời gian dài và hệ thống đã giải phóng bộ nhớ Activity). Hot Start — khi người dùng thu nhỏ ứng dụng và mở lại ngay lập tức: Activity bị tạm dừng và việc khôi phục mất thời gian tối thiểu. Đối với người dùng, Cold Start là loại khởi động dễ nhận thấy nhất và việc tối ưu nó mang lại cải thiện lớn nhất về UX.
Cold Start có thể trở thành Warm Start sau khi ứng dụng đã được khởi động ít nhất một lần — ART lưu vào bộ nhớ đệm các hình ảnh lớp đã biên dịch (Image trong Boot Profile) và việc tải DEX tiếp theo nhanh hơn. Do đó, lần khởi động thứ hai sau Cold Start đầu tiên thường nhanh hơn 20–40%. Nếu ứng dụng sử dụng Baseline Profiles, các hồ sơ được tải trong lần khởi động đầu tiên và lần khởi động thứ hai có thể nhanh hơn nữa: Google Play, đã phát hành Baseline Profiles, đã tăng tốc Cold Start lên 30% trên các thiết bị có Android 12+.
Cold Start bao gồm các giai đoạn được xác định chặt chẽ, mỗi giai đoạn có thể được đo và tối ưu độc lập. Biết các giai đoạn giúp xác định ứng dụng đang mất thời gian ở giai đoạn nào. Google xác định bốn giai đoạn chính: tạo tiến trình, khởi tạo Application, tạo Activity và khung hình đầu tiên.
Hệ thống Android (ActivityManagerService) tạo một tiến trình mới bằng cách fork từ tiến trình Zygote. Zygote là một tiến trình được tải sẵn với các lớp Android chung. Fork mất 30–80 ms — thời gian này nằm ngoài tầm kiểm soát của ứng dụng. Sau fork, ActivityThread được khởi động — phiên bản vòng lặp chính của ứng dụng. Ở giai đoạn này, tải lớp cũng diễn ra thông qua ClassLoader và ART bắt đầu giải thích bytecode đầu tiên. Nếu ứng dụng sử dụng nhiều bộ khởi tạo tĩnh, giai đoạn này có thể kéo dài.
Ngay sau khi ActivityThread khởi động, Application.onCreate được gọi. Đây là nơi các nhà phát triển thường mắc lỗi nhất, khởi tạo mọi thứ cùng một lúc: Crashlytics, Firebase, máy khách mạng, cơ sở dữ liệu, thành phần Dagger, vùng chứa DI. Mỗi lần khởi tạo như vậy là thời gian bị chặn trên luồng chính. Nếu Application.onCreate mất 500 ms, người dùng sẽ thấy màn hình trắng (hoặc đen) trong nửa giây. Thời lượng tối ưu của giai đoạn này là dưới 200 ms trên thiết bị tầm trung.
Sau khi khởi tạo Application, một phiên bản Activity được tạo (MainActivity hoặc Launcher Activity). Activity.onCreate được gọi, nơi diễn ra setContentView, khởi tạo fragment, thiết lập ViewModel và đăng ký LiveData/Flow. Nếu onCreate tải dữ liệu (SharedPreferences, SQLite, API) đồng bộ trên luồng chính, giai đoạn sẽ kéo dài. Mục tiêu là giữ onCreate trong khoảng 200–400 ms trên thiết bị tầm trung.
Sau khi onCreate hoàn tất, quá trình kết xuất đầu tiên bắt đầu: đo, bố cục, vẽ. Khoảnh khắc này được gọi là TTFD (Time To First Draw). Nếu ứng dụng sử dụng màn hình splash (qua SplashScreen API trên Android 12+ hoặc qua theme), việc kết xuất có thể diễn ra nhanh hơn nhưng người dùng vẫn sẽ đợi cho đến khi splash biến mất. TTFD lý tưởng cho Cold Start là dưới 1,5 giây.
Đo Cold Start yêu cầu các công cụ đặc biệt, vì ghi nhật ký thông thường (Log.d) chỉ bắt đầu hoạt động sau khi tạo Application và thời gian fork cũng như tải lớp vẫn không thể truy cập được. Google khuyến nghị ba phương pháp: lệnh ADB, Android Vitals và macro hiệu suất tùy chỉnh.
Phương pháp đơn giản và có thể tái tạo nhất là lệnh adb shell am start -S -W. Cờ -S buộc dừng ứng dụng trước khi khởi động (đảm bảo Cold Start). Lệnh xuất ra ba chỉ số: ThisTime (thời gian bắt đầu Activity), TotalTime (tổng thời gian bao gồm khởi động tiến trình) và WaitTime (thời gian bao gồm tất cả độ trễ của Activity Manager). Để có phép đo sạch, hãy thực hiện 5–7 lần đo và sử dụng trung vị — các lần đo đơn lẻ chịu ảnh hưởng của nhiễu (CPU throttling, tải nền).
# Cold Start cưỡng bức với đo lường
$ adb shell am start -S -W \
com.example.app/.MainActivity
# Đầu ra lệnh:
# ThisTime: 1842 ms
# TotalTime: 1842 ms
# WaitTime: 1855 ms
Google Play Console thu thập các chỉ số ẩn danh từ tất cả các thiết bị đã cài đặt ứng dụng. Trong phần Android Vitals → Launch time, phân phối trung vị của Cold Start theo mẫu thiết bị và phiên bản Android được hiển thị. Đây là cách duy nhất để thấy các chỉ số thực tế trên thiết bị của người dùng, không chỉ trên thiết bị thử nghiệm. Nếu Cold Start vượt quá 5 giây trên Redmi 9A (2 GB RAM) và 1,2 giây trên Pixel 8, vấn đề là kích thước bộ nhớ và số lượng lớp. Google cũng hiển thị độ trễ người dùng cảm nhận được dựa trên phân vị thứ 25.
Google Jetpack Macrobenchmark (thư viện androidx.benchmark) cho phép viết các bài kiểm tra khởi động ứng dụng có công cụ. Bài kiểm tra cài đặt ứng dụng, khởi động nó từ trạng thái nguội và đo thời gian cho đến khung hình đầu tiên. Macrobenchmark tự động chạy 20 lần, loại bỏ các giá trị ngoại lệ và hiển thị các phân vị ổn định. Đối với CI/CD, bạn có thể so sánh đường cơ sở và lần khởi động hiện tại — nếu thời gian tăng, đường ống CI có thể thất bại.
Tối ưu Cold Start là công việc có hệ thống ảnh hưởng đến nhiều cấp độ của ứng dụng: mã, tài nguyên, cấu hình xây dựng và kiến trúc khởi tạo. Google khuyến nghị bắt đầu với phần đắt nhất — Application.onCreate — và tiến dần đến các chi tiết nhỏ hơn.
Di chuyển tất cả các khởi tạo không cần thiết khi khởi động ra khỏi Application.onCreate đến điểm sử dụng đầu tiên. Firebase, Crashlytics, SDK phân tích, thông báo đẩy, thành phần DI — tất cả có thể được khởi tạo sau khi màn hình đầu tiên được kết xuất. Sử dụng Lazy (by lazy) trong Kotlin hoặc khởi tạo ContentProvider với lệnh gọi initialize(context) rõ ràng. Theo Google (Android Performance, 2023), khởi tạo trễ giảm Cold Start 40–60% cho các ứng dụng sử dụng 5+ SDK.
Baseline Profiles là biên dịch AOT của các lớp và phương thức quan trọng được sử dụng khi khởi động ứng dụng. Nếu không có Baseline Profiles, ART giải thích mã DEX hoặc biên dịch nó qua JIT, mất thời gian. Với hồ sơ, ART biên dịch các phương thức được chỉ định thành mã gốc (AOT) trong khi cài đặt ứng dụng. Google tuyên bố rằng Baseline Profiles tăng tốc Cold Start 15–40% trên Android 9+ và lên đến 60% với các tối ưu ART của Android 12+. Để tạo hồ sơ, hãy sử dụng plugin androidx.benchmark:benchmark-baseline-profile-gradle-plugin.
Thư viện androidx.startup cho phép sắp xếp thứ tự khởi tạo thành phần và thực thi nó trong một ContentProvider duy nhất. Thay vì nhiều ContentProviders từ các thư viện khác nhau (mỗi cái thêm 1–2 ms vào khởi động nguội), App Startup hợp nhất chúng thành một đồ thị phụ thuộc và khởi tạo theo đúng nhu cầu. Khi khởi động, chỉ các thành phần được đánh dấu bằng @Initializer cần thiết cho màn hình đầu tiên mới được thực thi. Đối với các thành phần còn lại, cờ needEarlyInit = false được đặt — chúng khởi động sau lần kết xuất đầu tiên.
// App Startup Initializer — khởi tạo sau khi khởi động
class AnalyticsInitializer : Initializer<Unit> {
override fun create(context: Context) {
Analytics.init(context)
}
override fun dependencies() = listOf<Class<out Initializer<*>>>()
}
// Trong AndroidManifest.xml đánh dấu là tùy chọn
// <meta-data android:name="AnalyticsInitializer"
// android:value="false" />
Kích thước tệp DEX ảnh hưởng trực tiếp đến thời gian tải ART. Sử dụng R8/ProGuard để làm rối và xóa mã chết (MinifyEnabled = true). Bật android:extractNativeLibs="false" trong tệp kê khai để APK không giải nén các tệp .so khi cài đặt. Đối với các dự án có hơn 10 theo dõi tham chiếu, chỉ thêm startup-priority cho màn hình đầu tiên. Mỗi phương thức thừa trong DEX thêm 0,5–2 ms vào thời gian tải và đối với ứng dụng có hơn 50k phương thức (multidex với primary dex) — lên đến 300 ms.
Android Vitals trong Google Play Console (phần Launch time) thu thập dữ liệu từ tất cả các thiết bị đã cài đặt ứng dụng, với điều kiện người dùng đã đồng ý chẩn đoán ẩn danh. Các chỉ số được chia thành ba loại: “tốt”, “trung bình”, “xấu”, tùy theo thời gian Cold Start.
Google định nghĩa Cold Start “xấu” là thời gian vượt quá 5 giây trên bất kỳ thiết bị nào. Tuy nhiên, trên thực tế, đối với thiết bị cao cấp (Snapdragon 8 Gen), thời gian tốt là dưới 1,5 giây, đối với tầm trung — dưới 2,5 giây, đối với bình dân — dưới 4 giây. Android Vitals hiển thị trung vị cho mỗi mẫu thiết bị, cho phép hiểu thiết bị nào ứng dụng khởi động chậm. Nếu Cold Start xấu trên các thiết bị Samsung A-series hoặc Xiaomi Redmi, nguyên nhân thường là bộ nhớ flash chậm và RAM thấp (tăng tốc qua Baseline Profiles mang lại hiệu quả lớn nhất trên các thiết bị như vậy).
Ngoài việc hiển thị trong bảng điều khiển, chỉ số Cold Start ảnh hưởng đến đánh giá chất lượng ứng dụng trong Google Play Search. Các ứng dụng có tỷ lệ khởi động “xấu” cao sẽ nhận được nhãn “Cảnh báo hiệu suất” trên trang cài đặt, làm giảm tỷ lệ chuyển đổi. Theo Google (Android Performance Playbook, 2024), các ứng dụng đã giải quyết vấn đề Cold Start đã tăng tỷ lệ chuyển đổi cài đặt trung bình 5% và cải thiện khả năng giữ chân (D1) từ 3–7%.
Để giám sát chi tiết hơn, hãy sử dụng Firebase Performance Monitoring. Nó theo dõi Cold Start ở cấp độ phiên, phân chia theo phiên bản ứng dụng và phiên bản Android. Không giống như Android Vitals, Firebase hiển thị sơ đồ dấu vết thời gian đã dành theo giai đoạn. Ví dụ: bạn có thể thấy rằng trong phiên bản 3.2.0, Application.onCreate mất 800 ms (do thư viện thông báo đẩy mới), trong khi ở phiên bản 3.2.1 — 200 ms (sau khi sửa lỗi).
Dưới đây là hai ví dụ thực tế giúp tăng tốc Cold Start trực tiếp: di chuyển khởi tạo SDK sau khi khởi động và sử dụng SplashScreen API.
Một lỗi điển hình là khởi tạo tất cả SDK trong Application.onCreate. Dưới đây cho thấy cách di chuyển khởi tạo không quan trọng sang một coroutine được khởi chạy sau khi vẽ khung hình đầu tiên. Quan trọng: Firebase, Crashlytics và SDK báo cáo sự cố phải được khởi tạo khi khởi động — không thể trì hoãn vì chúng bắt lỗi trong khi khởi tạo các thành phần khác. Phần còn lại, hãy sử dụng lifecycleScope trong Activity đầu tiên.
// ❌ Xấu — tất cả khởi tạo trong Application.onCreate
class App : Application() {
override fun onCreate() {
super.onCreate()
Firebase.init(this) // quan trọng
Analytics.init(this) // có thể sau
Database.init(this) // có thể sau
ImageLoader.init(this) // có thể sau
}
}
// ✅ Tốt — Firebase khi khởi động, còn lại after inflate
class App : Application() {
override fun onCreate() {
super.onCreate()
Firebase.init(this)
}
}
// Trong MainActivity sau khung hình đầu tiên:
lifecycleScope.launchWhenResumed {
initializeNonCriticalSdks()
}
Trên Android 12+, hãy sử dụng SplashScreen API chính thức hiển thị splash hệ thống (biểu tượng ứng dụng trên nền tối/sáng) ngay khi tiến trình khởi động. Điều này che giấu thời gian khởi tạo khỏi người dùng — họ thấy splash thay vì màn hình trắng. Đối với thiết bị cũ, hãy sử dụng theme-based splash (Theme.SplashScreen trong kiểu). Quan trọng: splash không nên kéo dài quá 300 ms — nếu ứng dụng chưa sẵn sàng vào lúc đó, hãy vẽ một bộ xương “cố định” (shimmer) và hiển thị tiến trình tải.
// SplashScreen API — Android 12+
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
val splashScreen = installSplashScreen()
splashScreen.setKeepOnScreenCondition {
isReady.value == false
}
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
// Splash dựa trên chủ đề (Android 5-11)
// Trong themes.xml:
// <style name="Theme.App.Starting" parent="Theme.SplashScreen">
// <item name="windowSplashScreenBackground">@color/white</item>
// <item name="windowSplashScreenAnimatedIcon">@mipmap/ic_launcher</item>
// </style>
Câu hỏi thường gặp
Giả lập sử dụng máy tính chủ mạnh mẽ và mô phỏng bộ xử lý với tăng tốc phần cứng (HAXM / WHPX). Thiết bị thật, đặc biệt là thiết bị bình dân (bộ nhớ eMMC thay vì UFS), có I/O chậm hơn nhiều. Nên đo Cold Start trên thiết bị thật tầm trung để có dữ liệu thực tế.
Theo khuyến nghị của Google, Cold Start trung vị phải dưới 2 giây trên thiết bị tầm trung. Đối với thiết bị cao cấp — dưới 1,5 giây. Đối với thiết bị bình dân (2 GB RAM) có thể chấp nhận tối đa 4 giây, nhưng khuyến nghị tối ưu xuống 3 giây. Giá trị trên 5 giây được coi là nghiêm trọng.
Gián tiếp — có. Nếu tệp kê khai chứa biểu tượng vector (AdaptiveIcon), nó phải được biên dịch thành drawable khi khởi động. Nếu biểu tượng chứa các đường dẫn phức tạp (pathData với hàng chục đường cong), quá trình biên dịch mất 10–30 ms. Sử dụng VectorDrawable với pathData đã tối ưu (qua SVGOMG hoặc Android Studio Vector Asset).
Có, nếu Feature Module (Android App Bundle) được tải theo yêu cầu, Cold Start của nó được đo từ thời điểm nhấn vào tính năng cho đến khung hình đầu tiên. Các mô-đun theo yêu cầu được tải qua Play Core Library và quá trình cài đặt chúng thêm 500–3000 ms vào thời gian khởi động. Tối ưu mã tính năng giống như mô-đun chính.
Các ứng dụng có hơn 64k phương thức yêu cầu Multidex. Điều này có nghĩa là ART phải tải nhiều tệp DEX, làm tăng thời gian Cold Start thêm 200–800 ms tùy thuộc vào số lượng tệp classes.dex. Sử dụng minSdk 21+ (ART hỗ trợ multidex gốc) và cấu hình primary dex qua --main-dex-list để giữ các lớp quan trọng trong tệp DEX đầu tiên.
Tổng kết
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm