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

如何每隔一分钟周期性调用协程挂起函数实现天气API请求

问题:Android天气APP实现每分钟调用API更新数据

我正在开发一款天气APP,需要每隔一分钟调用一次API获取数据。API调用函数是挂起函数,目前在MainActivity的协程中执行,请问怎么实现每隔60秒调用这些协程方法发起请求?

现有代码片段

MainActivity中的调用逻辑

val lat = location.latitude
val lon = location.longitude

val vmFactory = weatherVMFactory(application,appcontext,netwReq,lat.toString(),lon.toString())
val viewModel = ViewModelProvider(this,vmFactory).get(weatherVM::class.java)

GlobalScope.launch(Dispatchers.Main) {
    // 需每分钟调用以下方法
    val wInfo = async { viewModel.otherData() }
    setOtherData(wInfo.await())

    val dInfo = async { viewModel.dailyData() }
    setDailyData(dInfo.await())

    val hInfo = async { viewModel.hourlyData() }
    setHourlyData(hInfo.await())
}

ViewModel类

package com.example.weatherapp

import android.app.Application
import android.content.Context
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.*

class weatherVM(application: Application, appcon: Context, jQueue: Singleton, latitude: String, longitude: String) : AndroidViewModel(application) {

    private val wRepo = WeatherRepo(appcon,jQueue,latitude,longitude)

    suspend fun otherData(): MutableLiveData<OtherInfo> {
        return wRepo.getOtherData()
    }

    suspend fun dailyData(): MutableLiveData<ArrayList<DailyInfo>> {
        return wRepo.getDailyData()
    }

    suspend fun hourlyData(): MutableLiveData<ArrayList<HourlyInfo>>{
        return wRepo.getHourlyData()
    }
}

Repository中的API调用示例

suspend fun getDailyData(): MutableLiveData<ArrayList<DailyInfo>> = withContext(Dispatchers.IO){
        val tinfoArr = ArrayList<DailyInfo>()
        val jsonO = JsonObjectRequest(Request.Method.GET,url,null,{

            val dailyArr = it.getJSONArray("daily")

            for (i in 1 until dailyArr.length()) {

                val dailyObj = dailyArr.getJSONObject(i)
                val daily = DailyInfo(dailyObj.getLong("dt"),dailyObj.getInt("humidity"),dailyObj.getJSONObject("temp").getInt("day"),dailyObj.getJSONObject("feels_like").getInt("day"))
                tinfoArr.add(daily)
            }
            dInfo.value = tinfoArr

        },{
            Toast.makeText(appCon,"Error fetching Daily Forecast",Toast.LENGTH_LONG).show()
        })

        jQueue.addToRequestQueue(jsonO)

        return@withContext dInfo
    }

解决方案与优化建议

一、用ViewModelScope实现可控的定时任务

不要使用GlobalScope(生命周期不受控,易引发内存泄漏),改用ViewModel自带的viewModelScope,页面销毁时会自动取消协程。

在ViewModel中添加定时刷新逻辑

class weatherVM(...) : AndroidViewModel(application) {
    // 原有代码...
    private var refreshJob: Job? = null

    fun startPeriodicRefresh() {
        refreshJob = viewModelScope.launch(Dispatchers.Main) {
            while (isActive) { // 协程活跃时持续执行
                // 并行发起三个API请求
                val otherDeferred = async { otherData() }
                val dailyDeferred = async { dailyData() }
                val hourlyDeferred = async { hourlyData() }

                // 等待所有请求完成后更新UI
                setOtherData(otherDeferred.await().value)
                setDailyData(dailyDeferred.await().value)
                setHourlyData(hourlyDeferred.await().value)

                delay(60000) // 间隔60秒
            }
        }
    }

    // 手动停止刷新的方法
    fun stopPeriodicRefresh() {
        refreshJob?.cancel()
    }

    override fun onCleared() {
        super.onCleared()
        stopPeriodicRefresh() // ViewModel销毁时自动停止
    }

    // 注:建议将setXXXData逻辑移至ViewModel或通过LiveData观察更新UI,更符合MVVM规范
}

在MainActivity中启动定时任务

val viewModel = ViewModelProvider(this, vmFactory).get(weatherVM::class.java)
viewModel.startPeriodicRefresh()

二、修复Repository中挂起函数的逻辑问题

当前Repository的挂起函数存在缺陷:withContext(Dispatchers.IO)仅切换线程,但JsonObjectRequest是异步回调,挂起函数会直接返回空的LiveData,需用suspendCoroutine将回调转为挂起风格:

优化后的Repository方法

suspend fun getDailyData(): ArrayList<DailyInfo>? = suspendCoroutine { continuation ->
    val jsonO = JsonObjectRequest(Request.Method.GET, url, null, { response ->
        val tinfoArr = ArrayList<DailyInfo>()
        val dailyArr = response.getJSONArray("daily")
        for (i in 1 until dailyArr.length()) {
            val dailyObj = dailyArr.getJSONObject(i)
            val daily = DailyInfo(
                dailyObj.getLong("dt"),
                dailyObj.getInt("humidity"),
                dailyObj.getJSONObject("temp").getInt("day"),
                dailyObj.getJSONObject("feels_like").getInt("day")
            )
            tinfoArr.add(daily)
        }
        continuation.resume(tinfoArr)
    }, { error ->
        Toast.makeText(appCon, "Error fetching Daily Forecast", Toast.LENGTH_LONG).show()
        continuation.resume(null)
    })
    jQueue.addToRequestQueue(jsonO)
}

对应ViewModel的调整

private val _dailyData = MutableLiveData<ArrayList<DailyInfo>>()
val dailyData: LiveData<ArrayList<DailyInfo>> = _dailyData

suspend fun fetchDailyData() {
    val data = wRepo.getDailyData()
    data?.let { _dailyData.postValue(it) }
}

Activity中通过LiveData观察更新UI

// MainActivity初始化后
viewModel.dailyData.observe(this) { dailyList ->
    setDailyData(dailyList)
}
// 其他数据接口同理

三、额外注意事项

  • 避免请求堆积:添加isRefreshing标志位,每次循环前检查是否有未完成的请求,防止重复发起。
  • 后台适配:APP退至后台时,建议降低刷新频率或改用WorkManager实现周期性任务,减少电量消耗。
  • 异常处理:给API请求添加超时、重试逻辑,避免单次请求失败导致后续定时任务中断。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 11:03:25