创建前台服务通知

ANR(Application Not Responding) 错误,是 Android 系统中一个典型的 前台服务启动违规 问题。具体错误信息如下:

1
2
Reason: Context.startForegroundService() did not then call Service.startForeground(): 
ServiceRecord{... com.hansong.orion.sipclient/.service.SipAidlService}

🔍 问题原因

从 Android 8.0(API 级别 26)开始,Google 对 前台服务(Foreground Service) 的使用做了严格限制:

  • 如果你通过 Context.startForegroundService() 启动一个服务,
  • 那么该服务 必须在 5 秒内 调用 startForeground(int id, Notification notification)
  • 否则系统会抛出 ANR,并可能杀死你的应用。

你的 SipAidlService 被通过 startForegroundService() 启动了,但 **没有及时调用 startForeground()**,导致系统判定为无响应。


✅ 解决方案

1. 定义权限

AndroidManifest.xml中添加前台服务及通知权限

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!-- 必须:通知权限(API 33+ 需要) 危险权限,需要动态申请-->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

<!-- 必须:前台服务基础权限(API 28+ 需要) 普通权限-->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

<!-- 这里有很多类型可以选择(如dataSync),根据服务的类型定义 普通权限-->
<!-- 可选:如果你使用了 foregroundServiceType="phoneCall"(API 29+) -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_PHONE_CALL" />
<!-- 系统权限 非普通应用-->
<uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />

<service
android:name=".service.SipAidlService"
android:foregroundServiceType="phoneCall"
... />

动态申请

1
2
3
4
5
6
7
8
9
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS)
!= PackageManager.PERMISSION_GRANTED) {
// 请求权限
ActivityCompat.requestPermissions(this,
arrayof(Manifest.permission.POST_NOTIFICATIONS),
REQUEST_CODE_POST_NOTIFICATIONS)
}
}

2. 确保在 onStartCommand() 中立即调用 startForeground()

SipAidlServiceonStartCommand() 方法开头,尽快创建并显示一个有效的通知:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Override
fun onStartCommand(intent: Intent, flags: Int, startId: Int) {
// 创建通知渠道(Android 8.0+ 必须)
createNotificationChannel()

val notification: Notification = NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("SIP Service Running")
.setContentText("SIP client is active")
.setSmallIcon(R.drawable.ic_notification)
.build()

// 必须在 5 秒内调用!
startForeground(1, notification)

// 其他初始化逻辑...
return START_STICKY
}

⚠️ 注意:不要在 onCreate() 中调用 startForeground(),因为 onCreate() 只在服务首次创建时调用,而 startForegroundService() 每次都会触发 onStartCommand()

2. 创建通知渠道(Android 8.0+ 必需)

1
2
3
4
5
6
7
8
9
10
11
private fun createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val serviceChannel: NotificationChannel = NotificationChannel(
CHANNEL_ID,
"SIP Service Channel",
NotificationManager.IMPORTANCE_LOW
)
val manager: NotificationManager = getSystemService(NotificationManager.class)
manager.createNotificationChannel(serviceChannel)
}
}

定义常量:

1
private const val String CHANNEL_ID = "sip_service_channel"

3. 避免耗时操作阻塞 onStartCommand()

如果 onStartCommand() 中有网络请求、数据库操作、SIP 初始化等耗时任务,务必放到子线程中执行,否则即使调用了 startForeground(),主线程仍可能卡住导致 ANR。

建议使用:

  • HandlerThread
  • ExecutorService
  • WorkManager(不适合前台服务)
  • 或 Kotlin 协程(如果你用 Kotlin)

示例:

1
2
3
new Thread(() -> {
// 执行 SIP 初始化等耗时操作
}).start();