onStart là một phương thức vòng đời Android được gọi khi Activity hoặc Fragment trở nên hiển thị với người dùng. Tại thời điểm này, màn hình xuất hiện trên thiết bị, nhưng chưa thể tương tác với người dùng — tiêu điểm đầu vào không có cho đến khi onResume được gọi. Phương thức onStart rất lý tưởng để đăng ký bộ lắng nghe hệ thống, kết nối với dịch vụ định vị và khởi chạy hoạt ảnh cần chạy khi thành phần hiển thị trên màn hình. Tìm hiểu thêm về vòng đời Activity đầy đủ trong bài viết Activity Lifecycle.
Những điểm chính
onStart là phương thức thứ hai của vòng đời Activity, được hệ thống gọi sau onCreate (hoặc sau onRestart khi quay lại từ trạng thái dừng). Tại thời điểm onStart được gọi, Activity hoặc Fragment trở nên hiển thị trên màn hình. Người dùng nhìn thấy giao diện, nhưng màn hình chưa sẵn sàng cho tương tác — tiêu điểm đầu vào sẽ chỉ xuất hiện sau onResume.
Phương thức onStart thuộc về “thời gian sống hiển thị” (visible lifetime) của Activity — khoảng thời gian giữa onStart và onStop. Trong giai đoạn này, Activity có thể bị che một phần bởi các cửa sổ khác (ví dụ: Activity trong suốt hoặc cửa sổ hội thoại), nhưng UI của nó vẫn hiển thị. Điều này phân biệt thời gian sống hiển thị với “thời gian sống tiền cảnh” (onResume — onPause), khi Activity có tiêu điểm đầu vào đầy đủ.
Hiểu được hệ thống phân cấp ba cấp này rất quan trọng để phân phối mã đúng cách. onCreate — khởi tạo một lần, onStart — kết nối tài nguyên hiển thị, onResume — truy cập độc quyền vào tài nguyên độc quyền. Nhà phát triển nhầm lẫn các cấp này có nguy cơ tạo ra rò rỉ bộ nhớ hoặc hành vi ứng dụng không chính xác khi chuyển đổi giữa các màn hình.
Trong Activity, phương thức onStart được gọi mỗi khi màn hình xuất hiện trên thiết bị — cả khi khởi chạy lần đầu (sau onCreate) và khi quay lại từ nền (sau onRestart). Không giống như onCreate, onStart có thể được gọi nhiều lần trong suốt vòng đời của một phiên bản Activity, vì vậy mã cần thực thi mỗi khi màn hình xuất hiện được đặt ở đây.
class DashboardActivity : AppCompatActivity() {
private val connectivityReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
val isConnected = ... // Kiểm tra ConnectivityManager
binding?.statusIndicator?.setColor(
if (isConnected) Color.GREEN else Color.RED
)
}
}
override fun onStart() {
super.onStart()
registerReceiver(
connectivityReceiver,
IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)
)
SensorManager.getInstance().registerStepCounter()
}
override fun onStop() {
unregisterReceiver(connectivityReceiver)
SensorManager.getInstance().unregisterStepCounter()
super.onStop()
}
}
Quy tắc chính: tất cả tài nguyên kết nối trong onStart phải được giải phóng trong onStop. Điều này đảm bảo rằng khi Activity bị ẩn khỏi màn hình, nó không tiêu thụ pin, không lắng nghe sự kiện hệ thống và không chiếm dụng bộ nhớ. Android Studio bao gồm các quy tắc lint cảnh báo về việc đăng ký BroadcastReceiver mà không hủy đăng ký tương ứng.
onStart trong Fragment gắn chặt với vòng đời của Activity chứa nó. Fragment nhận được lệnh gọi onStart sau khi Activity chứa nó đã nhận được onStart. Tuy nhiên, nếu Fragment được thêm ở chế độ trì hoãn (FragmentTransaction.commit() không có addToBackStack), onStart có thể được gọi với độ trễ.
class MapFragment : Fragment() {
private var mapView: MapView? = null
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
mapView = MapView(requireContext())
return mapView!!
}
override fun onStart() {
super.onStart()
mapView?.onStart()
LocationService.connect(requireContext())
}
override fun onStop() {
mapView?.onStop()
LocationService.disconnect()
super.onStop()
}
}
Đặc thù của Fragment.onStart: nếu Fragment nằm trong ViewPager với offscreenPageLimit = 1, các fragment lân cận cũng sẽ nhận được onStart trước khi hiển thị. Điều này có thể dẫn đến việc đăng ký bộ lắng nghe sớm. Trong những trường hợp này, hãy sử dụng phương thức setUserVisibleHint() hoặc kiểm tra isVisible bên trong onStart để chỉ đăng ký bộ lắng nghe cho các fragment thực sự hiển thị.
Sự khác biệt chính giữa onStart và onResume là mức hoạt động của màn hình. onStart báo hiệu rằng Activity hiển thị trên màn hình nhưng không nhất thiết ở tiền cảnh. onResume báo hiệu rằng Activity ở tiền cảnh và có tiêu điểm đầu vào. Sự khác biệt được thể hiện qua ví dụ cửa sổ hội thoại: khi Dialog xuất hiện trên Activity, Activity mất onResume (onPause được gọi) nhưng vẫn hiển thị — onStart/onStop không được gọi.
Bảng so sánh hiển thị rõ ràng trong những kịch bản nào mỗi phương thức được gọi:
| Kịch bản | onStart | onResume |
|---|---|---|
| Khởi chạy ứng dụng | Được gọi | Được gọi |
| Dialog mở trên Activity | Không được gọi | onPause (mất tiêu điểm) |
| Nhấn nút Home | onStop (ẩn) | onPause → onStop |
| Quay lại từ Gần đây | onStart (hiển thị) | onResume (tiêu điểm) |
| Xoay màn hình | onCreate → onStart | → onResume |
| Cuộc gọi đến | onStop (ẩn) | onPause → onStop |
Bảng này giúp nhà phát triển quyết định nên đặt mã cụ thể vào phương thức nào. Ví dụ: nếu ứng dụng cần tạm dừng phát video khi có bất kỳ sự chồng chéo màn hình nào (kể cả hội thoại), mã được đặt trong onPause. Nếu video chỉ nên dừng khi màn hình bị ẩn hoàn toàn — mã được đặt trong onStop.
onStart là nơi tối ưu để đăng ký bộ lắng nghe chỉ nên hoạt động khi Activity hiển thị trên màn hình. Điều này liên quan đến ba loại thành phần hệ thống chính: BroadcastReceiver cho sự kiện hệ thống, LocationListener cho định vị và SensorListener cho cảm biến thiết bị.
BroadcastReceiver được đăng ký động qua Context.registerReceiver() trong onStart và hủy đăng ký trong onStop qua unregisterReceiver(). Đăng ký động được ưu tiên hơn đăng ký tĩnh (trong tệp kê khai) vì nó giới hạn thời gian sống của bộ thu trong khoảng thời gian hiển thị của Activity — ứng dụng không thức dậy từ các thông báo broadcast hệ thống khi Activity bị ẩn.
private val batteryReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent) {
val level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1)
binding?.batteryText?.text = "$level%"
}
}
override fun onStart() {
super.onStart()
registerReceiver(batteryReceiver, IntentFilter(Intent.ACTION_BATTERY_CHANGED))
}
override fun onStop() {
unregisterReceiver(batteryReceiver)
super.onStop()
}
Định vị và cảm biến là các hoạt động tốn nhiều tài nguyên. Yêu cầu cập nhật GPS trong onStart và hủy trong onStop đảm bảo rằng ứng dụng không tiêu hao pin khi màn hình bị ẩn. Để điều chỉnh chính xác, hãy sử dụng requestLocationUpdates với khoảng thời gian và khoảng cách tối thiểu — ví dụ: 10 giây và 10 mét, mang lại sự cân bằng tối ưu giữa độ chính xác và mức tiêu thụ năng lượng.
Khởi chạy hoạt ảnh trong onStart, thay vì trong onCreate, đảm bảo rằng hoạt ảnh bắt đầu mỗi khi màn hình xuất hiện. Nếu bạn khởi chạy hoạt ảnh trong onCreate, nó sẽ chỉ hoạt động khi tạo Activity lần đầu, không phải khi quay lại từ nền. onStart được gọi mỗi khi Activity hiển thị, khiến nó trở thành nơi lý tưởng để khởi chạy hoạt ảnh tuần hoàn và chuyển tiếp.
private lateinit var pulseAnimator: ValueAnimator
override fun onStart() {
super.onStart()
pulseAnimator.start()
binding?.loadingIndicator?.animate()?.alpha(1f)?.start()
}
override fun onStop() {
pulseAnimator.cancel()
binding?.loadingIndicator?.animate()?.cancel()
super.onStop()
}
Đối với hoạt ảnh sử dụng ObjectAnimator hoặc ValueAnimator, điều quan trọng là gọi cancel() trong onStop. Nếu hoạt ảnh tiếp tục chạy sau khi Activity bị ẩn, nó tiêu thụ tài nguyên GPU và CPU một cách vô ích, làm giảm hiệu suất thiết bị và tăng tốc độ xả pin. Android Studio Profiler (đồ thị GPU) cho phép bạn theo dõi hoạt ảnh đang hoạt động và phát hiện rò rỉ.
Quy tắc cặp onStart/onStop cũng áp dụng khi làm việc với máy ảnh để xem trước (CameraX). Mở máy ảnh trong onStart và đóng trong onStop đảm bảo rằng máy ảnh không bị chặn cho các ứng dụng khác khi ứng dụng của bạn không hiển thị trên màn hình. Vi phạm quy tắc này là một nguyên nhân phổ biến của các đánh giá tiêu cực trên Google Play.
Các câu hỏi thường gặp
onStart — dành cho bộ lắng nghe cần hoạt động khi màn hình hiển thị (BroadcastReceiver, LocationListener, SensorListener). onResume — dành cho tài nguyên yêu cầu truy cập độc quyền (máy ảnh, chụp video, nhận dạng giọng nói). Bộ lắng nghe sự kiện hệ thống không yêu cầu truy cập độc quyền và có thể hoạt động với sự chồng chéo một phần — chúng được đăng ký trong onStart. Máy ảnh chỉ nên hoạt động khi có tiêu điểm đầy đủ — nó được mở trong onResume.
onStart luôn được gọi nếu Activity chuyển sang trạng thái hiển thị. Kịch bản duy nhất không có onStart — Activity được tạo và kết thúc ngay lập tức (ví dụ: do lỗi trong onCreate). Trong trường hợp này, onDestroy được gọi ngay sau onCreate. Nhưng đây là kịch bản khẩn cấp không nên xảy ra trong mã được viết đúng cách.
Có, onStart có thể không nhận được onResume nếu một Activity khác hoặc cửa sổ trong suốt mở ngay lập tức trên Activity. Ví dụ: nếu màn hình ủy quyền được khởi chạy sau onCreate (Activity A → Activity B), trong Activity A, onStart được gọi, nhưng onResume thì không — nó ngay lập tức nhận onPause → onStop khi bị che bởi màn hình B.
onStart có thể được gọi nhiều lần trong suốt vòng đời của một phiên bản Activity. Mỗi khi Activity chuyển từ trạng thái ẩn (onStop) sang trạng thái hiển thị, onStart được gọi. Trong thực tế, khi sử dụng ứng dụng tích cực, onStart có thể được gọi hàng chục hoặc hàng trăm lần mỗi phiên.
Tải dữ liệu trong onStart là hợp lý nếu dữ liệu cần được cập nhật mỗi khi màn hình xuất hiện. Ví dụ: nguồn cấp tin tức hoặc danh sách thông báo. Tuy nhiên, việc tải phải không đồng bộ — qua coroutine với lifecycleScope, để không chặn luồng UI. Đối với dữ liệu không thay đổi giữa các lần xuất hiện màn hình, tải một lần trong onCreate là đủ.
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