我们就继续用前面的例子开始讲了,如果我使用了两个对象:
class CoffeeMaker {
@Inject
Heater heater;
@Inject
Heater heater2;
CoffeeMaker() {
CoffeeComponent component = DaggerCoffeeComponent.create();
component.inject(this);
}
public void brew() {
Log.e("@@@", "heater :" + heater.toString());
Log.e("@@@", "heater2 :" + heater2.toString());
}
}
我们的执行结果是:
heater :com.charon.stplayer.test.ElectricHeater@2fea2fd
heater2 :com.charon.stplayer.test.ElectricHeater@8c2abf2
很显然这是两个不同的对象。 我现在想让该Heater
是单例呢?
- 首先,在
CoffeeModule
里的provideHeater()
方法前面添加Singleton
@Module
public class CoffeeModule {
@Singleton
@Provides
Heater provideHeater() {
return new ElectricHeater();
}
....
}
- 然后再
CoffeeComponent
里的接口上面添加@Singleton
注释
@Singleton
@Component(modules = CoffeeModule.class)
public interface CoffeeComponent {
void inject(CoffeeMaker maker);
Heater provideHeater();
Pump providePump();
Ice provideIce();
IceBox provideIceBox();
@Type("normal")
Milk provideNormalMilk();
@Type("shuiguo")
Milk provideShuiGuoMilk();
String provideString();
}
注意Modle
是在方法上面添加Singleton
,而Component
是在接口上面添加Singleton
。
好了,再执行以下:
heater :com.charon.stplayer.test.ElectricHeater@2fea2fd
heater2 :com.charon.stplayer.test.ElectricHeater@2fea2fd
看,它是同一个对象了。
在Dagger2
里面还有有一个注解@Scope
也是用来实现单例的,其实它和Singleton
差不多,Singleton
是它的实现。用法也基本一样,只是换一个名字。
如果你想控制@Inject
构造器类被实例化或provider
方法被调用的的次数,且不用保证某个component
或subcomponent
在生命周期中使用同一个实例,就可以使用@Reusable
作用域了。@Reusable
作用域绑定,不像其他作用域绑定一样需要关联一个component
,它不会关联任何component
,相反真正要用这个绑定的component
都会缓存这个返回值或对象实例。
这就意味着,如果你用@Reusable
为component
指定module
,且只有一个subcomponent
会用到这个绑定,那么只有这个subcomponent
会缓存这个绑定对象。如果两个subcomponent
祖先没有共享一个绑定,那么每个subcomponent
会各自缓存自己的对象。如果一个component
的祖先已经缓存了绑定对象,那么subcomponent
会重用这个对象。
由于无法保证component
只会调用一次绑定,@Reusable
绑定可能会返回不确定的对象,这个时候想引用同一个对象是很危险的。所以当你不关心对象会被实例化多少次使用@Reusable
是安全的。
@Reusable // It doesn't matter how many scoopers we use, but don't waste them.
class CoffeeScooper {
@Inject CoffeeScooper() {}
}
@Module
class CashRegisterModule {
@Provides
@Reusable // DON'T DO THIS! You do care which register you put your cash in.
// Use a specific scope instead.
static CashRegister badIdeaCashRegister() {
return new CashRegister();
}
}
@Reusable // DON'T DO THIS! You really do want a new filter each time, so this
// should be unscoped.
class CoffeeFilter {
@Inject CoffeeFilter() {}
}
在Dagger2
中,可以通过自定义Scope
来实现局部单例:
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface HeaterScope {
}
然后在Module
中和Singleton
类似去使用它,来指定它的作用范围:
@Module
public class CoffeeModule {
@HeaterScope
@Provides
Heater provideHeater() {
return new ElectricHeater();
}
....
}
然后在Component
中和Singleton
类似去使用它,来指定它的作用范围:
@HeaterScope
@Component(modules = CoffeeModule.class)
public interface CoffeeComponent {
void inject(CoffeeMaker maker);
Heater provideHeater();
Pump providePump();
Ice provideIce();
IceBox provideIceBox();
@Type("normal")
Milk provideNormalMilk();
@Type("shuiguo")
Milk provideShuiGuoMilk();
String provideString();
}
那@Singleton
和@Scope
有什么区别呢? 我们看一下@Singleton
的源码:
/**
* Identifies a type that the injector only instantiates once. Not inherited.
*
* @see javax.inject.Scope @Scope
*/
@Scope
@Documented
@Retention(RUNTIME)
public @interface Singleton {}
查看源码可以发现@Singleton
是使用注解@Scope
修饰的, 而@Scope
是用来声明作用域的,Dagger2
的机制:在同一作用域下provides
提供的依赖对象会保持单例, 脱离这一作用域, 单例作用就会失效, 可以说@Singleton
是@Scope
注解的一个标准, 我们还可以去自定义一些Scope
注解来指定具体的作用域,这里就先不介绍了。
好了,到这里基本讲的差不多了,那我们就开始开发了,为了简单,我们就还是用咖啡的例子,在Android
开发中,有两个Activity
,我想在这两个Activity
中都使用Heater
对象,我们前面已经把它设置
成单例了。但是想要在两个Activity
中都使用,我们首先要修改一下CoffeeComponent
的inject
方法。
@Singleton
@Component(modules = CoffeeModule.class)
public interface CoffeeComponent {
void inject(MainActivity maker);
void inject(FirstActivity maker);
Heater provideHeater();
Pump providePump();
Ice provideIce();
IceBox provideIceBox();
@Type("normal")
Milk provideNormalMilk();
@Type("shuiguo")
Milk provideShuiGuoMilk();
String provideString();
}
好了接下来分别在MainActivity
和FirstActivity
中去使用:
import javax.inject.Inject;
public class MainActivity extends AppCompatActivity {
@Inject
Heater heater1;
@Inject
Heater heater2;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
CoffeeComponent component = DaggerCoffeeComponent.create();
component.inject(this);
Log.e("@@@", "MainActivity heater :" + heater1.toString());
Log.e("@@@", "MainActivity heater2 :" + heater2.toString());
startActivity(new Intent(MainActivity.this, FirstActivity.class));
finish();
}
}
FirstActivity
:
public class FirstActivity extends AppCompatActivity {
@Inject
Heater heater1;
@Inject
Heater heater2;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
CoffeeComponent component = DaggerCoffeeComponent.create();
component.inject(this);
Log.e("@@@", "FirstActivity heater :" + heater1.toString());
Log.e("@@@", "FirstActivity heater2 :" + heater2.toString());
}
}
走你 ~
04-21 11:57:31.514 12146-12146/com.charon.daggerdemo E/@@@: MainActivity heater :com.charon.daggerdemo.test.ElectricHeater@91bece5
MainActivity heater2 :com.charon.daggerdemo.test.ElectricHeater@91bece5
04-21 11:57:31.734 12146-12146/com.charon.daggerdemo E/@@@: FirstActivity heater :com.charon.daggerdemo.test.ElectricHeater@765de0d
FirstActivity heater2 :com.charon.daggerdemo.test.ElectricHeater@765de0d
我屮艸芔茻!!!每个Activity
中的对象确实是一样的,但是两个不同的Activity
中是不一样的。单例个锤子啊单例。这也是单例,只不过是局部单例。
使用@Singeton
注解或者定制的@Scope
的, 只在同一个activity
或者fragment
的一个生命周期中保持单例. 而平时我们希望一些对象能够在整个应用的生命周期中只存在一个, 也就是说实现全局意义上真正的单例该怎么做呢?
上面的DaggerCoffeeComponent
在两个Activity
中被各自被实例化一次, 导致产生了两个不同的对象, 所以我们需要做到让DaggerCoffeeComponent
能够实现单例,在不同的Activity
都是同一个对象,这样就可以保证全局单例了,Android
中整个App
生命周期中都只有一个Appclication
实例,所以可以在Application
中获得一个唯一的Component
实例:
主要分为以下基本部分:
-
创建一个
BaseModule
,并且创建单例的provideHeater
方法@Module public class BaseModule { @Singleton @Provides public Heater provideHeater() { return new ElectricHeater(); } }
-
接下来就是创建
BaseComponent
类,并且要将该接口声明单例@Singleton @Component(modules = BaseModule.class) public interface BaseComponent { // 之前我们说过Component中的provideXXX是可以不写的,但是如果你想让别的Component依赖该Component,就必须写,不写的话意味着没有向外界暴露该依赖 Heater provideHeater(); }
到这里,可能就会有疑问了,你忘了写
inject
方法了。这里BaseComponent
是不需要写inject
方法的,因为他没有要注入的类,我们下面会有具体的Component
类来写inject
,方法。这是base
就不需要了
注意:BaseComponent
中不再需要写inject
方法, 因为这个component
是用来让别的component
来依赖的, 只需要告诉别的component
他可以提供哪些类型的依赖即可, 这个例子中 我们提供一个全局Heater
的依赖 -
接下类是定义具体依赖于
BaseComponent
的子Component
,之前我们写Component
都是用@Component(modules = BaseModule.class)
来指定具体要去查找类的Module
,但是这里不一样了, 这里该Component
是依赖于BaseComponent
,所以这里要使用dependencies
关键字了。@Singleton @Component(dependencies = BaseComponent.class) public interface HeaterComponent { // 因为这里HeaterComponent依赖了BaseComponent,所以这里就有proviceHetear方法 void inject(MainActivity maker); void inject(FirstActivity maker); }
好了,这里我们重新
build
一次,如果没有意外的话,就会生成DaggerHeaterComponent
类了,我们去使用它就可以了。
但是,但是,出意外了.....错误: This @Singleton component cannot depend on scoped components: @Singleton com.charon.daggerdemo.test.BaseComponent
答题意思是说,使用
@Singleton
修饰的component
不能依赖另一个局部的component
,也就是@Singleton com.charon.daggerdemo.test.BaseComponent
。 也就是说如果依赖的component
中也使用了@singleton
时, 被依赖的地方就不能再使用@Singleton
了。
BaseComponent
我明明是 用了@Singleton
,而这个错误信息却说是一个scoped component
,这是什么鬼? 这个其实前面我们也说过了,这里再看一下@Singleton
的源码:@Scope @Documented @Retention(RUNTIME) public @interface Singleton {}
那这里不能用
@Singleton
该怎么处理呢? 这里要用到自定义@Scope
了。
自定义一个ActivityScope
:@Scope @Retention(RetentionPolicy.RUNTIME) public @interface ActivityScope { }
然后修改我们上面的
HeaterComponent
类,把之前使用的@Singleton
修改成我们刚才自定义的@ActivityScope
:@ActivityScope @Component(dependencies = BaseComponent.class) public interface HeaterComponent { void inject(MainActivity maker); void inject(FirstActivity maker); }
好了,再
build
一下, -
自定义
Application
public class BaseApplication extends Application {
private static BaseApplication mApplication;
private BaseComponent baseComponent;
public static BaseApplication getInstance() {
return mApplication;
}
@Override
public void onCreate() {
super.onCreate();
mApplication = this;
// 下面这两种方式都是可以的
// baseComponent = DaggerBaseComponent.create();
baseComponent = DaggerBaseComponent.builder().baseModule(new BaseModule()).build();
}
public BaseComponent getBaseComponent() {
return baseComponent;
}
}
- 好了,去
MainActivity
和FirstActivity
中修改下:
和前面一样,直接调用DaggerHeaterComponent
你会发现没有create
方法了,只有一个builder
方法,这种依赖的方式,必须要使用buildrer
来进行。
public class MainActivity extends AppCompatActivity {
@Inject
Heater heater1;
@Inject
Heater heater2;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// .baseComponent就是将Application中的BAseComponent传递到子component中,而在application中一创建就调用了DaggerBaseComponent.builder().build();
HeaterComponent component = DaggerHeaterComponent.builder().baseComponent(
BaseApplication.getInstance().getBaseComponent()).build();
component.inject(this);
Log.e("@@@", "MainActivity heater :" + heater1.toString());
Log.e("@@@", "MainActivity heater2 :" + heater2.toString());
startActivity(new Intent(MainActivity.this, FirstActivity.class));
finish();
}
}
public class FirstActivity extends AppCompatActivity {
@Inject
Heater heater1;
@Inject
Heater heater2;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
HeaterComponent component = DaggerHeaterComponent.builder().baseComponent(
BaseApplication.getInstance().getBaseComponent()).build();
component.inject(this);
Log.e("@@@", "FirstActivity heater :" + heater1.toString());
Log.e("@@@", "FirstActivity heater2 :" + heater2.toString());
}
}
运行下:
04-21 13:28:46.944 15130-15130/com.charon.daggerdemo E/@@@: MainActivity heater :com.charon.daggerdemo.test.ElectricHeater@91bece5
MainActivity heater2 :com.charon.daggerdemo.test.ElectricHeater@91bece5
04-21 13:28:47.024 15130-15130/com.charon.daggerdemo E/@@@: FirstActivity heater :com.charon.daggerdemo.test.ElectricHeater@91bece5
FirstActivity heater2 :com.charon.daggerdemo.test.ElectricHeater@91bece5
看,现在是全局单例了
上面讲到了Component
依赖,这里仔细讲讲,在上面的例子Activity
中:
// .baseComponent就是将Application中的BAseComponent传递到子component中,而在application中一创建就调用了DaggerBaseComponent.builder().build();
HeaterComponent component = DaggerHeaterComponent.builder().baseComponent(
BaseApplication.getInstance().getBaseComponent()).build();
component.inject(this);
这里因为baseComponent
的生命周期比较特殊,需要和Application
绑定,所以BaseComponent
的build
工作放到Application
中了。如果对于普通的component
依赖
这里其实是两部分,假设目前我们有这种情况,已经有了一个A
:
public class A {
public A() {
Log.e("@@@", "crate a");
}
}
@Module
public class AModule {
@Provides
public A proviceA() {
return new A();
}
}
@Component(modules = AModule.class)
public interface AComponent {
A proviceA();
void inject(FirstActivity activity);
}
同样还有B
:
public class B {
public B() {
Log.e("@@@", "crate b");
}
}
@Module
public class BModule {
@Provides
public B proviceB() {
return new B();
}
}
@Component(modules = BModule.class)
public interface BComponent {
B proviceB();
void inject(MainActivity activity);
}
我们在MainActivity
中去注入B
:
public class MainActivity extends AppCompatActivity {
@Inject
B b;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
BComponent bComponent = DaggerBComponent.create();
bComponent.inject(this);
}
}
执行结果:
04-21 18:45:38.494 18851-18851/com.charon.daggerdemo E/@@@: crate b
现在我想在B
中注入A
怎么办?我想也去用A
,但是A
已经有了,我不至于要重新再去写一遍:
@Component(modules = BModule.class, dependencies = AComponent.class) // 只需要在这里把AComponent引入,其他不用动
public interface BComponent {
B proviceB();
void inject(MainActivity activity);
}
到这里,我们的BComponent
继承了AComponent
,它也有了AComponent
中的方法:
public class MainActivity extends AppCompatActivity {
@Inject
B b;
@Inject
A a;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 注意要先build获取AComponent
AComponent aComponent = DaggerAComponent.builder().aModule(new AModule()).build();
// 要通过aComponent()方法将AComponent设置到BComponent中
BComponent bComponent = DaggerBComponent.builder().bModule(new BModule()).aComponent(aComponent).build();
bComponent.inject(this);
}
}
执行结果:
04-21 19:04:59.209 20445-20445/? E/@@@: crate b
04-21 19:04:59.210 20445-20445/? E/@@@: crate a
如果一个绑定使用scope
注解,这就意味着component
对象会持有这个作用域对象的引用直到component
自己被GC
回收。在像Android
这样的内存敏感环境中,如果你想在内存紧张时让GC
回收当前未使用的作用域对象,只需要使用@CanReleaseReferences
定义一个scope
即可:
@Documented
@Retention(RUNTIME)
@CanReleaseReferences
@Scope
public @interface MyScope {}
可以为scope
注入一个ReleasableReferenceManager
对象,在内存紧张时调用它的releaseStrongReferences()
方法以便让component
持有该对象的弱引用而不是强引用。
@Inject @ForReleasableReferences(MyScope.class)
ReleasableReferences myScopeReferences;
void lowMemory() {
myScopeReferences.releaseStrongReferences();
}
当内存压力减少时可以调用restoreStrongReferences()
方法恢复缓存对象的强引用。
void highMemory() {
myScopeReferences.restoreStrongReferences();
}
如果一个Component
的功能不能满足你的需求,你需要对它进行拓展,这里有两种方式:
- 使用
@Component(dependencies=××.classs)
- 使用
@Subcomponent
Subcomponent
用于拓展原有component
。同时这将产生一种副作用——子component
同时具备两种不同生命周期的scope
。子Component
具备了父Component
拥有的Scope
,也具备了自己的Scope
。
Subcomponent
其功能效果优点类似component
的dependencies
。但是使用@Subcomponent
不需要在父component
中显式添加子component
需要用到的对象,只需要添加返回子Component
的方法即可,子Component
能自动在父Component
中查找缺失的依赖。
我们还是用上面A
和B
的例子,假设A
是父类:
- 首先,修改
AComponent
类,在里面提供BComponent
//@Singleton
@Component(modules = AModule.class)
public interface AComponent {
// 一定要在A中提供获取BComponent的方法
BComponent bcomponent();
A provideA();
void inject(FirstActivity activity);
}
- 其次,修改
BComponent
,将其修改成SubComponent
的方式,并且去掉前面的dependencies
//@ActivityScope // 如果有的话只能比父类的范文小
@Subcomponent(modules = BModule.class)
public interface BComponent {
B provideB();
void inject(MainActivity activity);
}
- 使用
public class MainActivity extends AppCompatActivity {
@Inject
B b;
@Inject
A a;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
AComponent aComponent = DaggerAComponent.builder().aModule(new AModule()).build();
// 通过AComponent里面的方法去拿BComponent,然后调用inject
aComponent.bcomponent().inject(this);
// BComponent bComponent = DaggerBComponent.builder().bModule(new BModule()).aComponent(aComponent).build();
// bComponent.inject(this);
}
}
对比中我们发现dependencies
中Component
强调的是在子类Component
依赖于某个Component
(子类为主角),而Subcomponent
中强调的则是在父类Component
中提供某个子类的Component
(父类为主角)。
但是这两种到底有什么区别呢?
- 可以很清晰的看到具体的依赖关系
- 会生成独立的
DaggerXXXXComponent
类 - 依赖
Component
仅继承被依赖Component
中显示提供的依赖,如果不提供,则无法使用@Inject
注入被依赖的Component
中的对象
- 不需要在被依赖的
Component
显示提供依赖 - 不需要使用更多的
DaggerXXXXComponent
对象来创建依赖,仅需要在被依赖Component
中增加获取XXXComponent
的方法 - 但是会依赖不明确,不容易明确的看到具体的依赖关系
- 多人开发,被依赖
Component
容易造成冲突 - 务必在被依赖
Component
中创建XXXComponent
的获取方法,并在被依赖Component
的DaggerXXXComponent
的实例化对象中调用该方法
最后总结一下几个注解:
@Module
: 修饰类,用来提供依赖,里面会提供具体生成依赖类对象的方法,供使用者通过@Provides
:用于注解被@Module
修饰的类中的方法, 当需要该类的依赖时会去找返回值为该类的方法@Component
:注解一个接口或者抽象类,为inject
和module
之间建立联系,可以理解为不赚差价的中间商,在编译时会产生对应的类的实例, 为依赖方和被依赖方提供桥梁,把相关的依赖注入到其中.@inject
:依赖注入类中的依赖变量声明,让Dagger2
为其提供依赖或者用在被依赖的类的构造方法上申明, 通过标记构造函数让Dagger2
来使用, 可以在需要这个类实例的时候来找到这个构造函数并把相关实例new
出来@Named
:用于区分我们获取依赖的方法Dagger2
寻找依赖是根据我们提供方法的返回值进行匹配的, 当我们遇到不同提供依赖的方法但是返回值类型是相同的应该怎么办呢?Dagger2
提供了Named
注解,用于区分相同返回值类型的不同方法.@Qualifier
:与Named
一致,Named
是继承Qulifier
的,相当于Dagger2
为我们提供的标准化的区分器,Singleton
:声明单例模式,Dagger2
的机制是需要某个对象是回去找对应提供的实例化方法,多次寻找就会创建多个对象,当我们需要让对象保持单例模式时, 要使用@Singleton
修饰提供依赖的方法@Scope
:查看源码可以发现@Singleton
是使用注解@Scope
修饰的,而@Scope
是用来声明作用域的,Dagger2
的机制:在同一作用域下,provides
提供的依赖对象会保持单例,脱离这一作用域, 单例作用就会失效,可以说@Singleton
是@Scope
注解的一个标准,我们还可以去自定义一些Scope
注解.
- 邮箱 :charon.chui@gmail.com
- Good Luck!