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

MVVM架构天气应用UI未更新:ViewModel获取位置后界面无变化

MVVM天气应用:迁移定位逻辑到ViewModel后UI不更新的解决方案

问题概述

开发采用MVVM架构的天气应用时,将位置获取逻辑从MainActivity迁移到WeatherViewModel后,界面无法更新天气数据。此前直接在Activity中实现定位逻辑时,UI能正常展示结果。

代码问题分析

1. ViewModel中fetchLocation()返回值逻辑错误

ViewModel的fetchLocation()方法无论权限是否已授予,最终都返回true,导致MainActivity中执行权限申请后直接return,跳过了weatherResp的观察者注册,后续数据更新无法通知UI。

2. MainActivity缺失权限申请回调

申请定位权限后,未处理权限授予结果,导致用户同意权限后,不会重新触发定位获取流程,无法获取位置并请求天气数据。

3. 冗余的FusedLocationProviderClient实例

MainActivity中自行初始化了fusedLocationProviderClient,但ViewModel已通过Hilt注入该实例,Activity中的此实例属于冗余代码,可移除。

修复后的代码

WeatherViewModel.kt 修复

@HiltViewModel
class WeatherViewModel @Inject constructor(
    private val repo: WeatherRepository,
    private val application: Application,
    private val fusedLocationProviderClient: FusedLocationProviderClient
) : ViewModel() {
    private val _resp = MutableLiveData<WeatherDTO>()
    val weatherResp: LiveData<WeatherDTO> get() = _resp

    private val _cord = MutableLiveData<Coord>()
    val cord: LiveData<Coord> get() = _cord

    fun getWeather(latitude: Double, longitude: Double) = viewModelScope.launch {
        repo.getWeather(latitude, longitude).let { response ->
            if (response.isSuccessful) {
                _resp.postValue(response.body())
            } else {
                Log.d("Weather Error", "getWeather Error Response: ${response.message()}")
            }
        }
    }

    // 修正返回值逻辑:仅当权限未授予时返回true
    fun fetchLocation(): Boolean {
        val task = fusedLocationProviderClient.lastLocation
        if (ActivityCompat.checkSelfPermission(
                application,
                android.Manifest.permission.ACCESS_FINE_LOCATION
            ) != PackageManager.PERMISSION_GRANTED &&
            ActivityCompat.checkSelfPermission(
                application,
                android.Manifest.permission.ACCESS_COARSE_LOCATION
            ) != PackageManager.PERMISSION_GRANTED
        ) {
            return true
        }
        task.addOnSuccessListener {
            it?.let { location ->
                getWeather(location.latitude, location.longitude)
            }
        }
        // 权限已授予时返回false
        return false
    }

    private fun getAddressName(lat: Double, long: Double): String {
        Log.d("lat", lat.toString())
        var addressName = " "
        val geoCoder = Geocoder(application, Locale.getDefault())
        val address = geoCoder.getFromLocation(lat, long, 1)
        if (address != null) {
            addressName = address[0].subAdminArea
        }
        Log.d("Address", address.toString())
        Log.d("subadmin", addressName.toString())
        return addressName
    }
}

MainActivity.kt 修复

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding
    private lateinit var weatherModel: WeatherViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        weatherModel = ViewModelProvider(this)[WeatherViewModel::class.java]

        // 先注册观察者,确保数据更新能被UI接收
        weatherModel.weatherResp.observe(this) { weather ->
            binding.apply {
                tvDescription.text = weather.weather[0].description
                livelocation.setOnClickListener {
                    Toast.makeText(this@MainActivity, "this is rohit", Toast.LENGTH_SHORT).show()
                }
                tvWind.text = "${weather.wind.speed} Km/h"
                humidity.text = weather.main.humidity.toString()
                val tempInC = weather.main.temp - 273.15
                val number3digits = (tempInC * 1000.0).roundToInt() / 1000.0
                tvTemperature.text = number3digits.toString()
                visibility.text = weather.visibility.toString()
            }
        }

        // 处理定位逻辑
        if (weatherModel.fetchLocation()) {
            ActivityCompat.requestPermissions(
                this,
                arrayOf(android.Manifest.permission.ACCESS_FINE_LOCATION),
                101
            )
        }
    }

    // 添加权限申请结果回调
    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        if (requestCode == 101) {
            if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // 权限授予后,重新触发定位获取
                weatherModel.fetchLocation()
            } else {
                Toast.makeText(this, "定位权限未授予,无法获取天气数据", Toast.LENGTH_SHORT).show()
            }
        }
    }
}

关键修复说明

  1. 调整fetchLocation()返回值:仅当权限未授予时返回true,确保权限已授予时MainActivity能正常注册观察者。
  2. 添加权限回调:在onRequestPermissionsResult中处理权限授予结果,重新触发定位流程。
  3. 调整观察者注册顺序:先注册weatherResp的观察者,再执行定位逻辑,避免数据更新时UI未监听。
  4. 移除冗余代码:删除MainActivity中自行初始化的fusedLocationProviderClient,统一使用ViewModel中注入的实例。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 21:40:27