android Service在8.0,9.0以上运行报错

电商项目集成极光语音播报,将消息接收等操作放于Service中。在8.0以下系统正常,8.0及以上报错,需将Service改成前台服务形式。9.0系统还需添加权限,否则会报错。此外还提及前台服务和后台服务的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、电商项目需要语音播报,然后集成了极光的语音播报,把消息接收、下载音频文件及播放都放在Service中进行,在8.0以下的系统没有问题,但是在8.0及以上的系统报错,日志如下:

android.app.RemoteServiceException: Context.startForegroundService() did not then call Service.startForeground()
	at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1961)
	at android.os.Handler.dispatchMessage(Handler.java:106)
	at android.os.Looper.loop(Looper.java:198)
	at android.app.ActivityThread.main(ActivityThread.java:7015)
	at java.lang.reflect.Method.invoke(Native Method)
	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:521)
	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:837)

然后查看了官方的文档,说要把Service改成前台服务的形式:

1)、需要在Service的onCreate()中添加下面的代码:

  public static final String CHANNEL_ID_STRING = "yyb001";

  @Override
    public void onCreate() {
        super.onCreate();
    
        NotificationManager notificationManager = (NotificationManager) 
 getSystemService(Context.NOTIFICATION_SERVICE);
        NotificationChannel mChannel = null;
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
            mChannel = new NotificationChannel(CHANNEL_ID_STRING, "诺秒贷", NotificationManager.IMPORTANCE_HIGH);
            notificationManager.createNotificationChannel(mChannel);
            Notification notification = new Notification.Builder(getApplicationContext(), CHANNEL_ID_STRING).build();
            startForeground(1, notification);
        }
    }

2)、启动服务添加兼容处理:

//开启服务兼容
        Intent intentService = new Intent(SwapSpaceApplication.this, ZhkjLocalService.class);
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
            startForegroundService(intentService);
        } else {
            startService(intentService);
        }

3)、另外在9.0的系统上需要添加权限:

    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

如果不加权限,9.0会报错,日志如下:

java.lang.RuntimeException: Unable to create service com.swap.space.zh.service.ZhkjLocalService: java.lang.SecurityException: Permission Denial: startForeground from pid=5827, uid=10094 requires android.permission.FOREGROUND_SERVICE
        at android.app.ActivityThread.handleCreateService(ActivityThread.java:3544)
        at android.app.ActivityThread.access$1300(ActivityThread.java:199)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1666)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:193)
        at android.app.ActivityThread.main(ActivityThread.java:6669)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
     Caused by: java.lang.SecurityException: Permission Denial: startForeground from pid=5827, uid=10094 requires android.permission.FOREGROUND_SERVICE
        at android.os.Parcel.createException(Parcel.java:1942)
        at android.os.Parcel.readException(Parcel.java:1910)
        at android.os.Parcel.readException(Parcel.java:1860)
        at android.app.IActivityManager$Stub$Proxy.setServiceForeground(IActivityManager.java:5198)
        at android.app.Service.startForeground(Service.java:695)
        at com.swap.space.zh.service.ZhkjLocalService.onCreate(ZhkjLocalService.java:67)
        at android.app.ActivityThread.handleCreateService(ActivityThread.java:3532)
        at android.app.ActivityThread.access$1300(ActivityThread.java:199) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1666) 
        at android.os.Handler.dispatchMessage(Handler.java:106) 
        at android.os.Looper.loop(Looper.java:193) 
        at android.app.ActivityThread.main(ActivityThread.java:6669) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858) 

 

2、理论知识:

1). 前台服务和后台服务的区别(转载https://blog.youkuaiyun.com/o279642707/article/details/82352431): 

类别区别应用
前台服务会在通知一栏显示 ONGOING 的 Notification, 当服务被终止的时候,通知一栏的 Notification 也会消失,这样对于用户有一定的通知作用。常见的如音乐播放服务
后台服务默认的服务即为后台服务,即不会在通知一栏显示 ONGOING 的 Notification。 当服务被终止的时候,用户是看不到效果的。某些不需要运行或终止提示的服务,如天气更新,日期同步,邮件同步等。

 

 

 

 

 

 

startForeground报错: FATAL EXCEPTION: main Process: com.trassion.audiopath, PID: 30638 java.lang.RuntimeException: Unable to create service com.trassion.audiopath.rpc.RpcService: java.lang.SecurityException: Starting FGS with type connectedDevice callerApp=ProcessRecord{fb94d03 30638:com.trassion.audiopath/u0a323} targetSDK=34 requires permissions: all of the permissions allOf=true [android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE] any of the permissions allOf=false [android.permission.BLUETOOTH_ADVERTISE, android.permission.BLUETOOTH_CONNECT, android.permission.BLUETOOTH_SCAN, android.permission.CHANGE_NETWORK_STATE, android.permission.CHANGE_WIFI_STATE, android.permission.CHANGE_WIFI_MULTICAST_STATE, android.permission.NFC, android.permission.TRANSMIT_IR, android.permission.UWB_RANGING, USB Device, USB Accessory] at android.app.ActivityThread.handleCreateService(ActivityThread.java:5277) at android.app.ActivityThread.-$$Nest$mhandleCreateService(Unknown Source:0) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2588) at android.os.Handler.dispatchMessage(Handler.java:107) at android.os.Looper.loopOnce(Looper.java:311) at android.os.Looper.loop(Looper.java:408) at android.app.ActivityThread.main(ActivityThread.java:9151) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:692) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:945) Caused by: java.lang.SecurityException: Starting FGS with type connectedDevice callerApp=ProcessRecord{fb94d03 30638:com.trassion.audiopath/u0a323} targetSDK=34 requires permissions: all of the permissions allOf=true [android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE] any of the permissions allOf=false [android.permission.BLUETOOTH_ADVERTISE, android.permission.BLUETOOTH_CONNECT, android.permission.BLUETOOTH_SCAN, android.permission.CHANGE_NETWORK_STATE, android.permission.CHANGE_WIFI_STATE, android.permission.CHANGE_WIFI_MULTICAST_STATE, android.permission.NFC, android.permission.TRANSMIT_IR, android.permission.UWB_RANGING, USB Device, USB Accessory] at android.os.Parcel.createExceptionOrNull(Parcel.java:3242) at android.os.Parcel.createException(Parcel.java:3226) at android.os.Parcel.readException(Parcel.java:3209) at android.os.Parcel.readException(Parcel.java:3151) at android.app.IActivityManager$Stub$Proxy.setServiceForeground(IActivityManager.java:7547) at android.app.Service.startForeground(Service.java:863) at com.trassion.audiopath.rpc.RpcService.onCreate(RpcService.kt:57) at android.app.ActivityThread.handleCreateService(ActivityThread.java:5264) ... 9 more Caused by: android.os.RemoteException: Remote stack trace: at com.android.server.am.ActiveServices.validateForegroundServiceType(ActiveServices.java:3078) at com.android.server.am.ActiveServices.setServiceForegroundInnerLocked(ActiveServices.java:2756) at com.android.server.am.ActiveServices.setServiceForegroundLocked(ActiveServices.java:2000) at com.android.server.am.ActivityManagerService.setServiceForeground(ActivityManagerService.java:15512) at android.app.IActivityManager$Stub.onTransact$setServiceForeground$(IActivityManager.java:13167)
最新发布
07-18
<think>我们正在解决在Android 14.0+中启动前台服务时因权限不足导致的SecurityException问题。根据引用[1],在Android 14及以上版本,使用前台服务必须声明特定的权限,并且这些权限在清单文件中声明后是不可撤销的(一般权限)。如果未声明,调用startForeground()会抛出SecurityException。 具体到错误信息,需要FOREGROUND_SERVICE_CONNECTED_DEVICE权限,这表明我们正在使用的前台服务类型是“connectedDevice”。因此,我们需要在AndroidManifest.xml中声明该权限以及相应的前台服务类型。 解决方案步骤: 1.AndroidManifest.xml中声明权限和设置服务的前台服务类型属性。 2. 在启动前台服务时,使用正确的API(包括针对Android 14+的startForeground方法的重载版本,指定服务类型)。 3. 在启动服务前检查权限(虽然是一般权限,但声明后系统会自动授予,所以主要确保声明正确)。 具体实现: 步骤1:修改AndroidManifest.xml ```xml <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.app"> <!-- 声明前台服务基本权限(Android 9.0+) --> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <!-- Android 14+ 连接设备前台服务类型所需权限 --> <uses-permission android:name="android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE" /> <application ...> <!-- 服务声明 --> <service android:name=".MyForegroundService" android:enabled="true" android:exported="false" android:foregroundServiceType="connectedDevice" /> <!-- 注意:foregroundServiceType属性在Android 10(API 29)引入,但在Android 14(API 34)成为强制要求 --> </application> </manifest> ``` 步骤2:在服务类中正确启动前台服务(注意Android 14+的特殊处理) ```kotlin class MyForegroundService : Service() { private val NOTIFICATION_ID = 1001 private val CHANNEL_ID = "foreground_service_channel" override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { // 创建通知渠道(针对Android 8.0+) createNotificationChannel() // 构建通知 val notification = buildNotification() // 启动前台服务 try { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { // Android 14 (API 34) 及以上 startForeground( NOTIFICATION_ID, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE ) } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { // Android 8.0 (API 26)Android 13 startForeground(NOTIFICATION_ID, notification) } else { // Android 8.0以下,直接启动前台服务(但注意,startForegroundService方法在8.0引入,所以这里用startService) startService(Intent(this, MyForegroundService::class.java)) // 并且需要调用startForeground(但低版本没有通知渠道,直接构建通知) startForeground(NOTIFICATION_ID, notification) } } catch (e: SecurityException) { // 处理异常:可能是权限声明问题 e.printStackTrace() // 可以选择停止服务 stopSelf() } return START_STICKY } private fun createNotificationChannel() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channel = NotificationChannel( CHANNEL_ID, "Foreground Service Channel", NotificationManager.IMPORTANCE_LOW ).apply { description = "Channel for foreground service" } val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager manager.createNotificationChannel(channel) } } private fun buildNotification(): Notification { return NotificationCompat.Builder(this, CHANNEL_ID) .setContentTitle("服务运行中") .setContentText("正在执行后台任务...") .setSmallIcon(R.drawable.ic_notification) .setPriority(NotificationCompat.PRIORITY_LOW) .build() } override fun onBind(intent: Intent?): IBinder? = null } ``` 步骤3:启动服务(注意:从Android 8.0开始,启动前台服务必须使用startForegroundService) ```kotlin // 启动服务 val serviceIntent = Intent(context, MyForegroundService::class.java) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { context.startForegroundService(serviceIntent) } else { context.startService(serviceIntent) } ``` 注意:在Android 8.0及以上,使用startForegroundService启动服务后,必须在5秒内调用该服务的startForeground()方法,否则会引发ANR。 错误排查: 1. 确保在AndroidManifest.xml中正确声明了权限和服务属性。 2. 确保在服务中针对Android 14+使用了带有foregroundServiceType参数的startForeground方法。 3. 如果仍然遇到SecurityException,请检查权限字符串是否正确(注意大小写)以及服务声明中的foregroundServiceType是否设置。 引用[1]提到,如果应用调用startForeground()但未声明适当的前台服务类型权限,系统将引发SecurityException异常。因此,请再次确认权限和服务类型声明。 另外,根据引用[3],播放音频的Service应设置为前台Service,并使用startForegroundService启动。我们这里虽然不是音频,但同样需要前台服务类型。 关于前台服务类型connectedDevice,它适用于与设备连接(如蓝牙、NFC、USB等)相关的服务。因此,如果你的服务确实涉及设备连接,使用这个类型是合适的。 如果服务不涉及设备连接,可能需要选择其他前台服务类型,例如: - dataSync:数据同步 - location:位置 - mediaPlayback:媒体播放 - ... 等等 根据实际服务功能选择正确的前台服务类型,并声明对应的权限。 最后,再次强调:在AndroidManifest.xml中声明的权限是“一般权限”,系统会在安装时自动授予,用户无法撤销。因此,只要正确声明,就不会出现运行时权限请求被拒绝的情况。 如果按照上述步骤操作,应该可以解决SecurityException问题。 如果问题仍然存在,请检查: - 应用的目标SDK版本是否设置为34(Android 14)或更高?如果是,必须遵守上述规则。 - 是否在多个地方启动了前台服务,而其中某处没有正确处理? 希望这些步骤能帮助你解决问题。</think>### 解决 Android 14.0+ 前台服务 SecurityException 问题 在 Android 14.0+ 中启动前台服务时遇到 `SecurityException: FOREGROUND_SERVICE_CONNECTED_DEVICE` 错误,是由于 Android 14 引入了新的前台服务类型权限要求[^1]。以下是完整解决方案: #### 1. 清单文件关键配置 ```xml <manifest> <!-- 基础前台服务权限 --> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <!-- Android 14+ 连接设备服务专用权限 --> <uses-permission android:name="android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE" /> <!-- 其他可能需要的权限 --> <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /> <uses-permission android:name="android.permission.NFC" /> <service android:name=".DeviceConnectionService" android:foregroundServiceType="connectedDevice" android:exported="false" /> </manifest> ``` > **关键点**: > - 必须声明 `FOREGROUND_SERVICE_CONNECTED_DEVICE` 权限[^1] > - 服务必须设置 `android:foregroundServiceType="connectedDevice"` > - 蓝牙/NFC等设备相关权限根据实际功能添加 #### 2. 服务启动代码(Kotlin) ```kotlin fun startDeviceService(context: Context) { val serviceIntent = Intent(context, DeviceConnectionService::class.java) try { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { // Android 8.0+ 必须使用 startForegroundService context.startForegroundService(serviceIntent) } else { context.startService(serviceIntent) } } catch (e: SecurityException) { // 处理权限缺失情况 handleSecurityException(context, e) } } private fun handleSecurityException(context: Context, e: SecurityException) { when { // Android 14+ 特定权限缺失 e.message?.contains("FOREGROUND_SERVICE_CONNECTED_DEVICE") == true -> { showPermissionNotification(context) } // 其他权限问题 else -> { Log.e("ServiceStart", "SecurityException: ${e.message}") } } } ``` #### 3. 服务实现类(处理 Android 14+) ```kotlin class DeviceConnectionService : Service() { private val NOTIFICATION_ID = 2001 private val CHANNEL_ID = "device_service_channel" override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { try { startForegroundWithType() } catch (e: SecurityException) { // 权限异常处理 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { if (e.message?.contains("FOREGROUND_SERVICE_CONNECTED_DEVICE") == true) { stopSelf() // 停止服务避免崩溃 } } } return START_STICKY } private fun startForegroundWithType() { val notification = buildNotification() // Android 14+ 特殊处理 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { startForeground( NOTIFICATION_ID, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE ) } else { startForeground(NOTIFICATION_ID, notification) } } private fun buildNotification(): Notification { // 创建通知渠道(Android 8.0+ 必需) createNotificationChannel() return NotificationCompat.Builder(this, CHANNEL_ID) .setContentTitle("设备连接服务") .setContentText("正在管理设备连接") .setSmallIcon(R.drawable.ic_device_connection) .setPriority(NotificationCompat.PRIORITY_DEFAULT) .build() } // ... 其他必要方法 } ``` #### 4. 权限缺失处理方案 ```kotlin private fun showPermissionNotification(context: Context) { val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply { data = Uri.parse("package:${context.packageName}") flags = Intent.FLAG_ACTIVITY_NEW_TASK } val pendingIntent = PendingIntent.getActivity( context, 0, intent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT ) val notification = NotificationCompat.Builder(context, "permission_channel") .setContentTitle("需要权限授权") .setContentText("点击开启设备连接权限") .setSmallIcon(android.R.drawable.ic_dialog_alert) .setContentIntent(pendingIntent) .setAutoCancel(true) .build() val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager notificationManager.notify(3001, notification) } ``` #### 5. 错误排查清单 1. **权限声明检查**: - 确认 `AndroidManifest.xml` 包含 `FOREGROUND_SERVICE_CONNECTED_DEVICE` - 验证服务声明包含 `android:foregroundServiceType="connectedDevice"` 2. **API 级别处理**: ```kotlin // 正确的前台服务启动方式 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { startForegroundService(serviceIntent) } else { startService(serviceIntent) } ``` 3. **权限状态验证**: ```kotlin fun hasConnectedDevicePermission(context: Context): Boolean { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { ContextCompat.checkSelfPermission( context, Manifest.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE ) == PackageManager.PERMISSION_GRANTED } else { true // 低版本不需要此权限 } } ``` #### 6. 适用场景说明 `FOREGROUND_SERVICE_CONNECTED_DEVICE` 权限适用于以下设备连接场景[^1]: - 蓝牙设备配对和数据传输 - NFC 近场通信交互 - USB 外设连接管理 - UWB 超宽带设备交互 - 智能家居设备控制 > **数学关系**: > 前台服务权限验证流程可表示为: > $$ > \text{启动服务} \rightarrow > \begin{cases} > \text{SDK < 34} & \rightarrow \text{直接启动} \\ > \text{SDK ≥ 34} & \rightarrow > \begin{cases} > \text{有权限} & \rightarrow \text{启动成功} \\ > \text{无权限} & \rightarrow \text{SecurityException} > \end{cases} > \end{cases} > $$ --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

andrew_style

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值