Android获取GPS当前位置时lateinit未初始化导致应用崩溃如何解决
问题根源
onMapReady方法和权限申请、位置获取的逻辑是异步执行的,大概率会在用户完成权限授权、拿到定位结果前就执行完成,此时lateinit修饰的currentLocation还未初始化,直接访问就会触发崩溃- 权限申请回调的判断逻辑存在错误,原逻辑
grantResults.isEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED永远无法成立,空数组访问下标0会直接触发越界崩溃
解决方案
步骤1:修改定位变量定义,避免未初始化访问
将lateinit修饰的currentLocation改为可空类型,从根源避免未初始化访问异常:
// 替换原 private lateinit var currentLocation: Location private var currentLocation: Location? = null
步骤2:抽离添加当前位置标记的逻辑
单独封装添加当前位置标记的方法,只有当定位结果和地图实例都准备完成时再调用:
private fun addCurrentLocationMarker() { // 双重校验,地图和定位都就绪才执行逻辑 val map = googleMap ?: return val location = currentLocation ?: return val latLng = com.google.android.gms.maps.model.LatLng(location.latitude, location.longitude) val markerOptions = MarkerOptions().position(latLng).title("Current Location") map.animateCamera(CameraUpdateFactory.newLatLngZoom(latLng, 16.0f)) map.addMarker(markerOptions) }
步骤3:修改onMapReady逻辑,移除直接访问currentLocation的代码
onMapReady中只保留添加预置标记的逻辑,不要直接操作定位相关内容:
override fun onMapReady(map: GoogleMap) { googleMap = map for(i in locationArrayList!!.indices){ googleMap.addMarker(MarkerOptions().position(locationArrayList!![i]).title("Marker $i")) googleMap.animateCamera(CameraUpdateFactory.zoomTo(18.0f)) googleMap.moveCamera(CameraUpdateFactory.newLatLng(locationArrayList!![i])) } // 尝试添加当前位置标记,如果此时定位已经拿到就会直接添加,没拿到就什么都不做 addCurrentLocationMarker() }
步骤4:修改定位成功回调逻辑,拿到定位后主动触发添加标记
在fetchLocation的定位成功回调中,调用封装好的添加标记方法:
private fun fetchLocation(){ if (ActivityCompat.checkSelfPermission(requireContext(), android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(requireContext(), android.Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(requireActivity(), arrayOf(android.Manifest.permission.ACCESS_FINE_LOCATION), permissionCode) return } val task = fusedLocationProvider.lastLocation task.addOnSuccessListener { location -> if(location != null){ currentLocation = location Toast.makeText(this.context, currentLocation.latitude.toString() + " " + currentLocation.longitude.toString(), Toast.LENGTH_SHORT).show() // 拿到定位后调用添加标记方法 addCurrentLocationMarker() } } }
步骤5:修复权限申请回调的逻辑错误
修正权限判断条件,避免越界崩溃:
override fun onRequestPermissionsResult( requestCode: Int, permissions: Array<out String>, grantResults: IntArray ) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) when(requestCode){ permissionCode -> if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED){ fetchLocation() } else { // 可以在这里添加权限拒绝的提示逻辑 Toast.makeText(requireContext(), "需要定位权限才能显示当前位置", Toast.LENGTH_SHORT).show() } } }
完整修改后代码
class FirstFragment : Fragment(), OnMapReadyCallback{ private var _binding: FragmentFirstBinding? = null private val binding get() = _binding!! // 修改为可空类型 private var currentLocation: Location? = null private lateinit var fusedLocationProvider: FusedLocationProviderClient private val permissionCode = 101 private var googleMap: GoogleMap? = null private val MasjidZahir = com.google.android.gms.maps.model.LatLng(6.1046784,100.3716608) private val Jabil = com.google.android.gms.maps.model.LatLng(5.3267065,100.2820637) private val TARUCKL = com.google.android.gms.maps.model.LatLng(3.2162302,101.7267724) private val TARUCPG = com.google.android.gms.maps.model.LatLng(5.4532105,100.2826858) private val MARINA = com.google.android.gms.maps.model.LatLng(1.3149938,103.890498) private var locationArrayList: ArrayList<com.google.android.gms.maps.model.LatLng>? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) fusedLocationProvider = LocationServices.getFusedLocationProviderClient(requireActivity()) fetchLocation() } override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) val map = binding.mapView mapView.onCreate(savedInstanceState) mapView.onResume() mapView.getMapAsync(this) locationArrayList = ArrayList() locationArrayList!!.add(MasjidZahir) locationArrayList!!.add(Jabil) locationArrayList!!.add(TARUCKL) locationArrayList!!.add(TARUCPG) locationArrayList!!.add(MARINA) } private fun fetchLocation(){ if (ActivityCompat.checkSelfPermission(requireContext(), android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(requireContext(), android.Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(requireActivity(), arrayOf(android.Manifest.permission.ACCESS_FINE_LOCATION), permissionCode) return } val task = fusedLocationProvider.lastLocation task.addOnSuccessListener { location -> if(location != null){ currentLocation = location Toast.makeText(this.context, "${currentLocation.latitude} ${currentLocation.longitude}", Toast.LENGTH_SHORT).show() addCurrentLocationMarker() } } } private fun addCurrentLocationMarker() { val map = googleMap ?: return val location = currentLocation ?: return val latLng = com.google.android.gms.maps.model.LatLng(location.latitude, location.longitude) val markerOptions = MarkerOptions().position(latLng).title("Current Location") map.animateCamera(CameraUpdateFactory.newLatLngZoom(latLng, 16.0f)) map.addMarker(markerOptions) } override fun onMapReady(map: GoogleMap) { googleMap = map for(i in locationArrayList!!.indices){ googleMap.addMarker(MarkerOptions().position(locationArrayList!![i]).title("Marker $i")) googleMap.animateCamera(CameraUpdateFactory.zoomTo(18.0f)) googleMap.moveCamera(CameraUpdateFactory.newLatLng(locationArrayList!![i])) } addCurrentLocationMarker() } override fun onRequestPermissionsResult( requestCode: Int, permissions: Array<out String>, grantResults: IntArray ) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) when(requestCode){ permissionCode -> if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED){ fetchLocation() } else { Toast.makeText(requireContext(), "需要定位权限才能显示当前位置", Toast.LENGTH_SHORT).show() } } } // 补充销毁时清理binding避免内存泄漏 override fun onDestroyView() { super.onDestroyView() _binding = null } }
内容的提问来源于stack exchange,提问作者YIN NEO
相关产品推荐
相关产品推荐

