Android MediaSessionCompat初始化触发ISession.getController() NPE如何解决
问题根因
- 该崩溃属于Android 10(API 29)的系统级已知Bug:当系统媒体服务进程临时异常、被杀死或者部分定制ROM对后台服务做了权限限制时,原生
MediaSession构造过程中内部ISession实例会返回空,进而抛出此NullPointerException,不属于代码逻辑的直接错误,但缺少异常捕获逻辑会导致应用崩溃。 - 你的代码存在三处可优化的兼容隐患:
- 初始化
MediaSessionCompat时没有捕获系统层抛出的异常,异常直接向上抛出导致服务崩溃。 MediaMetadata使用了framework层的构造器再转换为MediaMetadataCompat,部分低版本/定制ROM可能存在序列化兼容问题,加大MediaSession初始化失败概率。- Android 10及以上版本要求后台服务必须先转为前台服务才能初始化MediaSession,若你的服务是后台启动未先设置前台,也会提升该异常出现概率。
- 初始化
规避实现方案
1. 改造MediaSessionCompat初始化逻辑,增加异常捕获
统一使用MediaMetadataCompat构造元数据,同时捕获所有初始化阶段的异常,异常时返回空,不阻断服务运行:
open class API21VersionFactory : DefaultAndroidVersionFactory() { ... override fun getMediaSessionCompat(context: Context): MediaSessionCompat? { return try { MediaSessionCompat(context, "TAG").apply { setMetadata( MediaMetadataCompat.Builder() .putLong(MediaMetadataCompat.METADATA_KEY_DURATION, -1) .build() ) // 主动标记会话为活跃状态 isActive = true } } catch (e: Exception) { // 捕获所有初始化异常,包括系统NPE、权限异常 null } } ... }
2. 调整Service初始化时序,先启动前台再初始化MediaSession
Android 10及以上版本禁止后台服务初始化MediaSession,需要先将服务提升为前台服务再执行初始化操作:
companion object { private const val NOTIFICATION_ID = 1001 } override fun onCreate() { super.onCreate() // API26+先创建通知渠道 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channelId = versionFactory.getAudioPlayerNotificationChannelId(this) val channel = NotificationChannel(channelId, "音频播放", NotificationManager.IMPORTANCE_LOW).apply { setShowBadge(false) } getSystemService(NotificationManager::class.java).createNotificationChannel(channel) // 先启动前台服务,传递临时占位通知 val tempNotification = NotificationCompat.Builder(this, channelId) .setSmallIcon(R.drawable.ic_icon3) .setContentTitle("正在启动播放器") .build() startForeground(NOTIFICATION_ID, tempNotification) } // 再初始化MediaSession mediaSessionCompat = versionFactory.getMediaSessionCompat(this) // 最后更新为带MediaStyle的正式通知 updateMediaNotification() }
3. 通知逻辑保持现有判空即可
你现有的通知代码已经对mediaSessionCompat做了非空判断,当MediaSession初始化失败时会自动降级为普通通知,不影响音频播放核心功能,无需额外修改。
该崩溃仅出现在系统媒体服务异常的极端场景,常规测试环境系统服务状态正常,所以之前无法复现,以上方案可100%覆盖该异常场景。
内容的提问来源于stack exchange,提问作者Agustín Alcázar
相关产品推荐
相关产品推荐

