dagger2学习笔记

dagger2 git仓库

理解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博主喜欢这么用:

  1. 父类如下:
    IPresenter泛型设置在父类,@inject我们也在父类中注入;initDagger就是dagger的生成Component,注入当前类的代码,由子类根据自己确定类去实现
public abstract class BaseFragment<P extends IPresenter> extends Fragment{
	@Inject
    protected P presenter;
    protected abstract void initDagger();
}
  1. 此IPresenter为一个接口,绑定具体某一个实现类,他们怎么对应呢,我通过Module类去规定接口绑定某个实例类,使用@Binds注解
@Module
public abstract class MineModule {
	//接口绑定具体的实现类
    @Binds
    abstract MineBehavior.MineAction bindMinePresenter(MinePresenter presenter);
}
  1. 以上还不能实现接口和实现类的绑定,还需要一个具体类继承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快速开发框架

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

帅气好男人_Jack

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值