You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在LocationServiceUser中初始化lateinit变量mMap(GoogleMap)?

问题:lateinit property mMap has not been initialized

报错场景

在Service中尝试初始化lateinit var mMap: GoogleMap时触发该错误,Service负责获取定位数据并更新地图,但目前无法完成mMap的初始化。

相关代码

LocationServiceUser(Service类)

class LocationServiceUser : Service(), OnMapReadyCallback {
    private val serviceScopeUser = CoroutineScope(SupervisorJob() + Dispatchers.IO)
    private lateinit var locationClient: LocationClientUser
    private val db = FirebaseFirestore.getInstance()
    private lateinit var mMap: GoogleMap
    private val markerOptions = MarkerOptions()

    override fun onBind(p0: Intent?): IBinder? {
        return null
    }

    override fun onCreate() {
        super.onCreate()
        locationClient = DefaultLocationClientUser(
            applicationContext,
            LocationServices.getFusedLocationProviderClient(applicationContext)
        )
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        when (intent?.action) {
            ACTION_READING_DATA -> readingData()
        }
        return super.onStartCommand(intent, flags, startId)
    }

    override fun onMapReady(googleMap: GoogleMap) {
        mMap = googleMap
    }

    fun readingData() {
        locationClient.getLocation(5000L)
            .catch { e -> e.printStackTrace() }
            .onEach { location ->
                var lat = location.latitude
                var lng = location.longitude
                var currentLatLng = LatLng(lat, lng)
                db.collection("").document("")
                    .addSnapshotListener { documentUser, error ->
                        if (documentUser != null) {
                            lat = documentUser.getGeoPoint("localizacao")?.latitude!!
                            lng = documentUser.getGeoPoint("localizacao")?.longitude!!
                            Log.d(
                                "Response",
                                "Dado lido a cada 5s:  lat: $lat, lng: $lng}"
                            )
                            currentLatLng = LatLng(lat, lng)
                        }
                        if (::mMap.isInitialized) {
                            mMap.addMarker(
                                MarkerOptions().position(currentLatLng)
                                    .title("Minha Localizacao atual")
                            )
                            Log.d("Response", "Minha localizacao de agora: $currentLatLng")
                        }else{
                            Log.d("Response", "readingData: Erro ao inicializar o mMap")
                        }
                        markerOptions.position(currentLatLng)
                        mMap.addMarker(markerOptions)
                        mMap.moveCamera(CameraUpdateFactory.newLatLng(currentLatLng))
                    }
            }.launchIn(serviceScopeUser)
    }

    override fun onDestroy() {
        super.onDestroy()
        serviceScopeUser.cancel()
    }

    companion object {
        const val ACTION_READING_DATA = "ACTION_READING_DATA"
    }
}

UserFragment(使用Service的Fragment)

class UserFragment: Fragment() {
    private var _binding: FragmentUserBinding? = null
    private val binding: FragmentUserBinding get() = _binding!!
    private lateinit var locationClientUser: LocationClientUser

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        _binding = FragmentUserBinding.inflate(inflater,container,false)

        return binding.root
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        activity?.startService(Intent(context,LocationServices::class.java))
        locationClientUser = DefaultLocationClientUser(
            requireContext().applicationContext,
            LocationServices.getFusedLocationProviderClient(requireContext().applicationContext)
        )
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        binding.btnReadingData.setOnClickListener {
            Intent(
                requireContext().applicationContext,
                LocationServiceUser::class.java
            ).apply {
                action = LocationServiceUser.ACTION_READING_DATA
                activity?.startService(this)
            }
        }
    }
}

疑问

已知GoogleMap通常在Fragment中初始化,但该Service持有需用于更新地图的定位数据,请问是否有方法在Service中完成mMap的初始化?


解决方案

首先明确:Service中无法直接初始化GoogleMap。原因是GoogleMap的初始化依赖于MapView或MapFragment这类UI组件,而Service是运行在后台的无UI组件,没有关联的View层级和UI生命周期,OnMapReadyCallback永远不会被触发,自然无法完成mMap的初始化。

正确的做法是拆分职责:

  1. Service负责数据获取:只处理定位请求、Firebase数据监听,把获取到的经纬度数据传递给UI层(Fragment)。
  2. Fragment负责地图操作:在Fragment中初始化GoogleMap,接收Service传递的数据,更新地图标记和相机位置。

1. 修改Service,通过Flow传递数据

将Service改为可绑定类型,用MutableSharedFlow发送定位数据:

class LocationServiceUser : Service() {
    private val serviceScopeUser = CoroutineScope(SupervisorJob() + Dispatchers.IO)
    private lateinit var locationClient: LocationClientUser
    private val db = FirebaseFirestore.getInstance()
    // 用SharedFlow发送定位数据,支持多订阅者
    private val locationFlow = MutableSharedFlow<LatLng>(replay = 1)

    override fun onBind(intent: Intent): IBinder {
        return LocalBinder()
    }

    inner class LocalBinder : Binder() {
        fun getService(): LocationServiceUser = this@LocationServiceUser
    }

    override fun onCreate() {
        super.onCreate()
        locationClient = DefaultLocationClientUser(
            applicationContext,
            LocationServices.getFusedLocationProviderClient(applicationContext)
        )
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        when (intent?.action) {
            ACTION_READING_DATA -> readingData()
        }
        return START_STICKY
    }

    fun readingData() {
        locationClient.getLocation(5000L)
            .catch { e -> e.printStackTrace() }
            .onEach { location ->
                var lat = location.latitude
                var lng = location.longitude
                var currentLatLng = LatLng(lat, lng)
                db.collection("").document("")
                    .addSnapshotListener { documentUser, error ->
                        if (documentUser != null) {
                            lat = documentUser.getGeoPoint("localizacao")?.latitude!!
                            lng = documentUser.getGeoPoint("localizacao")?.longitude!!
                            currentLatLng = LatLng(lat, lng)
                            Log.d("Response", "Dado lido a cada 5s:  lat: $lat, lng: $lng}")
                        }
                        // 发送数据到Flow
                        serviceScopeUser.launch {
                            locationFlow.emit(currentLatLng)
                        }
                    }
            }.launchIn(serviceScopeUser)
    }

    // 对外暴露Flow,供Fragment订阅
    fun getLocationFlow(): SharedFlow<LatLng> = locationFlow

    override fun onDestroy() {
        super.onDestroy()
        serviceScopeUser.cancel()
    }

    companion object {
        const val ACTION_READING_DATA = "ACTION_READING_DATA"
    }
}

2. 修改Fragment,初始化地图并监听数据

在Fragment中初始化MapView,绑定Service并订阅数据更新地图:

class UserFragment: Fragment(), OnMapReadyCallback {
    private var _binding: FragmentUserBinding? = null
    private val binding: FragmentUserBinding get() = _binding!!
    private lateinit var mMap: GoogleMap
    private var locationService: LocationServiceUser? = null
    private var serviceBound = false

    private val serviceConnection = object : ServiceConnection {
        override fun onServiceConnected(className: ComponentName, service: IBinder) {
            val binder = service as LocationServiceUser.LocalBinder
            locationService = binder.getService()
            serviceBound = true
            // 订阅定位数据,在UI线程更新地图
            locationService?.getLocationFlow()?.onEach { latLng ->
                requireActivity().runOnUiThread {
                    updateMap(latLng)
                }
            }?.launchIn(viewLifecycleOwner.lifecycleScope)
        }

        override fun onServiceDisconnected(arg0: ComponentName) {
            serviceBound = false
            locationService = null
        }
    }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        _binding = FragmentUserBinding.inflate(inflater,container,false)
        // 初始化布局中的MapView
        binding.mapView.onCreate(savedInstanceState)
        binding.mapView.getMapAsync(this)
        return binding.root
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // 绑定Service
        Intent(requireContext(), LocationServiceUser::class.java).also { intent ->
            requireActivity().bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)
        }
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        binding.btnReadingData.setOnClickListener {
            Intent(requireContext(), LocationServiceUser::class.java).apply {
                action = LocationServiceUser.ACTION_READING_DATA
                requireActivity().startService(this)
            }
        }
    }

    override fun onMapReady(googleMap: GoogleMap) {
        mMap = googleMap
        // 基础地图设置
        mMap.uiSettings.isZoomControlsEnabled = true
    }

    private fun updateMap(latLng: LatLng) {
        if (::mMap.isInitialized) {
            // 清除旧标记(可选)
            mMap.clear()
            // 添加新标记
            mMap.addMarker(MarkerOptions().position(latLng).title("Minha Localizacao atual"))
            // 移动相机到目标位置
            mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(latLng, 15f))
            Log.d("Response", "Minha localizacao de agora: $latLng")
        }
    }

    // 管理MapView的生命周期
    override fun onStart() {
        super.onStart()
        binding.mapView.onStart()
    }

    override fun onResume() {
        super.onResume()
        binding.mapView.onResume()
    }

    override fun onPause() {
        super.onPause()
        binding.mapView.onPause()
    }

    override fun onStop() {
        super.onStop()
        binding.mapView.onStop()
    }

    override fun onDestroyView() {
        super.onDestroyView()
        binding.mapView.onDestroy()
        _binding = null
    }

    override fun onDestroy() {
        super.onDestroy()
        if (serviceBound) {
            requireActivity().unbindService(serviceConnection)
            serviceBound = false
        }
    }
}

关键说明

  • 拆分职责后,Service专注于后台数据逻辑,Fragment负责UI渲染,符合Android组件设计规范。
  • 使用ServiceConnection绑定Service,Fragment可以安全获取Service实例并订阅数据;也可以用LiveData替代SharedFlow,更贴合Android生命周期管理。
  • 必须在Fragment中管理MapView的完整生命周期,确保onMapReady正常触发,完成mMap的初始化。

内容的提问来源于stack exchange,提问作者Matheus Finamor

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 22:25:25