目录
a.@Inject 依赖注入需求方-注入标示需要依赖提供方@Module&&@Provides提供的实例
1.Dagger2是什么?
Dagger2用于实现Android和Java的快速依赖注入器;Dagger2实现依赖注入降低代码的耦合度;
任何应用程序中最好的类都是那些真正有用的类:BarcodeDecoder、KoopaPhysicsEngine和AudioStreamer。这些类有依赖关系;可能是一个BarcodeCameraFinder, DefaultPhysicsEngine,和一个HttpStreamer。
相比之下,任何应用程序中最糟糕的类都是那些占用空间而根本不做太多工作的类:BarcodeDecoderFactory、CameraServiceLoader和MutableContextWrapper。这些类只是起一个粘合剂的作用,它们把感兴趣的类集合在了一起。
Dagger是这些工厂类的替代品。它允许你专注于有用的类。声明依赖项,指定如何满足它们,并发布应用程序。
通过构建标准的javax.inject注释(JSR-330),每个类都很容易测试。您不需要一堆样板文件就可以将RpcCreditCardService换成FakeCreditCardService。
依赖注入不仅仅是为了测试。它还使创建可重用、可交换的模块变得容易。您可以在所有应用程序中共享同一验证模块。您可以在开发期间运行DevLoggingModule,在生产中运行prologgingmodule,以便在各种情况下获得正确的行为。
2.Dagger2原理?
Dagger2通过借助Java注解功能,我们只负责声明实例对象和调用方法,将我们创建实例和实例间的依赖关系由Dagger2完成,Dagger2会生成工厂方法,将创建对象和对相间的依赖在工厂方法里完成,降低代码耦合度;
3.Dagger2如何使用?
3.1声明依赖
创建实体类,同时在构造函数添加@Inject注解,声明依赖关系;
class Thermosiphon implements Pump {
private final Heater heater;
//使用@Inject来注释Dagger用来创建类实例的构造函数。
@Inject
Thermosiphon(Heater heater) {
this.heater = heater;
}
@Override public void pump() {
if (heater.isHot()) {
System.out.println("=> => pumping => =>");
}
}
}
3.2实现依赖
通过添加注解Module和Provides实现依赖的实例 ;
@Module
class DripCoffeeModule {
@Provides static Heater provideHeater() {
return new ElectricHeater();
}
@Provides static Pump providePump(Thermosiphon pump) {
return pump;
}
}
3.3构建图谱-Dagger自动装载依赖
添加Component注解和需要装载的依赖;将Module中(实例对象)注入到@Inject注解的依赖(构造函数对象)中;
@Component(modules = DripCoffeeModule.class)
interface CoffeeShop {
CoffeeMaker maker();
}
3.1声明依赖
Dagger构造应用程序类的实例并满足它们的依赖性。它使用javax.inject.Inject注释来识别它需要的构造函数和字段。
使用@Inject来注释Dagger用来创建类实例的构造函数。当请求一个新实例时,Dagger将获得所需的参数值并调用这个构造函数。
package coffee;
import javax.inject.Inject;
class Thermosiphon implements Pump {
private final Heater heater;
//使用@Inject来注释Dagger用来创建类实例的构造函数。
@Inject
Thermosiphon(Heater heater) {
this.heater = heater;
}
@Override public void pump() {
if (heater.isHot()) {
System.out.println("=> => pumping => =>");
}
}
}
Dagger可以直接注入字段。在本例中,它获得了Heater字段对应Heater实例和Pump字段的Pump实例。
package coffee;
import dagger.Lazy;
import javax.inject.Inject;
class CoffeeMaker {
private final Lazy<Heater> heater; // Create a possibly costly heater only when we use it.
private final Pump pump;
//使用@Inject来注释Dagger用来创建类实例的构造函数。
@Inject CoffeeMaker(Lazy<Heater> heater, Pump pump) {
this.heater = heater;
this.pump = pump;
}
public void brew() {
heater.get().on();
pump.pump();
System.out.println(" [_]P coffee! [_]P ");
heater.get().off();
}
}
如果您的类有@Inject-annotated字段,但没有@Inject-annotated构造函数,Dagger将在请求时注入这些字段,但不会创建新实例。添加带有@Inject注释的无参数构造函数,以指示Dagger也可以创建实例。
Dagger也支持方法注入,尽管构造函数或字段注入通常是首选。
缺少@Inject注释的类不能由Dagger构造。
3.2实现依赖
认情况下,Dagger通过构造上述所请求类型的实例来满足每个依赖关系。当您请求一个CoffeeMaker时,它将通过调用new CoffeeMaker()并设置其注入字段来获得一个。
@Inject声明构造函数不起作用的情况
a.接口不能被实例化对象;
b.第三方类不能被注解;
c.必须配置可配置对象;
对于@Inject不够或不合适的情况,可以使用@Provides-annotated方法来满足依赖关系。方法的返回类型定义了它所满足的依赖项。
例如:当需要Heater时会调用provideHeater() 方法返回
@Provides static Heater provideHeater() {
return new ElectricHeater();
}
@Provide方法可以有自己的依赖项。当需要Pump时,它会返回一个Thermosiphon:
@Provides static Pump providePump(Thermosiphon pump) {
return pump;
}
所有@Provide方法必须属于一个模块(module)。这些类只是具有@Module注释的类。
@Module
class DripCoffeeModule {
@Provides static Heater provideHeater() {
return new ElectricHeater();
}
@Provides static Pump providePump(Thermosiphon pump) {
return pump;
}
}
命名规则:按照惯例,@Provide方法使用provide前缀命名,模块类使用Module后缀命名。
3.3构建图谱-Dagger自动装载依赖
通过@Inject和@Provides 注解类形成一个对象图,由它们的依赖项链接。像Application的主方法或Android下的Application这样的调用代码通过一组定义良好的根来访问该图。在Dagger 2中,该集合由一个接口定义,该接口的方法没有参数,并且返回所需的类型。通过将@Component注释应用于这样的接口并将模块类型传递给模块参数,Dagger 2然后完全生成该契约的实现。
@Component(modules = DripCoffeeModule.class)
interface CoffeeShop {
CoffeeMaker maker();
}
注意:写完Component以后,点击开发工具Build-Rebuild Project,会生成实现注解Component的接口;
Dagger生成的代码如下:
CoffeeMaker_Factory:生成CoffeeMaker工厂方法;
Thermosiphon_Factory:生成Thermosiphon工厂方法;
DaggerCoffeeApp_CoffeeShop:实现注解Component定义接口,实现Module和依赖桥梁;
DripCoffeeModule_ProvideHeaterFactory:生成DripCoffeeModule工厂方法;
public final class DaggerCoffeeApp_CoffeeShop implements CoffeeApp.CoffeeShop {
private Provider<Heater> provideHeaterProvider;
private DaggerCoffeeApp_CoffeeShop(Builder builder) {
initialize(builder);
}
private Thermosiphon getThermosiphon() {
return new Thermosiphon(provideHeaterProvider.get());
}
//初始化创建Module同时返回提供Provider<Heater>
@SuppressWarnings("unchecked")
private void initialize(final Builder builder) {
this.provideHeaterProvider =
DoubleCheck.provider(
DripCoffeeModule_ProvideHeaterFactory.create(builder.dripCoffeeModule));
}
//调用Module提供对象注意依赖创建对象
@Override
public CoffeeMaker maker() {
return new CoffeeMaker(DoubleCheck.lazy(provideHeaterProvider), getThermosiphon());
}
}
实现具有与带Dagger前缀的接口相同的名称。通过调用该实现上的builder()方法获得实例,并使用返回的builder设置依赖项和构建 build()
新实例。
CoffeeShop coffeeShop = DaggerCoffeeShop.builder()
.dripCoffeeModule(new DripCoffeeModule()) //手动添加装载Module
.build();
CoffeeShop coffeeShop = DaggerCoffeeShop.builder()
.build();//自动创建DripCoffeeModule对象装载
注意:如果@Component不是顶级类型,则生成的组件的名称将包括其封闭类型的名称,并使用下划线连接。例如,这段代码:
class Foo {
static class Bar {
@Component
interface BazComponent {}
}
}
生成的组件名称为DaggerFoo_Bar_BazComponent
任何具有可访问的默认构造函数的模块都可以被省略,因为如果没有设置任何构造函数,构建器将自动构造一个实例。对于@Provides方法都是静态的模块,实现根本不需要实例。如果所有依赖项都可以在用户不创建依赖项实例的情况下构造,那么生成的实现也将有一个create()方法,该方法可用于获取新实例,而不必处理构建器。
CoffeeShop coffeeShop = DaggerCoffeeShop.create();
public final class DaggerCoffeeApp_CoffeeShop implements CoffeeApp.CoffeeShop {
public static CoffeeApp.CoffeeShop create() {
return new Builder().build();
}
}
public final class DaggerCoffeeApp_CoffeeShop implements CoffeeApp.CoffeeShop {
public static CoffeeApp.CoffeeShop create() {
return new Builder().build();
}
}
现在,我们的CoffeeApp可以简单地使用由dagger生成的CoffeeShop实现来获得一个完全注入的CoffeeMaker。
public class CoffeeApp {
public static void main(String[] args) {
CoffeeShop coffeeShop = DaggerCoffeeShop.create();
coffeeShop.maker().brew();
}
}
现在已经构建了图并注入了入口点,我们运行咖啡机应用程序。
$ java -cp ... coffee.CoffeeApp
~ ~ ~ heating ~ ~ ~
=> => pumping => =>
[_]P coffee! [_]P
3.4Dagger2常用关键字说明
a.@Inject 依赖注入需求方-注入标示需要依赖提供方@Module&&@Provides提供的实例
可以表示构造函数,方法,字段需要注入依赖项;
在构造方法上使用 @Inject 时,其参数在运行时由配置好的IoC容器提供。比如,在下面的代码中,运行时调用Thermosiphon类的构造方法时,IoC 容器会注入其参数Heater对象。
class Thermosiphon implements Pump {
private final Heater heater;
//使用@Inject来注释Dagger用来创建类实例的构造函数。
@Inject
Thermosiphon(Heater heater) {
this.heater = heater;
}
@Override public void pump() {
if (heater.isHot()) {
System.out.println("=> => pumping => =>");
}
}
}
b.@Provides 依赖的提供方
提供需要@Inject注入的实例对象;
c.@Module 依赖提供方管理集合
Module其实是一个简单的工厂模式,Module 里面的方法都是创建相应类实例的方法。
@Module
class DripCoffeeModule {
@Provides static Heater provideHeater() {
return new ElectricHeater();
}
@Provides static Pump providePump(Thermosiphon pump) {
return pump;
}
}
d.Component
相当于@Inject和@Module之间的桥梁,从Module中获取实例注入到Inject;
@Component 对接口或抽象类进行注释,以便从一组Modules()生成完全形成的、注入依赖关系的实现。生成标示@Component类的类型名称将以Dagger作为前缀。例如,@Component接口MyComponent{…}将生成一个名为DaggerMyComponent的实现。
@Component(modules = DripCoffeeModule.class)
interface CoffeeShop {
CoffeeMaker maker();
}
3.5高级用法
a.在Module中构造方法中需要参数时
在Module中添加依赖即可,provideRetrofit(OkHttpClient okhttpClient)需要okhttpClient对象,只需要添加返回okhttpClient对象的依赖即可provideOkHttpClient(),不需要在provideRetrofit()方法中单独创建okhttpClient;
@Module
public class NetModule {
@Provides
public OkHttpClient provideOkHttpClient() {
OkHttpClient okhttpClient = new OkHttpClient;
return okhttpClient;
}
@Provides
public Retrofit provideRetrofit(OkHttpClient okhttpClient) {
Retrofit retrofit = new Retrofit.Builder()
.client(okhttpClient);
return retrofit;
}
@Provides
@Singleton
public ApiService provideApiService(Retrofit retrofit){
return retrofit.create(ApiService.class);
}
}
b.模块间的依赖
@Module(includes = PumpModule.class) //includes引用
public class DripCoffeeModule {
@Provides
@Singleton
public Heater provideHeater(){
return new ElectricHeater();
}
}
先在DripCoffeeModule找Heater对象,要是没有则去PumpModule中找Heater对象,如果还是没有,那么GG,抛出异常
c.组件可以添加多个模块
@Component(modules = { DripCoffeeModule.class,PumpModule.class })
public interface CoffeeShop {
CoffeeMaker maker();
CoffeeMaker makertest();
}
d.组件可以添加其他组件的依赖
@Component(modules = SplashModule.class,dependencies = NetComponent.class)
public interface SplashComponent {
void inject(SplashActivity splashActivity);
}
e.@Name
用@Name根据需要定义两个Provider对象,在使用时根据需要使用不用的对象;
@Module(includes = PumpModule.class) //includes引用
public class DripCoffeeModule {
//根据需要声明同一个类的两个不同对象
@Provides @Named("dev") static Heater provideDev() {
return new ElectricHeater(70);
}
@Provides @Named("release") static Heater provideRelease() {
return new ElectricHeater(93);
}
}
class ExpensiveCoffeeMaker {
@Inject @Named("water") Heater waterHeater;
@Inject @Named("hot plate") Heater hotPlateHeater;
...
}
f.Singletons和Scoped绑定
用@Singleton同时注释@Provides注释方法或注入的类。该图将为其所有客户端使用该值的单个实例。
//方法
@Provides @Singleton static Heater provideHeater() {
return new ElectricHeater();
}
//类
@Singleton
class CoffeeMaker {
...
}
由于Dagger 2将图中的作用域实例与组件实现的实例相关联,因此组件本身需要声明它们打算表示哪个作用域。例如,在同一个组件中使用@Singleton绑定和@RequestScoped绑定没有任何意义,因为这些作用域具有不同的生命周期,因此必须存在于具有不同生命周期的组件中。要声明组件与给定范围相关联,只需将范围注释应用于组件接口。
@Component(modules = DripCoffeeModule.class)
@Singleton
interface CoffeeShop {
CoffeeMaker maker();
}
组件可以应用多个范围注释。这声明它们都是同一作用域的别名,因此该组件可以包含作用域绑定和它声明的任何作用域。
在同一个Activity/Fragment使用注解的单例@Singleton的实例,获取的同一个实例;
@Inject
Heater heaterone;
@Inject
Heater heatertwo;
再多个Activity/Fragment使用注解的单例@Singleton的实例,获取的是不同的实例,每个Activity/Fragment都会new一个新的Heater;Module新建同时导致Heater在不同Activity/Fragment中新建;
public CoffeeApp.CoffeeShop build() {
if (dripCoffeeModule == null) {
this.dripCoffeeModule = new DripCoffeeModule();
}
return new DaggerCoffeeApp_CoffeeShop(this);
}
g.实现全局单例
Model单例
@Module
public class NetModule {
@Provides
@Singleton
public OkHttpClient provideOkHttpClient() {
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
loggingInterceptor.setLevel(BuildConfig.DEBUG ? HttpLoggingInterceptor.Level.BODY : HttpLoggingInterceptor.Level.NONE);
OkHttpClient okhttpClient = new OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.connectTimeout(20, TimeUnit.SECONDS)
.readTimeout(20,TimeUnit.SECONDS)
.addInterceptor(loggingInterceptor)
.build();
return okhttpClient;
}
@Provides
@Singleton
public Retrofit provideRetrofit(OkHttpClient okhttpClient) {
Retrofit retrofit = new Retrofit.Builder()
.client(okhttpClient)
.baseUrl("http://static.owspace.com/")
.addConverterFactory(StringConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create(EntityUtils.gson))//
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
return retrofit;
}
@Provides
@Singleton
public ApiService provideApiService(Retrofit retrofit){
return retrofit.create(ApiService.class);
}
}
Component单例
@Component(modules = NetModule.class)
@Singleton
public interface NetComponent {
ApiService getApiService();
OkHttpClient getOkHttp();
Retrofit getRetrofit();
}
Application创建单例netComponent
public class OwspaceApplication extends Application{
private static OwspaceApplication instance;
public static OwspaceApplication get(Context context){
return (OwspaceApplication)context.getApplicationContext();
}
private NetComponent netComponent;
@Override
public void onCreate() {
super.onCreate();
instance = this;
initLogger();
initNet();
initDatabase();
initTypeFace();
}
private void initTypeFace() {
CalligraphyConfig calligraphyConfig =new CalligraphyConfig.Builder()
.setDefaultFontPath("fonts/PMingLiU.ttf")
.setFontAttrId(R.attr.fontPath)
.build();
CalligraphyConfig.initDefault(calligraphyConfig);
}
private void initLogger(){
Logger.addLogAdapter(new AndroidLogAdapter());
// LogLevel logLevel;
// if (!BuildConfig.API_ENV){
// logLevel = LogLevel.FULL;
// }else{
// logLevel = LogLevel.NONE;
// }
// Logger.addLogAdapter();
// Logger.init("GithubOwspace") // default PRETTYLOGGER or use just init()
// .methodCount(3) // default 2
// .logLevel(logLevel) ; // default LogLevel.FULL
}
private void initNet(){
netComponent = DaggerNetComponent.builder()
.netModule(new NetModule())
.build();
}
private void initDatabase(){
}
public NetComponent getNetComponent() {
return netComponent;
}
public static OwspaceApplication getInstance() {
return instance;
}
}
在其他需要netComponent组件的地方从Application获取netComponent实例;
DaggerDetailComponent.builder()
.netComponent(OwspaceApplication.get(this).getNetComponent())
.detailModule(new DetailModule(this))
.build()
.inject(this);
4.Dagger使用注意事项
1.Provide 如果是单例模式 对应的Compnent 也要是单例模式
2.inject(Activity act) 不能放父类
3.即使使用了单利模式,在不同的Activity 对象还是不一样的
4.依赖component, component之间的Scoped 不能相同
5.子类component 依赖父类的component ,子类component的Scoped 要小于父类的Scoped,Singleton的级别是Application
6.多个Moudle 之间不能提供相同的对象实例
7.Moudle 中使用了自定义的Scoped 那么对应的Compnent 使用同样的Scoped
5.其他
5.1Lazy注入
有时需要延迟实例化对象。对于任何绑定T,都可以创建一个Lazy,它延迟实例化,直到第一次调用Lazy的get()方法。如果T是单例的,那么Lazy将是ObjectGraph中所有注入的相同实例。否则,每个注入站点将获得自己的惰性实例。不管怎样,对任何给定的Lazy实例的后续调用都将返回相同的T底层实例。
class GrindingCoffeeMaker {
@Inject Lazy<Grinder> lazyGrinder;
public void brew() {
while (needsGrinding()) {
// Grinder created once on first call to .get() and cached.
lazyGrinder.get().grind();
}
}
}
5.2Provider注入
有时需要返回多个实例,而不是只注入一个值。虽然您有几个选项(工厂、构建器等),但其中一个选项是注入提供者,而不仅仅是T。如果绑定逻辑是一个@Inject构造函数,那么将创建一个新实例,但是@Provides方法没有这样的保证。
class BigCoffeeMaker {
@Inject Provider<Filter> filterProvider;
public void brew(int numberOfPots) {
...
for (int p = 0; p < numberOfPots; p++) {
maker.addFilter(filterProvider.get()); //new filter every time. 每次新建Filter
maker.addCoffee(...);
maker.percolate();
...
}
}
}
注意:注入Provider有可能产生混乱的代码,并且可能是图中作用域错误或结构错误的对象的设计气味。通常,您希望使用工厂或惰性的,或者重新组织代码的生存期和结构,以便能够直接注入T。一个常见的用法是,您必须使用与对象的自然寿命不一致的遗留体系结构(例如,servlet在设计上是单例的,但仅在请求特定的数据上下文中有效)。
5.3Subcomponent
这个是系统提供的一个Component,当使用Subcomponent,那么默认会依赖Component
例如
@Subcomponent(modules = TestSubModule.class)
public interface TestSubComponent {
void inject(MainActivity activity);
}
@Component(modules = {MainModule.class})
public interface MainConponent {
TestSubComponent add(TestSubModule module);
}
在TestSubComponent
中 我void inject(MainActivity activity);
,便是这个桥梁,我是要注入到MainActivity
,但是dagger 并不会给我生成一个Dagger开头的DaggerTestSubComponent 这个类,如果我想使用TestSubModule.class
里面提供的对象,依然还是使用DaggerMainConponent
例如
DaggerMainConponent
.builder()
.mainModule(new MainModule())
.build()
.add(new TestSubModule())
.inject(this);
可以看到这里有一个add的方法,真是我在MainConponent
添加的TestSubComponent add(TestSubModule module);
参考:
https://www.jianshu.com/p/2cd491f0da01
https://dagger.dev/users-guide
测试代码
https://github.com/google/dagger/tree/master/examples/simple/src/main/java/coffee