onStart——是Android生命周期的方法,当Activity或Fragment对用户可见时被调用。此时屏幕出现在设备显示屏上,但还不能与用户交互——输入焦点在调用onResume之前不存在。onStart方法非常适合注册系统监听器、连接地理定位服务以及启动在组件可见于屏幕时应运行的动画。关于Activity完整生命周期的更多信息,请阅读文章Activity Lifecycle。
要点
onStart——Activity生命周期的第二个方法,由系统在onCreate之后(或在从停止状态返回时的onRestart之后)调用。在调用onStart的时刻,Activity或Fragment在屏幕上变为可见。用户看到界面,但屏幕尚未准备好交互——输入焦点只有在onResume之后才会出现。
onStart方法属于Activity的“可见生命周期”(visible lifetime)——onStart和onStop之间的间隔。在此期间,Activity可能会被其他窗口部分遮挡(例如透明的Activity或对话框),但其UI保持可见。这使可见生命周期与“前台生命周期”(onResume — onPause)区分开来,后者中Activity具有完整的输入焦点。
理解这种三层层次结构对于正确分配代码至关重要。onCreate——一次性初始化,onStart——连接可见资源,onResume——独占资源的垄断访问。混淆这些级别的开发人员可能会在屏幕间切换时造成内存泄漏或应用程序行为不正确。
在Activity中,onStart方法每次屏幕出现在显示屏上时被调用——无论是首次启动(onCreate之后)还是从后台模式返回(onRestart之后)。与onCreate不同,onStart在Activity实例的生命周期内可能被多次调用,因此这里放置每次屏幕出现时应执行的代码。
class DashboardActivity : AppCompatActivity() {
private val connectivityReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
val isConnected = ... // 检查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()
}
}
关键规则:所有在onStart中连接的资源必须在onStop中释放。这确保了当Activity从屏幕隐藏时,它不会消耗电池、监听系统事件或占用内存。Android Studio包含lint规则,警告在没有相应注销的情况下注册BroadcastReceiver。
Fragment中的onStart与容器Activity的生命周期紧密相关。Fragment在包含它的Activity收到onStart之后才收到onStart调用。但是,如果Fragment是以延迟模式添加的(FragmentTransaction.commit()不带addToBackStack),onStart可能会被延迟调用。
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()
}
}
Fragment.onStart的特殊性:如果Fragment位于offscreenPageLimit = 1的ViewPager中,相邻的片段也将在变为可见之前收到onStart。这可能导致监听器的过早注册。对于这种情况,使用setUserVisibleHint()方法或在onStart内部检查isVisible,以便仅对实际可见的片段注册监听器。
onStart和onResume之间的主要区别——屏幕活动级别。onStart表示Activity在屏幕上可见,但不一定在前台。onResume表示Activity在前台并具有输入焦点。差异通过对话框示例展示:当Dialog出现在Activity上方时,Activity失去onResume(调用onPause),但保持可见——onStart/onStop不被调用。
差异表清楚地显示了每种方法在哪些场景下被调用:
| 场景 | onStart | onResume |
|---|---|---|
| 启动应用程序 | 调用 | 调用 |
| Activity上方打开Dialog | 不调用 | onPause(失去焦点) |
| 按下“主页”按钮 | onStop(隐藏) | onPause → onStop |
| 从“最近”返回 | onStart(可见) | onResume(焦点) |
| 屏幕旋转 | onCreate → onStart | → onResume |
| 来电 | onStop(隐藏) | onPause → onStop |
该表帮助开发人员决定将特定代码放在哪个方法中。例如,如果应用程序应在任何屏幕遮挡时(甚至对话框)暂停视频播放,代码放在onPause中。如果视频只应在屏幕完全隐藏时停止——代码放在onStop中。
onStart——注册仅在Activity在屏幕上可见时应该工作的监听器的最佳位置。这涉及三种主要类型的系统组件:用于系统事件的BroadcastReceiver、用于地理定位的LocationListener和用于设备传感器的SensorListener。
BroadcastReceiver通过Context.registerReceiver()在onStart中动态注册并在onStop中通过unregisterReceiver()注销。动态注册优于静态注册(在清单中),因为它将接收器的生命周期限制为Activity的可见期——当Activity隐藏时,应用程序不会因系统广播消息而唤醒。
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()
}
地理定位和传感器——资源密集型操作。在onStart中请求GPS更新并在onStop中取消,确保屏幕隐藏时应用程序不会消耗电池。为了精确调整,使用带有最小间隔和距离的requestLocationUpdates——例如10秒和10米,在精度和能耗之间提供最佳平衡。
在onStart中启动动画,而不是在onCreate中,确保每次屏幕出现时动画都会启动。如果在onCreate中启动动画,它只在首次创建Activity时工作,但不会在从后台模式返回时工作。onStart每次Activity变为可见时被调用,这使其成为启动循环动画和过渡的理想位置。
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()
}
对于使用ObjectAnimator或ValueAnimator的动画,在onStop中调用cancel()很重要。如果动画在Activity隐藏后继续运行,会无谓地消耗GPU和CPU资源,降低设备性能并加速电池放电。Android Studio Profiler(GPU图表)允许跟踪活动动画并检测泄漏。
onStart/onStop配对规则也适用于预览相机(CameraX)的使用。在onStart中打开相机并在onStop中关闭,确保当您的应用程序在屏幕上不可见时,相机不会被其他应用程序锁定。违反此规则是Google Play中负面评价的常见原因之一。
常见问题
onStart——用于应在屏幕可见时工作的监听器(BroadcastReceiver、LocationListener、SensorListener)。onResume——用于需要独占访问的资源(相机、视频录制、语音识别)。系统事件监听器不需要独占访问,可以在部分遮挡时工作——它们在onStart中注册。相机应该只在完全聚焦时激活——在onResume中打开。
如果Activity进入可见状态,onStart总是被调用。唯一没有onStart的场景——Activity被创建并立即终止(例如由于onCreate中的错误)。在这种情况下,onCreate之后直接是onDestroy。但这是一个紧急场景,不应该出现在正确编写的代码中。
是的,如果另一个Activity或透明窗口立即在Activity上方打开,onStart可能不会收到onResume。例如,如果onCreate之后启动了身份验证屏幕(Activity A → Activity B),在Activity A中onStart被调用,但onResume没有——在被屏幕B遮挡时,它直接收到onPause → onStop。
onStart可以在Activity实例的生命周期内被多次调用。每次Activity从隐藏状态(onStop)过渡到可见状态时,都会调用onStart。实际上,在积极使用应用程序时,onStart在会话中可能被调用数十次或数百次。
在onStart中加载数据如果数据应在每次屏幕出现时更新,是合理的。例如,新闻推送或通知列表。但加载应该是异步的——通过带有lifecycleScope的协程,以避免阻塞UI线程。对于在屏幕出现之间不变化的数据,只需在onCreate中加载一次即可。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。