Hilt注入在BroadcastReceiver中无法工作

25

使用Hilt在BroadcastReceiver内进行依赖注入无法工作。我试图通过从MainActivity触发一个闹钟来调用BroadcastReceiver,但是出现了UninitializedPropertyAccessException。根据文档,只需将@AndroidEntryPoint注释添加到接收器中即可,但事实并非如此。

示例代码:

App.kt:

@HiltAndroidApp
class App: Application() {
    override fun onCreate() {
        super.onCreate()
        Log.d(App::class.simpleName, "onCreate: Application")
    }
}

TestHiltInjection.kt:

class TestHiltInjection @Inject constructor() {

    operator fun invoke() {
        Log.d(TestHiltInjection::class.java.simpleName, "invoke called.")
    }
}

HiltBroadcastReceiver.kt:

@AndroidEntryPoint
class HiltBroadcastReceiver : BroadcastReceiver() {

    @Inject lateinit var testHiltInjection: TestHiltInjection

    override fun onReceive(context: Context?, intent: Intent?) {
        testHiltInjection()
    }
}

MainActivity.kt:

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val manager = getSystemService(Context.ALARM_SERVICE) as AlarmManager

        val pending = PendingIntent
            .getBroadcast(this, 0, Intent(this, HiltBroadcastReceiver::class.java), 0)
        manager.setInexactRepeating(
            AlarmManager.ELAPSED_REALTIME_WAKEUP,
            15000, 15000,
            pending
        )
    }
}

输出 Logcat:

kotlin.UninitializedPropertyAccessException: lateinit property testHiltInjection has not been initialized

更新

问题已在2.29.1版本中解决,只需使用@AndroidEntryPoint即可。

5个回答

40

更新:根据问题,这个问题应该在Dagger Hilt的版本2.29.1中得到了解决。所以,请使用2.29.1-alpha或更高版本。不要忘记同时更新hilt-android-gradle-plugin版本


原始回答:有一个GitHub问题和一个解决方法。似乎注入不起作用,因为它实际上发生在生成的父类的onReceive()方法内部。问题是你不能调用超级方法,因为它是抽象的。但是你可以创建一个简单的包装类来解决这个问题:

abstract class HiltBroadcastReceiver : BroadcastReceiver() {
  @CallSuper
  override fun onReceive(context: Context, intent: Intent) {}
}

@AndroidEntryPoint
class MyBroadcastReceiver : HiltBroadcastReceiver() {
  @Inject lateinit var testHiltInjection: TestHiltInjection

  override fun onReceive(context: Context?, intent: Intent?) {
    super.onReceive(context, intent) // <-- it's the trick

    ...
  }
}

2
好发现!我简直不敢相信在发布之前 Google 没有发现这个问题... - Seven
1
一个相关的主题@ValeriyKatkov...你在dagger-hilt标签中有足够的声望来投票支持同义词。你能在这里做到吗:https://stackoverflow.com/tags/dagger-hilt/synonyms?似乎没有任何管理员关心将与`hilt`相关的标签整理好,所以我正在尝试自己协调。 - Bartek Lipinski
@Valeriy Katkov 我已经使用了您提供的解决方法,非常感谢,但是在我的应用程序中BroadcastReceiver仍然无法调用。还有其他需要做的事情吗?这是我迄今为止所做的链接 - BWappsAndmore
@Valeriy Katkov,我也遇到了同样的问题。我尝试了你提出的解决方法,还升级了Hilt Gradle插件版本,但是仍然没有解决。请问你可以帮忙吗? - Srushti
答案中的那个小细节“因为它实际上发生在生成的父类的onReceive()方法内部”是我需要找出问题的全部。干得好。有人知道为什么他们没有让它注入到生成的构造函数中吗? - Ace

7
我认为Hilt目前不支持BroadcastReceiver。在https://dagger.dev/hilt/android-entry-point上说:
提示:Hilt当前仅支持扩展ComponentActivity的活动和扩展androidx库Fragment的片段,而不是Android平台中(现已弃用)的Fragment。
内部工作原理:Hilt为标注了@AndroidEntryPoint的组件(Activity、Fragment、BroadcastReceiver等)创建抽象类,并且在字节码转换中,你的BroadcastReceiver将扩展生成的类作为基类。在基类的onReceive方法中,对象被注入。但是,在生成的字节码类中,父类的onReceive方法没有被调用。这就是为什么你的对象没有被注入的原因。
要进行测试,请在HiltBroadcastReceiver类的testHiltInjection()之前添加以下代码。顺便说一句,它仍处于Alpha模式。
((HiltBroadcastReceiver_GeneratedInjector) BroadcastReceiverComponentManager.generatedComponent(context)).injectHiltBroadcastReceiver(UnsafeCasts.<HiltBroadcastReceiver>unsafeCast(this));

更新: 现在问题已在版本 2.29.1-alpha 中修复。另外,请不要忘记将 hilt-android-gradle-plugin 版本升级至 2.29.1-alpha 或更新版本。更多信息请查看发行说明

修复 #1918:支持使用 @AndroidEntryPoint 转换的 BroadcastReceiver。(ede018b)


它运行得很好。是的,我知道它处于alpha模式,但想法是测试并报告错误。此外,在文档中,他们没有明确说明BroadcastReceiver不受支持。 - cmpeguerog
不错的解决方法。看起来像是 Hilt 的一个 bug,文档确实说支持 BroadcastReceiver。 https://developer.android.com/training/dependency-injection/hilt-android#inject-interfaces - Frank Harper
我在他们的Github上开了一个问题。 https://github.com/google/dagger/issues/1918 - Frank Harper
1
@FrankHarper 这里有一个不错的解决方法,可以在你的Github问题页面上找到:https://github.com/google/dagger/issues/1918#issuecomment-644057247 - cmpeguerog

6
使用稳定版本的Hilt,您可以轻松地注入任何变量,如下所示。
@AndroidEntryPoint
public class SMSReceiver extends BroadcastReceiver {
    @Inject
    public DataManager dataManager;

    @Override
    public void onReceive(final Context context, Intent intent) {
    }
}

1
这应该是被接受的答案。 - Olivier Payen

-1

Hilt现在已经稳定。现在它就像这样简单:

@AndroidEntryPoint
class MyAppWidget : AppWidgetProvider() {

@Inject
lateinit var getMyUseCase: MyUseCase

-1

目前,由于Hilt的错误,您无法在BroadcastReceiver中使用@AndroidEntryPoint。 相反,您可以使用以下解决逻辑:

如果您想要在BroadcastReceiver中注入SomeModule,则可以这样做。

  1. 创建新的EntryPoint

    @EntryPoint
    @InstallIn(ApplicationComponent::class)
    interface MyEntryPoint {
        fun getSomeModule: SomeModule
    }
    
  2. 获取EntryPoint并使用它

    class MyBroadcastReceiver : BroadcastReceiver() {
       override fun onReceive(context: Context?, intent: Intent?) {
        val someModule : SomeModule =
          EntryPoints.get(context?.applicationContext, MyEntryPoint::class.java)
       .getSomeModule()
      }
    }
    

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接