前台服务通知栏适配实践

188 阅读2分钟

针对 Android 平台的前台服务通知栏适配实践,以下是关键步骤和注意事项:

一、基础配置

  1. AndroidManifest.xml 声明
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<service 
    android:name=".MyForegroundService"
    android:foregroundServiceType="mediaPlayback|location" 
    android:exported="false"/>
  1. 基础前台服务实现
class MyForegroundService : Service() {
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        val notification = buildNotification()
        startForeground(NOTIFICATION_ID, notification)
        return START_STICKY
    }
}

二、版本适配要点

Android 8.0+(API 26+)

通知渠道强制要求

fun createNotificationChannel() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        val channel = NotificationChannel(
            CHANNEL_ID,
            "服务通知",
            NotificationManager.IMPORTANCE_LOW
        ).apply {
            description = "后台服务运行状态通知"
        }
        
        getSystemService(NotificationManager::class.java)
            .createNotificationChannel(channel)
    }
}

Android 12+(API 31+)

  1. 前台服务启动限制
// 启动服务前检查
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
    val hasPermission = ActivityManager.getService()
        .isBackgroundRestrictedExempted()
    if (!hasPermission) {
        // 引导用户前往设置页面授权
        val intent = Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)
        intent.data = Uri.parse("package:${packageName}")
        startActivity(intent)
    }
}
  1. 前台服务类型声明
val serviceType = when {
    Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q -> {
        FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK or
        FOREGROUND_SERVICE_TYPE_LOCATION
    }
    else -> 0
}

startForeground(NOTIFICATION_ID, notification, serviceType)

Android 13+(API 33+)

运行时通知权限

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
    when (checkSelfPermission(POST_NOTIFICATIONS)) {
        PERMISSION_GRANTED -> startService()
        else -> {
            requestPermissions(
                arrayOf(POST_NOTIFICATIONS),
                NOTIFICATION_PERMISSION_CODE
            )
        }
    }
} else {
    startService()
}

三、通知内容优化

  1. 必要元素配置
fun buildNotification(): Notification {
    return NotificationCompat.Builder(this, CHANNEL_ID)
        .setContentTitle("服务运行中")
        .setContentText("正在执行重要任务...")
        .setSmallIcon(R.drawable.ic_stat_notification)
        .setPriority(NotificationCompat.PRIORITY_LOW)
        .setOngoing(true)
        .apply {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
                setForegroundServiceBehavior(Notification.FOREGROUND_SERVICE_IMMEDIATE)
            }
        }
        .build()
}
  1. 交互控制
// 添加操作按钮
.addAction(
    R.drawable.ic_stop, 
    "停止服务", 
    PendingIntent.getService(...)
)

// 点击跳转
.setContentIntent(
    PendingIntent.getActivity(
        this, 0,
        Intent(this, MainActivity::class.java),
        PendingIntent.FLAG_IMMUTABLE
    )
)

四、厂商适配技巧

  1. 小米设备
// 检测自启动权限
if (Build.MANUFACTURER.equals("xiaomi", ignoreCase = true)) {
    if (!isAutoStartEnabled()) {
        showDialog("请在设置中开启自启动权限")
    }
}
  1. 华为设备
// 电池优化白名单检测
if (Build.MANUFACTURER.equals("huawei", ignoreCase = true)) {
    if (!isIgnoringBatteryOptimizations()) {
        val intent = Intent()
        intent.component = ComponentName(
            "com.huawei.systemmanager",
            "com.huawei.systemmanager.power.ui.HwPowerManagerActivity"
        )
        startActivity(intent)
    }
}

五、最佳实践

  1. 统一使用 NotificationManagerCompat
NotificationManagerCompat.from(this)
    .notify(NOTIFICATION_ID, notification)
  1. 动态权限检测模板
fun checkNotificationPermission(): Boolean {
    return when {
        Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU -> {
            checkSelfPermission(POST_NOTIFICATIONS) == PERMISSION_GRANTED
        }
        Build.VERSION.SDK_INT >= Build.VERSION_CODES.O -> {
            notificationManager.importance != NotificationManager.IMPORTANCE_NONE
        }
        else -> true
    }
}
  1. 服务保活策略
  • 使用 START_STICKY 启动方式
  • 结合 JobScheduler 进行服务重启
  • 在 onDestroy() 中发送重启广播

六、调试技巧

# 检查前台服务状态
adb shell dumpsys activity services your.package.name

# 强制关闭前台服务
adb shell am stopservice your.package.name/.YourService

以上方案需注意:

  1. 遵循最小必要原则,避免滥用前台服务
  2. 在通知中明确说明服务用途
  3. 提供便捷的服务停止入口
  4. 定期检查服务必要性并及时释放资源

实际开发中建议结合 WorkManager 等后台任务调度方案,最大限度降低对系统资源的影响。