我正在使用Dagger2构建一个Android应用程序,如果可以的话,它支持蓝牙。我想用Dagger注入BluetoothAdapter依赖项。
我知道用dagger注入null值的一种方法,即用@Nullable注释模块中的provider方法、组件中的依赖声明和注入站点中的参数。但是为了更清楚地说明BluetoothAdapter是一个可选的依赖项(应用程序的其余部分也可以在没有BT的情况下工作,并且也应该在模拟器上工作),我想声明依赖项为Optional<BluetoothAdapter>,如in the official docs所述。
我的模块中有一个provider方法:

@Provides
static BluetoothAdapter providesBluetoothAdapter(MainApplication application) {
    ...
}

以及组件中的相应声明:
BluetoothAdapter bluetoothAdapter();

按照说明,我将注射部位改为Optional<BluetoothAdapter>,使我的模块抽象,并在模块中添加了以下抽象方法:
@BindsOptionalOf abstract BluetoothAdapter optionalBluetoothAdapter();

但是,在模拟器上运行时,它仍然失败,并出现java.lang.NullPointerException: Cannot return null from a non-@Nullable @Provides method异常。
这时,我想我可能误解了@BindsOptionalOf的目的,并从组件中删除了BluetoothAdapter bluetoothAdapter();声明,以查看它是否取决于是否在组件中声明了依赖项。还是不行。
我错过了什么?既然用@BindsOptionalOf注释的方法必须是抽象的,那么有可能完成我对可选绑定的尝试吗?

最佳答案

你用@bindsoptional的方式并不完全符合它的用途。@bindsoptionalof最适用于编译时可能存在或不存在的绑定,因此最适用于可重用模块。
假设您正在构建一个可重用的库:

@Module public abstract class BluetoothAdapterModule {
  @Provides
  static BluetoothAdapter providesBluetoothAdapter(MainApplication application) {
    ...
  }
}

@Module public interface FooModule {
  // This consumes BluetoothAdapter when it happens to be present.
  @BindsOptionalOf BluetoothAdapter bindOptionalBluetoothAdapter();

  @Binds Bar bindBar(Foo foo);
}

这里,当foomodule和bluetoothadaptermodule绑定到同一个组件中时,bluetoothadapter将有一个绑定,因此可以注入Optional<BluetoothAdapter>,并且get将返回一个bluetoothadapter。在一个单独的组件中,如果您没有包含Bluetooth适配器模块,则Bluetooth适配器没有绑定。如果直接从foo(包括@Nullable BluetoothAdapter)依赖bluetoothadapter,则编译将失败。但是,使用@BindsOptionalOf,您可以依赖于Optional<BluetoothAdapter>的注入,当包含Bluetooth适配器模块时,该模块存在,而当排除时则不存在。
要在运行时反映蓝牙的存在或不存在,您需要使用@Nullable,或者编写一个单独的可注入的BluetoothAdapterHolder。您还可以显式绑定Optional<BluetoothAdapter>,并使用预期的存在或不存在特征显式创建它;此时,您不会使用@bindsoptionalof,因为您不依赖匕首来反映存在/不存在:您自己控制它。

10-04 19:12