android java ibinder_Java IBinder.isBinderAlive方法代码示例

本文介绍了一种在Android环境中获取已注册服务的IBinder对象的方法。该方法首先尝试从本地缓存中查找请求的服务,如果未找到,则通过远程服务通道获取,并进行必要的包装和缓存更新。

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

import android.os.IBinder; //导入方法依赖的package包/类

/**

* 获取已注册服务的IBinder对象,前提是该服务是静态服务,即默认一直存在,或者自己已经启动并且向我们注册过;

* 注意不能通过此借口获取一个插件的服务,除非明确知道该插件的服务已经主动注册过,否则使用getPluginService()

*

* @param context

* @param serviceName 请求获取的service名称

* @return 所请求的service实现对象

*/

public static IBinder getService(Context context, String serviceName) {

if (DEBUG) {

Log.d(TAG, "[getService] begin = " + SystemClock.elapsedRealtime());

}

IBinder service = null;

/**

* 先考虑本地缓存

*/

SoftReference ref = sCache.get(serviceName);

if (ref != null) {

service = ref.get();

if (service != null) {

if (service.isBinderAlive() && service.pingBinder()) {

if (DEBUG) {

Log.d(TAG, "[getService] Found service from cache: " + serviceName);

Log.d(TAG, "[getService] end = " + SystemClock.elapsedRealtime());

}

return service;

} else {

sCache.remove(serviceName);

}

}

}

IServiceChannel serviceChannel = getServerChannel(context);

if (serviceChannel == null) {

return null;

}

try {

service = serviceChannel.getService(serviceName);

if (service != null) {

if (DEBUG) {

Log.d(TAG, "[getService] Found service from remote service channel: " + serviceName);

}

service = ServiceWrapper.factory(context, serviceName, service);

sCache.put(serviceName, new SoftReference(service));

}

} catch (RemoteException e) {

if (DEBUG) {

Log.e(TAG, "[getService] Error when getting service from service channel...", e);

}

}

if (DEBUG) {

Log.d(TAG, "[getService] end = " + SystemClock.elapsedRealtime());

}

return service;

}

03-21 23:30:13.820 1000 2675 3465 D CompatChangeReporter: Compat change id reported: 311208629; UID 10125; state: ENABLED 03-21 23:30:13.820 1000 2675 2701 W DisplayManagerService: Failed to notify process 28083 that displays changed, assuming it died. 03-21 23:30:13.820 1000 2675 2701 W DisplayManagerService: android.os.DeadObjectException 03-21 23:30:13.820 1000 2675 2701 W DisplayManagerService: at android.os.BinderProxy.transactNative(Native Method) 03-21 23:30:13.820 1000 2675 2701 W DisplayManagerService: at android.os.BinderProxy.transact(BinderProxy.java:586) 03-21 23:30:13.820 1000 2675 2701 W DisplayManagerService: at android.hardware.display.IDisplayManagerCallback$Stub$Proxy.onDisplayEvent(IDisplayManagerCallback.java:119) 03-21 23:30:13.820 1000 2675 2701 W DisplayManagerService: at com.android.server.display.DisplayManagerService$CallbackRecord.notifyDisplayEventAsync(DisplayManagerService.java:3789) 03-21 23:30:13.820 1000 2675 2701 W DisplayManagerService: at com.android.server.display.DisplayManagerService$PendingCallback.sendPendingDisplayEvent(DisplayManagerService.java:3855) 03-21 23:30:13.820 1000 2675 2701 W DisplayManagerService: at com.android.server.display.DisplayManagerService$UidImportanceListener.onUidImportance(DisplayManagerService.java:1058) 03-21 23:30:13.820 1000 2675 2701 W DisplayManagerService: at android.app.ActivityManager$MyUidObserver.onUidStateChanged(ActivityManager.java:217) 03-21 23:30:13.820 1000 2675 2701 W DisplayManagerService: at com.android.server.am.UidObserverController.dispatchUidsChangedForObserver(UidObserverController.java:414) 03-21 23:30:13.820 1000 2675 2701 W DisplayManagerService: at com.android.server.am.UidObserverController.dispatchUidsChanged(UidObserverController.java:280) 03-21 23:30:13.820 1000 2675 2701 W DisplayManagerService: at com.android.server.am.UidObserverController$$ExternalSyntheticLambda0.run(D8$$SyntheticClass:0) 03-21 23:30:13.820 1000
03-23
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
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值