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

Android架构组件:ViewModel未随位置数据更新问题排查

解决LocationViewModel无法更新UI的问题

嘿,我来帮你排查这个问题!你的代码里有几个关键疏漏导致TextView始终没更新,咱们一步步理清楚:

你踩的几个坑

  1. 缺少位置权限处理
    FusedLocationProvider需要ACCESS_FINE_LOCATION或ACCESS_COARSE_LOCATION权限才能正常工作。没有权限的话,系统根本不会触发位置回调,currentLocation自然不会有任何更新。

  2. LocationCallback被局部变量持有,易被GC回收
    你在init块里创建的locationCallback是局部变量,当init执行完后,这个回调很可能被垃圾回收,后续的位置更新就无法被接收了。

  3. 位置设置检查成功的逻辑缺失
    你只在checkLocationSettings的onFailureListener里处理了null值,但完全漏掉了设置满足条件时的逻辑——这时候才应该发起位置更新请求啊!

  4. ViewModel获取方式过时
    ViewModelProviders已经被废弃了,现在应该用ViewModelProvider来获取ViewModel实例。


正确的实现方式

第一步:添加权限到AndroidManifest.xml

先在清单文件里声明必要的位置权限:

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!-- 如果你的应用需要后台定位(Android 10+),再加上这个 -->
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />

第二步:修复LocationViewModel

把locationCallback改成成员变量,补全位置设置成功的逻辑,同时在ViewModel销毁时移除位置更新避免内存泄漏:

class LocationViewModel(application: Application) : AndroidViewModel(application) {
    val currentLocation = MutableLiveData<Location?>()
    private lateinit var fusedLocationProvider: FusedLocationProviderClient
    private lateinit var locationCallback: LocationCallback

    init {
        val ctx = getApplication<Application>().applicationContext
        fusedLocationProvider = LocationServices.getFusedLocationProviderClient(ctx)
        val locationRequest = LocationRequest.create().apply {
            priority = LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY
            interval = 5000
            fastestInterval = 2000
        }

        val settingsBuilder = LocationSettingsRequest.Builder().addLocationRequest(locationRequest)
        LocationServices.getSettingsClient(ctx)
            .checkLocationSettings(settingsBuilder.build())
            .addOnSuccessListener {
                // 位置设置符合要求,开始请求位置更新
                startLocationUpdates(locationRequest)
            }
            .addOnFailureListener { exception ->
                when ((exception as ApiException).statusCode) {
                    LocationSettingsStatusCodes.RESOLUTION_REQUIRED -> {
                        // 位置设置未开启,这里可以通过LiveData通知Activity引导用户开启
                        currentLocation.postValue(null)
                    }
                    else -> currentLocation.postValue(null)
                }
            }
    }

    private fun startLocationUpdates(locationRequest: LocationRequest) {
        locationCallback = object : LocationCallback() {
            override fun onLocationResult(locationResult: LocationResult?) {
                locationResult ?: return
                currentLocation.postValue(locationResult.lastLocation)
            }
        }

        // 检查是否有位置权限
        val hasPermission = ContextCompat.checkSelfPermission(
            getApplication(),
            Manifest.permission.ACCESS_FINE_LOCATION
        ) == PackageManager.PERMISSION_GRANTED ||
                ContextCompat.checkSelfPermission(
                    getApplication(),
                    Manifest.permission.ACCESS_COARSE_LOCATION
                ) == PackageManager.PERMISSION_GRANTED

        if (hasPermission) {
            fusedLocationProvider.requestLocationUpdates(
                locationRequest,
                locationCallback,
                Looper.getMainLooper()
            )
        } else {
            currentLocation.postValue(null)
        }
    }

    override fun onCleared() {
        super.onCleared()
        // 页面销毁时移除位置更新,防止内存泄漏
        fusedLocationProvider.removeLocationUpdates(locationCallback)
    }
}

第三步:修复MainActivity

处理权限请求,用最新的ViewModelProvider获取ViewModel:

class MainActivity : AppCompatActivity() {
    private lateinit var locationText: TextView
    private val LOCATION_PERMISSION_REQUEST_CODE = 1001

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        locationText = findViewById(R.id.locationText)

        // 用新的ViewModelProvider获取实例
        val locationViewModel = ViewModelProvider(this)[LocationViewModel::class.java]
        locationViewModel.currentLocation.observe(this) { resultLocation ->
            locationText.text = resultLocation?.let {
                "Lat: ${it.latitude} Long: ${it.longitude}"
            } ?: "无法获取位置,请检查权限和位置设置"
        }

        // 检查并请求位置权限
        checkLocationPermission()
    }

    private fun checkLocationPermission() {
        val hasFinePerm = ContextCompat.checkSelfPermission(
            this,
            Manifest.permission.ACCESS_FINE_LOCATION
        ) == PackageManager.PERMISSION_GRANTED
        val hasCoarsePerm = ContextCompat.checkSelfPermission(
            this,
            Manifest.permission.ACCESS_COARSE_LOCATION
        ) == PackageManager.PERMISSION_GRANTED

        if (!hasFinePerm && !hasCoarsePerm) {
            ActivityCompat.requestPermissions(
                this,
                arrayOf(
                    Manifest.permission.ACCESS_FINE_LOCATION,
                    Manifest.permission.ACCESS_COARSE_LOCATION
                ),
                LOCATION_PERMISSION_REQUEST_CODE
            )
        }
    }

    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        if (requestCode == LOCATION_PERMISSION_REQUEST_CODE) {
            // 权限授予后,可以重新触发位置请求(比如给ViewModel加个refresh方法)
            val locationViewModel = ViewModelProvider(this)[LocationViewModel::class.java]
            // 这里可以调用ViewModel的刷新方法,或者因为ViewModel在init里已经尝试过,不过最好主动触发一次
        }
    }
}

关键修复总结

  • 必须声明并请求位置权限,没有权限一切都是空谈
  • 把LocationCallback改为ViewModel成员变量,避免被GC回收
  • 位置设置检查成功后才发起位置更新请求
  • 使用最新的ViewModelProvider替代过时的ViewModelProviders
  • 在onCleared中移除位置更新,防止内存泄漏
  • 处理位置设置未开启的场景,引导用户开启

内容的提问来源于stack exchange,提问作者Amol Borkar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 09:00:22