【问题标题】:Hilt Injection not working with BroadcastReceiverHilt 注入不适用于 BroadcastReceiver
【发布时间】:2020-06-12 00:38:52
【问题描述】:

BroadcastReceiver 中使用 Hilt 的依赖注入不起作用。我尝试使用来自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

【问题讨论】:

    标签: android android-jetpack dagger-hilt


    【解决方案1】:

    更新:根据issue,该问题应该在Dagger Hilt 的2.29.1 版本中得到修复。因此,只需使用版本2.29.1-alpha 或更高版本。不要忘记更新hilt-android-gradle-plugin 版本as well


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

    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
    
        ...
      }
    }
    

    【讨论】:

    • 很好的发现!!我不敢相信这在发布之前没有被 Google 发现......
    • 一个相关主题@ValeriyKatkov...您在dagger-hilt 标签中有足够的声誉来支持同义词。你可以在这里做吗:stackoverflow.com/tags/dagger-hilt/synonyms?似乎没有版主关心给hilt相关标签带来秩序,所以我正在尝试自己协调。
    • @Valeriy Katkov 我使用了提供的解决方法,非常感谢,但 BroadcastReceiver 仍然不会在我的应用程序中调用。还有什么需要做的吗? This 是我迄今为止所做的。
    • @Valeriy Katkov 我也面临同样的问题。我尝试了您建议的解决方法以及升级了 hilt gradle 插件版本,但仍然没有运气。你能帮忙吗?
    • 答案because it actually happens inside onReceive() method of the generated parent class 中的那一小部分就是我找出问题所需要的。接得好。有谁知道他们为什么不让它注入生成的构造函数?!
    【解决方案2】:

    我认为 hilt 目前不支持 BroadcastReceiver。在https://dagger.dev/hilt/android-entry-point

    注意:Hilt 目前仅支持扩展 ComponentActivity 的 Activity 和扩展 androidx 库 Fragment 的 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 或最新版本。有关releases 的更多信息。

    修复 #1918:通过 @AndroidEntryPoint 转换支持 BroadcastReceiver。 (ede018b)

    【讨论】:

    【解决方案3】:

    使用稳定版本的 Hilt。你可以很容易地注入任何这样的变量

    @AndroidEntryPoint
    public class SMSReceiver extends BroadcastReceiver {
        @Inject
        public DataManager dataManager;
    
        @Override
        public void onReceive(final Context context, Intent intent) {
        }
    }
    

    【讨论】:

    • 这应该是公认的答案
    【解决方案4】:

    目前,由于 Hilt 错误,您不能将 @AndroidEntryPoint 用于 BroadcastReceiver。 相反,您使用这样的解决方法逻辑

    如果你想在 BroadcastReceiver 中注入 SomeModule

    1. 创建新的入口点

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

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

    【讨论】:

      【解决方案5】:

      刀柄现在稳定了。现在很简单:

      @AndroidEntryPoint
      class MyAppWidget : AppWidgetProvider() {
      
      @Inject
      lateinit var getMyUseCase: MyUseCase
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-04-16
        • 1970-01-01
        • 2021-09-17
        • 2021-05-31
        • 1970-01-01
        • 1970-01-01
        • 2021-06-24
        相关资源
        最近更新 更多