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

如何优化Android应用通知系统?提升性能与可维护扩展性

优化方案:高效、可维护的Android通知系统

一、消除代码冗余,提升扩展性

核心思路是抽象通用检查逻辑,将重复的API请求、缓存对比、通知发送逻辑抽离为通用方法,仅将差异化部分(API端点、DAO、通知样式)封装为可配置组件:

  1. 定义通用检查接口,封装差异化行为:
interface NotificationChecker<T> {
    // 返回目标API端点
    fun getApiEndpoint(): String
    // 返回对应的Room DAO
    fun getDao(): BaseDao<T>
    // 根据数据构建通知
    fun buildNotification(data: T): Notification
    // 解析API返回的数据列表
    fun parseApiResponse(response: String): List<T>
}
  1. 实现通用检查方法,复用核心逻辑:
private suspend fun <T> checkNewData(checker: NotificationChecker<T>) {
    // 1. 调用API获取数据
    val response = APIManager.GET(checker.getApiEndpoint())
    val newDataList = checker.parseApiResponse(response)
    if (newDataList.isEmpty()) return

    // 2. 从Room获取缓存数据
    val cachedDataList = checker.getDao().getAll()
    val newItems = newDataList.filterNot { cachedDataList.contains(it) }

    // 3. 发送新数据通知
    newItems.forEach { item ->
        NotificationManagerCompat.from(this)
            .notify(getUniqueId(), checker.buildNotification(item))
    }

    // 4. 更新缓存
    checker.getDao().insertAll(newItems)
}
  1. 针对活动、公告实现具体Checker:
class ActivityChecker(private val context: Context) : NotificationChecker<Activity> {
    override fun getApiEndpoint() = "/api/v1/activities"
    override fun getDao() = AppDatabase.getInstance(context).activityDao()
    override fun buildNotification(data: Activity) = NotificationCompat.Builder(context, CHANNEL_ID)
            .setContentTitle("新活动:${data.title}")
            .setContentText(data.description)
            .build()
    override fun parseApiResponse(response: String) = Gson().fromJson(response, Array<Activity>::class.java).toList()
}

// 公告Checker类似,仅修改端点、DAO和通知内容
  1. 调用时只需传入对应Checker:
// 替代原有的checkActivity()和checkAnnouncements()
checkNewData(ActivityChecker(this))
checkNewData(AnnouncementChecker(this))

后续新增其他通知类型时,只需实现NotificationChecker接口,无需修改核心检查逻辑,扩展性大幅提升。

二、优化性能与API调用效率

  1. 令牌智能管理:

    • 存储令牌的过期时间(从API返回的过期字段提取),仅在令牌即将过期或请求返回401时重新登录,避免每次检查都调用登录API。
    • 示例:
    private fun shouldRefreshToken(): Boolean {
        val expiryTime = PreferenceManager.getDefaultSharedPreferences(this).getLong("token_expiry", 0)
        return System.currentTimeMillis() >= expiryTime - 300000 // 提前5分钟刷新
    }
    
  2. 增量API请求:

    • 向第三方API传递上次检查的时间戳或最后一条数据的ID,要求返回仅新增的数据(若API支持),减少数据传输量和本地对比的计算开销。
    • 示例:
    override fun getApiEndpoint(): String {
        val lastCheckTime = PreferenceManager.getDefaultSharedPreferences(context).getLong("last_activity_check", 0)
        return "/api/v1/activities?after=$lastCheckTime"
    }
    
  3. 替换轮询机制:

    • 放弃ScheduledExecutorService+前台Service的方案,改用WorkManager实现周期性任务调度:
      • WorkManager自动适配Android 8+的后台限制,无需前台通知,避免系统杀死进程。
      • 配置周期性任务:
      val periodicWork = PeriodicWorkRequestBuilder<NotificationWorker>(90, TimeUnit.SECONDS)
              .build()
      WorkManager.getInstance(context).enqueueUniquePeriodicWork(
              "notification_check",
              ExistingPeriodicWorkPolicy.KEEP,
              periodicWork
      )
      
      • 在NotificationWorker中执行检查逻辑,无需前台服务,彻底消除烦人的前台通知。

三、消除前台服务通知

如上述性能优化中提到的,用WorkManager替代前台Service是最优解:

  • WorkManager的周期性任务由系统调度,不需要将服务设为前台,自然不会显示强制通知。
  • 若必须保留Service(例如需要长期持有连接),可仅在执行耗时操作时临时启动前台服务,操作完成后立即切换回后台,减少通知显示时间。

四、Firebase适配方案(第三方API场景)

若想使用Firebase提升效率,可通过自建中转服务器实现:

  1. 自建服务器定时/实时拉取第三方API的新数据(替代客户端轮询)。
  2. 一旦检测到新数据,服务器通过Firebase Cloud Messaging (FCM) 向客户端推送通知。
  3. 客户端接收FCM消息后,再调用API获取完整数据并更新缓存。

这种方案的优势:

  • 客户端无需轮询,大幅降低电量和流量消耗。
  • 避免客户端前台服务的限制,完全消除本地通知。
  • 自建服务器可统一处理API令牌、增量请求,减轻客户端压力。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 00:20:08