理解dagger2我认为将其关键的几个注解理解了,基本原理了解就能应付日常开发,遇到不懂的问题在网上查找资料基本就无大碍
@inject
修饰成员,成员类型不能为私有 //该成员会被dagger框架注入进来
修饰构造方法 //该类会被dagger框架提供注入
class A {
@Inject
B b;
}
class B {
@Inject
public B(){
}
}
@Component
如上inject,一个注入,一个被注入,怎么把他们联系到一块就是Component,该接口里面的inject方法提供注入;dagger框架扫描到Component时,会自动搜索其内部为@Inject属性的成员和构造器,然后生成实例并注入
它由两个属性module和dependency,前者可以用于提供@provide实例,后者可以依赖其他Component
@Component
interface AComponet{
A injectA(A a);
}
@Component.Builder @BindsInstance
在我们定义Component接口时,其中提供的实例可能需要一些参数,如提供SharedPreference需要Context参数,这个时候可以用@Component.Builder提供Context传递进去
自定义Component里面的Builder类;
@BindsInstance为这个Builder提供属性,注入一个属性,属性在其内部是可以互相作为参数使用的
@Component
interface AComponet{
A injectA(A a);
@Component.Builder
interface Builder{
@BindsInstance
Builder ctx(Context ctx);
AComponet build();
}
}
@Module
有时候我们需要注入的类来自于第三方,比如我们引入的框架Retrofit这种,我们不能够去Retrofit的构造方法添加@Inject注解,这个时候就需要Module注解,在Module内部,使用@Provides注解来提供第三方的实例生成,最后在Component里面将Module引入进来,如下:
@Module
public class RetrofitModule {
@Provides
Retrofit provideRetrofit() {
return new Retrofit.Builder().baseUrl(uir)
.addConverterFactory(CustomGsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();;
}
}
@Component(modules = RetrofitModule.class)
public interface AComponent {
A injectA(A a);
@Component.Builder
interface Builder{
@BindsInstance
Builder ctx(Context ctx);
AComponet build();
}
}
大致原理
以上@Inject和@Component注解创建好后,make project,dagger编译时注解会为我们生成一些辅助类,所以为了完成依赖注入,我们还需要手动调用生产类:
class A {
DaggerAComponet.builder()
.ctx(ctx)
.build()
.inject(this);
}
这样才完成了依赖注入,这里的关键就是dagger为我们生成的DaggerAComponet这个辅助类,可以看看这个类的里面的结构:
public final class DaggerAComponent implements AComponent {
private Context ctx;
private Provider<Retrofit> retrofitProvider;
这里提供依赖注入的参数,如果AComponent还有其他依赖的Component,也随这里注入进来
private DaggerAComponent(Context ctx) {
this.ctx = ctx;
initialize();
}
初始化方法,实现Component的module里面的方法,这个module内部的Provide会产生工厂类来提供,
private void initialize() {
this.retrofitProvider =
DoubleCheck.provider(RetrofitModule_ProvidesRetrofitModuleFactory.create());
}
这里就是我们手动调用inject,将我们需要的实例注入到A类中去
@Override
public void inject(A a) {
这里假设A需要retrofitProvider,而不是前文说的B
injectA(a, retrofitProvider);
}
private A injectA(A instance) {
A_MembersInjector也是dagger生成的辅助类,该辅助类会持有A所有需要Inject引用,
在通过以下的方法将实例注入进去
A_MembersInjector.injectRetrofit(instance, retrofitProvider.get());
return instance;
}
Builder属于Component的内部类,辅助Component生成需要参数
public static Builder builder() {
return new Builder();
}
public static final class Builder {
这里需要Context注入进来,也就是上面的ctx(ctx)注入进来的
private Context ctx;
public Builder ctx(Context ctx) {
this.ctx = Preconditions.checkNotNull(ctx);
return this;
}
private Builder() {}
build方法生成DaggerAComponent,他实现AComponent接口
public AComponent build() {
return new DaggerAComponent(ctx);
}
}
}
这个就是module模块提供的第三方注入,使用工厂类来提供,是一个单利模式
public final class RetrofitModule_ProvidesRetrofitModuleFactory implements Factory<Retrofit> {
private static final RetrofitModule_ProvidesRetrofitModuleFactory INSTANCE =
new RetrofitModule_ProvidesRetrofitModuleFactory();
@Override
public Retrofit get() {
return proxyProvidesLoginFragment();
}
public static RetrofitModule_ProvidesRetrofitModuleFactory create() {
return INSTANCE;
}
public static Retrofit proxyProvidesRetrofit() {
return Preconditions.checkNotNull(
直接使用RetrofitModule类调用静态方法,这也是module里面的实现方法如果有实体的必须是static
RetrofitModule.provideRetrofit(),
"Cannot return null from a non-@Nullable @Provides method");
}
}
以上注释中做了详细的解释,再次也做个文字总结:
dagger生成的DaggerComponent内部重点分为三大类:
1 内部类Builder,这块主要是构建需要传入进去用到的参数,也就是在Component中定义的builder
2 DaggerComponent如果使用了dependencies依赖,也需要通过Builder引入进去
3 DaggerComponent的module模块,在DaggerComponent的初始化函数initialize去实现,类型为Provider的T泛型,module中的每个方法都会生成一个工厂类Factory,在工厂类中使用提供该实例的注入
4 最后,在我们调用inject方法注入时,会将本类中所有Inject的成员给注入进去
@Scope @Singleton
自定义注解,单利,singleton只是局部单一,就是在自己的范围内单一,如果超过此范围会重新生成一个;如何保证全局单一就只有被提供的实例只发送一次调用
@Binds
修饰Module里面的方法,主要用于返回为接口,参数为具体实现的实例,用于接口实例绑定
Lazy 懒加载类
懒加载获取对象只有一个,第一次才会触发生成
开发中遇到的问题
Android开发中使用,mvp注入Presenter博主喜欢这么用:
- 父类如下:
IPresenter泛型设置在父类,@inject我们也在父类中注入;initDagger就是dagger的生成Component,注入当前类的代码,由子类根据自己确定类去实现
public abstract class BaseFragment<P extends IPresenter> extends Fragment{
@Inject
protected P presenter;
protected abstract void initDagger();
}
- 此IPresenter为一个接口,绑定具体某一个实现类,他们怎么对应呢,我通过Module类去规定接口绑定某个实例类,使用@Binds注解
@Module
public abstract class MineModule {
//接口绑定具体的实现类
@Binds
abstract MineBehavior.MineAction bindMinePresenter(MinePresenter presenter);
}
- 以上还不能实现接口和实现类的绑定,还需要一个具体类继承BaseFragment,在当前类的中指明哪个具体的Presenter,从而进行绑定,如下代码操作:
public class HomeFragment extends BaseFragment<HomeBehavior.HomeAction>{
@Override
protected void initDagger() {
HomeCompont compont = DaggerHomeCompont.builder()
.view(this)
.appComponent(getMyApp().getComponent())
.build();
compont.inject(this);
}
}
以上就实现了从接口到具体实现类presenter的绑定了,我们可以在HomeFragment中直接使用presenter;
以上代码当需求变更时出现问题
当我们有个新需求,例如要开发一个相同header标题栏的fragment,那可能我们需要从BaseFragment继承,实现一个CommonHeaderFragment,在这个类里面去实现相同标题栏header的逻辑;Android中这种很常见,做法也大致如此,问题是 其中的泛型如何处理,才能保证presenter还能被正确注入 ,博主摸索了许久,都会报错:
错误:
[Dagger/MissingBinding] com.p.IPresenter cannot be provided without an @Provides-annotated method.
com.p.IPresenter is injected at
com.ui.base.BaseFragment.presenter
com.fragment.MineFragment is injected at
com.dagger.component.MineComponet.inject(com.ui.fragment.MineFragment)
大致意思就是,基类定义的presenter没有提供一个针对于IPresenter的实现类,思前想后我最终实现类fragment提供了确定的类型的,只不过中间多继承了一级CommonHeaderFragment;问题就出在这儿,我们看看中间一级CommonHeaderFragment怎么写的,可能有各种各样的:
public abstract class CommonHeaderFragment<P extends IPresenter> extends BaseFragment{}
public abstract class CommonHeaderFragment<P extends IPresenter> extends BaseFragment<IPresenter>{}
public abstract class CommonHeaderFragment extends BaseFragment<IPresenter>{}
看出错误原因来没?
因为BaseFragment的IPresenter要根据CommonHeaderFragment中指定的实现类来确定去bind具体的实现类presenter,而我们CommonHeaderFragment根据没法指定啊,需要在他下面的子类才能指定;而以上的写法继承时BaseFragment都指明了类型还是喂IPresenter,这样肯定为找不到实现类,让你在provides一个;正确的方法就是继续给他泛型,由下面子类指定,如下操作:
public abstract class CommonHeaderFragment<P extends IPresenter> extends BaseFragment<P>{}
完美解决
-------------分割线2020.5.27-------------------
-------------分割线2020.5.27-------------------
以上注解能完成Android的日常工作开发,但是仍然需要编写dagger的模板类代码,如每个需要注入的类都要去调用DaggerxxCompont.builder()…inject()等等,为了简化这一操作,dagger开发了dagger-android框架,简化操作
dagger-android
同上,也讲解几个注解就可以使用了
Subcomponent
如activity中有BaseActivity,为他做一个subcomponent模块
@Subcomponent(modules = AndroidInjectionModule.class)
public interface BaseActivitySubComponent extends AndroidInjector<BaseActivity> {
每一个继承BaseActivity的Activity都共享同一个SubComponent
@Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder<BaseActivity>{
}
}
AndroidInjectionModule是dagger提供的类
Module与ContributesAndroidInjector
在module中编写所有Activity的模块,并把上面的Subcomponent引入进来
@Module(subcomponents = BaseActivitySubComponent.class)
abstract class AllActivityModule {
这是需要给所有activity提供的注入
@Singleton
@Provides
static NetApi provideApi(){
return NetApi.Factory.create().create(NetApi.class);
}
ContributesAndroidInjector是为MainActivity的提供生成module模块
MainModule也是MainActivity中需要被注入的对象
@ContributesAndroidInjector(modules = MainModule.class)
abstract MainActivity mainActivityInjector();
同上
@ContributesAndroidInjector(modules = TestModule.class)
abstract TestActivity testActivityInjector();
}
Component
在Component把Module引入进来即可
@Singleton
@Component(modules =
{AndroidInjectionModule.class,
AllActivityModule.class})
public interface AppComponent {
@Component.Builder
interface Builder{
@BindsInstance
Builder applicationCtx(Context context);
AppComponent build();
}
MyApp inject(MyApp app);
}
重写Application
public class MyApp extends Application implements HasActivityInjector {
@Inject
DispatchingAndroidInjector<Activity> dispatchingAndroidInjector;
@Override
public void onCreate() {
super.onCreate();
AppComponent component = DaggerAppComponent.builder().applicationCtx(this).build();
if(component instanceof DaggerAppComponent){
component.inject(this);
}
}
@Override
public AndroidInjector<Activity> activityInjector() {
return dispatchingAndroidInjector;
}
}
最后在BaseActivity里面只需要调用AndroidInjection.inject(this)即可,
有兴趣可以查看我的JetPack之App快速开发框架