Dagger Hilt单例注入返回不同实例问题求助
核心问题分析
你的代码中TableDAO通过@Singleton注解声明为单例,但ViewModel和SyncAdapter拿到不同实例,大概率是以下两个原因之一:
- SyncAdapter注入逻辑错误,导致手动创建了实例而非通过Hilt获取单例
- SyncAdapter运行在独立进程,Hilt的单例是进程内唯一,跨进程无法共享
步骤1:修复SyncAdapter的注入逻辑
你的SyncAdapter构造函数缺少Context参数,无法正确继承AbstractThreadedSyncAdapter,这会导致Hilt无法创建实例,你可能被迫手动new SyncAdapter,从而获取到非单例的TableDAO。
修改SyncAdapter构造:
class SyncAdapter @Inject constructor( @ApplicationContext private val context: Context, private val tableDAO: TableDAO ) : AbstractThreadedSyncAdapter(context, true, true) { // 你的同步逻辑 }
确保SyncService的onBind方法正确返回SyncAdapter的Binder:
@AndroidEntryPoint class SyncService : Service() { @Inject lateinit var syncAdapter: SyncAdapter override fun onBind(intent: Intent): IBinder? { return syncAdapter.syncAdapterBinder } }
步骤2:检查SyncAdapter是否运行在独立进程
打开AndroidManifest.xml,查看SyncService的配置,如果存在android:process属性(比如android:process=":sync"),说明SyncAdapter运行在独立进程,此时Hilt的@Singleton仅在当前进程生效,主进程的ViewModel和SyncAdapter的TableDAO是完全独立的实例,导致数据无法同步。
解决方案:
方案A:取消独立进程(推荐)
如果不需要单独进程,删除SyncService的android:process属性,让SyncAdapter运行在主进程,这样就能共享同一个Database和TableDAO实例,Room的LiveData会自动感知数据变化并通知ViewModel。
方案B:保留多进程的适配
如果必须使用多进程,需要通过跨进程通信实现数据同步:
- 将SyncAdapter的数据库操作请求通过
ContentResolver发送到主进程的ContentProvider,由主进程执行数据库写入 - 使用
Messenger或AIDL实现进程间通信,让主进程处理数据更新,确保ViewModel的LiveData能收到通知
额外验证
确认DatabaseModule的单例配置正确:
@Module @InstallIn(SingletonComponent::class) class DatabaseModule { @Singleton @Provides fun provideDatabase(@ApplicationContext context: Context): Database { return Room.databaseBuilder( context, Database::class.java, "your_database_name" ).build() } @Singleton @Provides fun provideTableDAO(database: Database): TableDAO = database.TableDao() }
Room的database.TableDao()每次调用返回的是同一个实例,只要Database是单例,TableDAO就会保持单例。
内容的提问来源于stack exchange,提问作者Gabriel Santiago

