Android MVVM应用实现10秒间隔API轮询(学习用途)
问题描述
我正在开发一款用于学习的简易Android MVVM应用,需要每10秒从REST API获取数据,即使应用被杀死也需正常执行。了解到WorkManager默认最小重复间隔为15分钟,无法满足需求。以下是我的简化代码结构:
现有代码结构
ApiInterface
interface ApiInterface { @GET("search?country=India") suspend fun getUniversities() : Response<University> }
ApiUtilities
object ApiUtilities { val BASE_URL = "http://universities.hipolabs.com/" fun getInstance() : Retrofit { return Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .build() } }
Repository
class UniversityRepository(private val apiInterface: ApiInterface) { private val universityLiveData = MutableLiveData<University>() val university : LiveData<University> get() = universityLiveData suspend fun getUniversities() { val result = apiInterface.getUniversities() if(result.body() != null) { universityLiveData.postValue(result.body()) } } }
ViewModel
class UniversityViewModel(private val universityRepository: UniversityRepository): ViewModel() { init { viewModelScope.launch(Dispatchers.IO) { universityRepository.getUniversities() } } val university: LiveData<University> get() = universityRepository.university }
解决方案与指导
首先明确:Android系统对后台任务有严格限制,尤其是Android 8.0(API 26)及以上版本,应用被杀后要实现10秒级轮询几乎没有可靠的官方方案——系统会主动限制这类高频后台操作以节省电量。以下分场景给出可行方案:
1. 应用在前台/后台(未被杀)时的10秒轮询
这种场景可以用协程结合ViewModel实现,简单直接且无内存泄漏风险:
修改ViewModel实现轮询逻辑
class UniversityViewModel(private val universityRepository: UniversityRepository): ViewModel() { val university: LiveData<University> get() = universityRepository.university init { startPolling() } private fun startPolling() { viewModelScope.launch(Dispatchers.IO) { while (isActive) { universityRepository.getUniversities() delay(10000) // 10秒间隔 } } } }
- 优点:依托
viewModelScope,ViewModel销毁时协程自动取消,不会造成内存泄漏;代码实现简单 - 局限:应用退到后台后,系统可能在短时间内回收进程导致轮询中断;应用被杀后完全停止
2. 尝试实现应用被杀后的高频轮询(不推荐,兼容性差)
如果一定要尝试应用被杀后的10秒轮询,只能通过前台服务+协程的方式,但效果受系统版本和厂商定制影响极大:
步骤1:创建前台服务
前台服务会在通知栏显示持续通知,系统不会轻易杀死它:
class PollingForegroundService : Service() { private val coroutineScope = CoroutineScope(Dispatchers.IO + SupervisorJob()) override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { // 创建前台通知(需先在Manifest注册通知渠道) val notification = NotificationCompat.Builder(this, "POLLING_CHANNEL") .setContentTitle("正在获取数据") .setContentText("每10秒更新一次") .setSmallIcon(R.drawable.ic_notification) .build() startForeground(1, notification) // 启动轮询 coroutineScope.launch { val apiInterface = ApiUtilities.getInstance().create(ApiInterface::class.java) val repository = UniversityRepository(apiInterface) while (isActive) { repository.getUniversities() delay(10000) } } return START_STICKY // 服务被杀死后尝试重启 } override fun onBind(intent: Intent?): IBinder? = null override fun onDestroy() { super.onDestroy() coroutineScope.cancel() } }
步骤2:在Manifest注册服务与权限
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> <!-- Android 13+ 必填 --> <application ...> <service android:name=".PollingForegroundService" /> </application>
步骤3:启动服务
在Activity或ViewModel中触发启动:
val intent = Intent(context, PollingForegroundService::class.java) ContextCompat.startForegroundService(context, intent)
- 注意:部分厂商定制系统(如小米、华为)仍可能在应用被杀后强制停止前台服务;高频轮询会大幅消耗电量,严重影响用户体验
3. 更合理的替代方案(推荐)
鉴于Android系统的限制,10秒级轮询本身就不符合后台操作的最佳实践,推荐以下替代方案:
- 改用WebSocket:如果API支持WebSocket,用长连接代替轮询,服务器主动推送数据,既实时又省电
- 调整轮询间隔:将间隔调整到15分钟以上,用WorkManager实现可靠的后台轮询(官方推荐的后台任务方案)
- 使用FCM推送:让服务器通过Firebase Cloud Messaging主动给设备推送数据更新,无需客户端主动轮询
关键注意事项
- 高频后台轮询会严重消耗设备电量,极易引发用户反感甚至卸载应用
- Android 12(API 31)及以上对前台服务的限制更严格,部分场景下无法启动
- 应用被杀后,系统没有保证高频任务持续运行的机制,所有方案都存在兼容性问题
内容的提问来源于stack exchange,提问作者Sidharth
相关产品推荐
相关产品推荐

