
本文详解 UninitializedPropertyAccessException 的根本原因与修复方法,重点解决在异步网络请求中过早访问 lateinit 变量导致的崩溃问题,并提供安全初始化、空检查及生命周期适配的最佳实践。
本文详解 `uninitializedpropertyaccessexception` 的根本原因与修复方法,重点解决在异步网络请求中过早访问 `lateinit` 变量导致的崩溃问题,并提供安全初始化、空检查及生命周期适配的最佳实践。
在 Kotlin Android 开发中,lateinit 是一个常用但需谨慎使用的特性——它允许声明非空属性而延迟初始化,但绝不意味着“可随意访问”。你遇到的 kotlin.UninitializedPropertyAccessException: lateinit property polyline has not been initialized 异常,本质是时序错误(race condition):drawRouteOnMap() 在 polyline 被赋值前就被调用了。
从你的代码逻辑可清晰看出执行顺序:
-
onCreate()中调用routesDetails()发起异步网络请求; -
onMapReady()几乎立即被 Google Maps SDK 触发(通常快于网络响应); -
onMapReady()调用drawRouteOnMap(),此时polyline仍为未初始化状态 → 崩溃。
✅ 正确修复:确保初始化完成后再使用
最直接有效的修复,是将 drawRouteOnMap() 的调用移至网络响应成功且 polyline 已赋值之后:
private fun routesDetails(origin: String, destination: String, apiKey: String) {
val routeService = ServiceBuilder.buildService(RouteService::class.java)
val requestCall = routeService.getRoutes(origin, destination, apiKey)
requestCall.enqueue(object : Callback<routesresponse> {
override fun onResponse(
call: Call<routesresponse>,
response: Response<routesresponse>
) {
if (response.isSuccessful && response.body()?.routes?.isNotEmpty() == true) {
// ✅ 安全赋值
polyline = response.body()!!.routes[0].overview_polyline.points.toString()
// ✅ 确保此时 polyline 已初始化,再绘制路线
drawRouteOnMap()
} else {
// 处理无路线数据的情况(如提示用户)
Toast.makeText(this@CustomerMapActivity, "No route found", Toast.LENGTH_SHORT).show()
}
}
override fun onFailure(call: Call<routesresponse>, t: Throwable) {
// ✅ 建议添加日志和用户反馈
Log.e("RouteAPI", "Request failed", t)
Toast.makeText(this@CustomerMapActivity, "Network error", Toast.LENGTH_SHORT).show()
}
})
}</routesresponse></routesresponse></routesresponse></routesresponse>
⚠️ 注意:
onMapReady()中的drawRouteOnMap()必须移除,否则会再次触发未初始化访问。当前逻辑下,地图就绪与路线数据就绪是两个独立事件,不应耦合调用。
? 进阶防护:避免依赖 lateinit 的脆弱性
虽然上述修改可解决问题,但过度依赖 lateinit 会增加维护风险。更健壮的替代方案是使用可空类型 + 安全校验:
private var polyline: String? = null // 改为可空类型
private fun drawRouteOnMap() {
polyline?.let { points ->
val decodedPolyline = PolyUtil.decode(points)
// 继续绘制逻辑...
} ?: run {
// 明确处理未就绪状态(如显示加载中提示)
Log.w("Map", "Polyline not available yet")
}
}
或使用 isInitialized 检查(仅适用于 lateinit):
private fun drawRouteOnMap() {
if (::polyline.isInitialized) {
val decodedPolyline = PolyUtil.decode(polyline)
// ...绘制
} else {
Log.w("Map", "Polyline not initialized")
}
}
? 关键注意事项总结
-
lateinit不是异步安全的:它不解决并发或时序问题,仅免除空安全检查。 -
永远不要在异步回调外假设
lateinit已初始化:尤其避免在onCreate/onMapReady等生命周期方法中直接使用。 -
网络请求是异步的:
enqueue()后续代码会立即执行,响应处理在后台线程回调,二者无执行顺序保证。 -
考虑业务语义:若路线数据是地图展示的前提,建议在
onMapReady中先显示占位状态(如加载动画),待数据返回后再绘制,提升用户体验。 -
及时清理资源:若 Activity 在请求返回前已销毁(如用户快速退出),需在
onDestroy()中取消请求,防止内存泄漏(可借助lifecycleScope或Job管理协程)。
通过将业务逻辑与初始化时机严格对齐,并辅以空安全设计,即可彻底规避此类崩溃,构建更稳定可靠的 Android 应用。










