26
2017
09

初探Dagger2遇到的小坑

编译没错,为什么就是注入不了

//非常简单的一个活动
class MainActivity : AppCompatActivity() {
    @Inject
    lateinit var a: String//待注入的变量

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        DaggerMainComponent.builder()
                .mainModule(MainModule())
                .build()
                .inject(this)//注入

        Log.d("=============", a)//打印看看结果
    }
}

//提供字符串的module
@Module
class MainModule {
    @Provides
    fun provideString(): String = "123"

}

@Component(modules = arrayOf(MainModule::class))
interface MainComponent {
    fun inject(activity: Activity)
}

貌似操作都没错呀,然后结果如下:
Error: lateinit property a has not been initialized

所以这是为什么呢,还是看看Dagger生成的Inject方法是怎么工作的吧,说不定从这能找到答案

@Override
  public void inject(Activity activity) {
    MembersInjectors.<Activity>noOp().injectMembers(activity);
  }

上面就是生成的inject方法了,显然要往injectMembers里面挖

//跳进来发现这是个空方法,那就一定有他的实现类了
void injectMembers(T instance);

//========== 实现1 ===========
@Override
  public void injectMembers(MainActivity instance) {
    if (instance == null) {
      throw new NullPointerException("Cannot inject members into a null reference");
    }
    instance.a = aProvider.get();
  }

//========== 实现2 ===========
@Override public void injectMembers(Object instance) {
      checkNotNull(instance);
    }

可以看到,实现1中就对我们要注入的字符串a进行了赋值,而实现2中只是检查了一下目标类是不是空的。而实现1的对象是这样(<Activity>noOp())才能拿到的

//正确
MembersInjectors.<MainActivity>noOp().injectMembers(activity);
//错误
MembersInjectors.<Activity>noOp().injectMembers(activity);

也就是说我们的inject方法参数写错了,只要吧MainComopnentinject的参数改为MainActivity就好了

@Component(modules = arrayOf(MainModule::class)) interface MainComponent {
    fun inject(activity: MainActivity)
}

再跑一下:=============: 123

为什么我的单例会报错

@Module
class MainModule {
    @Provides
    @Singleton//单例??
    fun provideString(): String = "123"
}

把Module中的provide方法加上@Singleton注解后就是单例了?,编译一下…

Error:Execution failed for task ‘:app:kaptDebugKotlin’.
Internal compiler error. See log for more details

再看看Gradle Console中的日志:

Component (unscoped) may not reference scoped bindings

原来,对应的Comopnent要加上同样的标记才可以

@Singleton
@Component(modules = arrayOf(MainModule::class))
interface MainComponent {
    fun inject(activity: MainActivity)
}

再跑一下,编译通过了

再来看看@Singleton

@Scope
@Documented
@Retention(RUNTIME)
public @interface Singleton {}

原来Singleton是一种作用域注解,Component和Module中的provide方法作用域注解相同才能实现单例

什么时候要在构建Component的时候传入Module

之前是这样写的,中间传入了Module

DaggerMainComponent.builder()
                .mainModule(MainModule())
                .build()
                .inject(this)

把它去掉,照样工作

DaggerMainComponent.builder()
                .build()
                .inject(this)

再去看看Dagger生成的代码

public MainComponent build() {
      if (mainModule == null) {
        this.mainModule = new MainModule();
      }
      return new DaggerMainComponent(this);
    }

原来,build方法会自动生成Module,所以可以不传入,拿什么时候才要传入Module呢?修改一下Module,在构造方法中传入参数

@Module
class MainModule(val activity: MainActivity) {
    @Provides
    @Singleton
    fun provideString(): String = "123"
}

编译之后再来看看生成的方法

public MainComponent build() {
      if (mainModule == null) {
        throw new IllegalStateException(MainModule.class.getCanonicalName() + " must be set");
      }
      return new DaggerMainComponent(this);
    }

卧槽,变了…当Module有参数的时候就需要手动传入了,否则会抛异常。。

上一篇:#字节流转文件 下一篇:朋友圈评论回复的两种实现方式