Dagger Hilt单例在应用移除任务重启后未实例化的解决方法
问题分析
你的问题核心在于ExoPlayer被声明为进程级单例(@Singleton + SingletonComponent),但你在onTaskRemoved中手动释放了ExoPlayer资源。由于Dagger Hilt的SingletonComponent单例与进程生命周期绑定,只要应用进程未完全销毁,Hilt就不会重新创建ExoPlayer实例。当你重启应用时,注入的仍是已经被release()的废弃实例,其内部关联的Handler线程已死亡,从而抛出Handler sending message to a dead thread异常。
解决方案
方案1:调整ExoPlayer作用域,绑定到Service生命周期
将ExoPlayer的依赖注入作用域从SingletonComponent改为ServiceComponent,让ExoPlayer的生命周期与EPlayerSessionService绑定,每次Service启动都会生成新的实例:
- 修改
AppModule的安装组件和作用域:
@Module @InstallIn(ServiceComponent::class) // 改为ServiceComponent class AppModule { @Provides @ServiceScoped // 使用Service级作用域 fun providesAudioAttributes(): AudioAttributes = AudioAttributes.Builder() .setContentType(C.AUDIO_CONTENT_TYPE_MUSIC) .setUsage(C.USAGE_MEDIA) .build() @OptIn(UnstableApi::class) @Provides @ServiceScoped fun provideExoPlayer( @ApplicationContext context: Context, audioAttributes: AudioAttributes, ): ExoPlayer { Log.d("ExoPlayer", "Creating new instance of ExoPlayer") return ExoPlayer.Builder(context) .setAudioAttributes(audioAttributes, true) .setHandleAudioBecomingNoisy(true) .setTrackSelector(DefaultTrackSelector(context)) .build() } }
EPlayerSessionService的注入逻辑保持不变,现在每次Service启动时,Hilt都会提供全新的ExoPlayer实例,避免使用已释放的废弃对象。
方案2:用工厂模式管理ExoPlayer实例(适用于跨组件共享场景)
如果需要在多个组件(如Activity、Service)中共享ExoPlayer,可创建工厂类管理实例状态,释放后置空以允许重新创建:
- 创建ExoPlayer工厂类:
class ExoPlayerFactory @Inject constructor( @ApplicationContext private val context: Context, private val audioAttributes: AudioAttributes ) { private var exoPlayer: ExoPlayer? = null @OptIn(UnstableApi::class) fun getInstance(): ExoPlayer { return exoPlayer ?: run { Log.d("ExoPlayer", "Creating new instance of ExoPlayer") ExoPlayer.Builder(context) .setAudioAttributes(audioAttributes, true) .setHandleAudioBecomingNoisy(true) .setTrackSelector(DefaultTrackSelector(context)) .build() .also { exoPlayer = it } } } fun release() { exoPlayer?.release() exoPlayer = null // 置空,下次getInstance时会重新创建 } }
- 在
AppModule中提供工厂的单例:
@Module @InstallIn(SingletonComponent::class) class AppModule { @Provides @Singleton fun providesAudioAttributes(): AudioAttributes = AudioAttributes.Builder() .setContentType(C.AUDIO_CONTENT_TYPE_MUSIC) .setUsage(C.USAGE_MEDIA) .build() @Provides @Singleton fun provideExoPlayerFactory( @ApplicationContext context: Context, audioAttributes: AudioAttributes ): ExoPlayerFactory { return ExoPlayerFactory(context, audioAttributes) } }
- 修改Service中的注入和释放逻辑:
@AndroidEntryPoint class EPlayerSessionService:MediaSessionService() { @Inject lateinit var mediaSession: MediaSession @Inject lateinit var notificationManager: EPlayerNotificationManager @Inject lateinit var playerServiceHandler: PlayerServiceHandler @Inject lateinit var exoPlayerFactory: ExoPlayerFactory private lateinit var exoPlayer: ExoPlayer override fun onCreate() { super.onCreate() exoPlayer = exoPlayerFactory.getInstance() } override fun onTaskRemoved(rootIntent: Intent?) { super.onTaskRemoved(rootIntent) if (exoPlayer.playWhenReady) { exoPlayer.pause() } exoPlayerFactory.release() // 调用工厂的release方法置空实例 mediaSession.release() stopSelf() } }
额外注意事项
- 如果应用中有其他组件(如Activity)使用ExoPlayer,需同步调整注入逻辑,确保Service释放后,其他组件能获取新实例。
- 避免直接持有ExoPlayer的全局引用,始终通过工厂或作用域正确管理实例生命周期。
内容的提问来源于stack exchange,提问作者daniel ernest
相关产品推荐
相关产品推荐

