接着上一篇: http://blog.csdn.net/niubitianping/article/details/60878104
一、单例@Singleton
需要实例的类如果是单例的,需要在Component接口和Module类的方法使用@Singleton。
栗子: 新建一个SingleClass.java
public class SingleClass {
//内容可以为空,仅做测试
}
1.1 Component接口添加@Singleton
在Component接口添加@Singleton
注解
@Singleton
@Component(modules = LoginModule.class)
public interface LoginComponent {
void inject(TestActivity activity);
}
1.2 Module类方法添加@Singleton
在Module类里面实例SingleClass的方法添加@Singleton
注解
@Module
public class LoginModule {
private Context mContext; //供给LoginStore使用
public LoginModule(Context mContext) {
this.mContext = mContext;
}
@Provides
@Singleton //这里添加单例注解
SingleClass provideSingleClass(){
return new SingleClass();
}
...(之前的代码缩略)
}
测试使用,在TestActivity中@Inject
两个Singleclass
public class TestActivity extends Activity {
@Inject
SingleClass singleClassOne;
@Inject
SingleClass singleClassTwo;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//DaggerLoginComponent.create().inject(this);
//当Module需要构造方法传参的时候,使用builder的方式初始化Dagger。
DaggerLoginComponent.builder()
.loginModule(new LoginModule(this)) //loginModule这个方法是构建之后才有的
.build()
.inject(this);
Log.e("@@", "singleClassOne: "+singleClassOne);
Log.e("@@", "singleClassTwo: "+singleClassTwo);
}
}
这时候可以看到输出的两个类的内存地址似乎一样的,证明这是同一个类,即单例类:
03-05 20:53:28.268 30247-30247/com.tpnet.dagger2test E/@@: singleClassOne: com.tpnet.dagger2test.five.SingleClass@c612337
03-05 20:53:28.268 30247-30247/com.tpnet.dagger2test E/@@: singleClassTwo: com.tpnet.dagger2test.five.SingleClass@c612337
ps注意: @Singleton 依赖于Component,如果两个不同的Component使用同一个Module来是哟个@Singleton创建单例,将会不是单例。
二、作用域@Scope
@Singleton
可以看到他的源码为:
@Scope
@Documented
@Retention(RUNTIME)
public @interface Singleton {}
这里看到有一个@Scope
,这个是用来给依赖划定作用域。
2.1 问题发掘
在单例的栗子中,我新建一个TwoActivity和GetInfoComponent,
GetInfoComponent.java
@Singleton
@Component(modules = LoginModule.class)
public interface GetInfoComponent {
void inject(TwoActivity activity);
}
TwoActivity.java
public class TwoActivity extends Activity {
@Inject
LoginCtrl loginCtrl;
@Inject
SingleClass singleClass;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DaggerGetInfoComponent.builder()
.loginModule(new LoginModule(this))
.build()
.inject(this);
Log.e("@@", "TwoActivity: "+singleClass);
}
}
把TestActivity修改为:
可以看到怎么SingleClass这个地址怎么不对呢,不是单例吗?
03-06 14:55:29.556 7020-7020/com.tpnet.dagger2test E/@@: provideLoginCtrlOne被调用:
03-06 14:55:29.556 7020-7020/com.tpnet.dagger2test E/@@: TestActivity: com.tpnet.dagger2test.six.SingleClass@421bd648
03-06 14:55:29.596 7020-7020/com.tpnet.dagger2test E/@@: provideLoginCtrlOne被调用:
03-06 14:55:29.596 7020-7020/com.tpnet.dagger2test E/@@: TwoActivity: com.tpnet.dagger2test.six.SingleClass@421c4558
继续看下去
2.2 Scope分类
我们的应用有三个范围:
- @Singleton: Singleton是Dagger已经定义好的,Application级别的Scope,只要application存活依赖就存活。
- @UserScope: 只要用户会话是激活状态依赖就存活(在单个应用程序中启动)。重要的是:这个scope存活时间不会超过application本身。每一个新的app实例都会创建一个新的@UserScope(甚至app不同的启动间用户会话没有关闭)。
- @ActivityScope: Activity范围,依赖于Activity,只要Activity界面存活依赖就存活。例如Activity销毁了,inject的component里面的module里面实例的对象都没了。
2.3 自定义Scope
利用Application级别的单例来实现单例,接下来解决上面的问题:
1. 新建AppModule.java
在这里提供SingleClass类。所以LoginModule里面提供Singleclass的方法需要去掉了。
@Module
public class AppModule {
@Singleton
@Provides
SingleClass providerSingleClass(){
return new SingleClass();
}
}
2. 新建AppComponent.java
@Singleton
@Component(modules = AppModule.class)
public interface AppComponent {
//把AppModule里面的SingleClass桥接出来
SingleClass singleClass();
}
3. 自定义Scope
创建一个ActivityScope.java,作为自定义的Scope
,用来标识LoginComponent和GetInfoComponent。
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ActivityScope {
}
4. 添加Component依赖
在LoginComponent和GetInfoComponent添加AppComponent的依赖,并且去掉@Singleton
,添加自定义的ActivityScope
注解
LoginComponent.java
@ActivityScope
@Component(modules = LoginModule.class,dependencies = AppComponent.class)
public interface LoginComponent {
void inject(TestActivity activity);
}
GetInfoComponent.java
@ActivityScope
@Component(modules = LoginModule.class,dependencies = AppComponent.class)
public interface GetInfoComponent {
void inject(TwoActivity activity);
}
5. 创建BaseApplication
完成上面的步骤之后,make 一下module,使得dagger生成DaggerAppComponent。
然后新建一个BaseApplication,用来注入AppComponent:
public class BaseApplication extends Application {
AppComponent mAppComponent;
@Override
public void onCreate() {
super.onCreate();
mAppComponent = DaggerAppComponent.create();
}
public AppComponent getAppComponent(){
return mAppComponent;
}
}
然后在manifest添加BaseApplication。
6. 在Activity注入
TestActivity的注入改为;
DaggerLoginComponent.builder()
.loginModule(new LoginModule(this)) //loginModule这个方法是构建之后才有的
//添加appComponent,参数从BaseApplication获取
.appComponent((((BaseApplication)getApplication()).getAppComponent()))
.build()
.inject(this);
TwoActivity的注入改为:
DaggerGetInfoComponent.builder()
.loginModule(new LoginModule(this))
//添加appComponent,参数从BaseApplication获取
.appComponent(((BaseApplication)getApplication()).getAppComponent())
.build()
.inject(this);
最后运行看到结果,两个Activity里面实例的SingleClass的类相同:
03-06 16:10:29.937 21284-21284/com.tpnet.dagger2test E/@@: provideLoginCtrlOne被调用:
03-06 16:10:29.937 21284-21284/com.tpnet.dagger2test E/@@: TestActivity: com.tpnet.dagger2test.six.SingleClass@421c4530
03-06 16:10:31.438 21284-21284/com.tpnet.dagger2test E/@@: provideLoginCtrlOne被调用:
03-06 16:10:31.438 21284-21284/com.tpnet.dagger2test E/@@: TwoActivity: com.tpnet.dagger2test.six.SingleClass@421c4530
自定义Scope可以看看外国大牛的文章:
http://frogermcs.github.io/dependency-injection-with-dagger-2-custom-scopes/
三、@SubComponent
顾名思义,子@Component。类似@Component的dependencies。 @SubComponent可以在@Component里面进行返回获取。
dependencies不会继承范围,@Subcomponent
会。@Subcomponent
同时具备两种不同作用域的scope。
来个栗子:
3.1 创建子SubComponent
创建SingleClassTwo:
public class SingleClassTwo {
//制作演示,可以为空方法
}
创建自定义Scope, UserScope.java:
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface UserScope {
}
创建ChildModule.java
@Module
public class ChildModule {
@UserScope
@Provides
SingleClassTwo provideSingleClass(SingleClassOne singleClass){
Log.e("@@", "provideSingleClass: singleClass是否为空:"+ (singleClass == null) );
return new SingleClassTwo();
}
}
创建ChildComponent.java:
@UserScope //自定义Scope修饰
@Subcomponent(modules = ChildModule.class)
public interface ChildComponent {
//这里的ThreeActivity在下面创建
void inject(ThreeActivity activity);
}
ps注意: 如果父的Component使用了@Singleton, 子@Subcomponent 不能使用@Singleton。 就是SubComponent的范围 < 父Component的范围,需要自定义Scope,这里定义了一个UserScope。
3.2 创建父Component
创建SingleClassOne:
public class SingleClassOne {
//制作演示,可以为空方法
}
创建FatherModule.java
@Module
public class FatherModule {
@Singleton
@Provides
SingleClassOne providerSingleClass(){
return new SingleClassOne();
}
}
创建FatherComponent.java:
@Singleton
@Component(modules = FatherModule.class)
public interface FatherComponent {
//父Component可以获取SubComponent
ChildComponent getChildComponent();
}
3.3 使用
上面的代码创建完毕之后,make一下module,然后创建一个ThreeActivity.java
public class ThreeActivity extends Activity{
@Inject
SingleClassTwo singleClassTwo;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DaggerOneComponent.create();
}
}
运行ThreeActivity之后,没看到输出 singleClass是否为空 ,点解?
因为还没有注入ChildComponent,把ThreeActivity的onCreate改为下面的代码:
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DaggerOneComponent.create()
.getChildComponent()
.inject(this);
}
即可看到输出:
03-06 18:37:55.216 3414-3414/? E/@@: provideSingleClass: singleClass是否为空:false
四、懒加载Lazy和Provider
在上面的栗子中,在ThreeActivity.java的onCreate添加一句代码:
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DaggerFatherComponent.create()
.getChildComponent()
.inject(this);
Log.e("@@", "还没有进行创建singleClassTwo");
}
可以看到下面的输出,证明了在inject的时候已经调用了ChildModule的provideSingleClass方法,简单说就是已经实例化了:
03-06 18:48:26.222 14898-14898/? E/@@: provideSingleClass: singleClass是否为空:false
03-06 18:48:26.222 14898-14898/? E/@@: 还没有进行创建singleClassTwo
4.1 Lazy
用Lazy标识的变量,在inject的时候并没有实例化,而是在t.get()的时候才实例化并返回。来个栗子:
创建Test.java
public class Test {
}
创建TestModule.java
@Module
public class TestModule {
@Provides Test provideTest(){
Log.e("@@", "调用了provideTest方法");
return new Test();
}
}
创建TestComponent.java
@Component(modules = TestModule.class)
public interface TestComponent {
void inject(TestActivity activity);
}
make moudle,然后创建TestActivity.java
public class TestActivity extends Activity{
@Inject Test test;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DaggerTestComponent.builder().build().inject(this);
Log.e("@@", "已经注入完成");
}
}
这时候运行可以看到输出:
03-06 19:09:46.301 5187-5187/com.tpnet.dagger2test E/@@: 调用了provideTest方法
03-06 19:09:46.301 5187-5187/com.tpnet.dagger2test E/@@: 已经注入完成
上面的输出这是正常的,现在来修改一下TestActivtiy:
public class TestActivity extends Activity{
// @Inject Test test;
@Inject Lazy<Test> test;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DaggerTestComponent.builder().build().inject(this);
Log.e("@@", "已经注入完成");
Log.e("@@", "开始第一次get: " + test.get().toString());
Log.e("@@", "开始第二次get: " + test.get().toString());
}
}
可以看到输出为下面的内容,表示inject的时候还没有进行TestModule里面的provideTest方法,是get的时候才实例化了,而且多次get得到的是同一个Test对象。
03-06 19:14:27.425 9846-9846/com.tpnet.dagger2test E/@@: 已经注入完成
03-06 19:14:27.425 9846-9846/com.tpnet.dagger2test E/@@: 调用了provideTest方法
03-06 19:14:27.425 9846-9846/com.tpnet.dagger2test E/@@: 开始第一次get: com.tpnet.dagger2test.eight.Test@421ae290
03-06 19:14:27.425 9846-9846/com.tpnet.dagger2test E/@@: 开始第二次get: com.tpnet.dagger2test.eight.Test@421ae290
4.2 Provider
使用这个类修饰的变量,也是懒加载,但是它每次get都会重新执行一遍provides实例化的方法。
修改一下上面的TestActivity:
public class TestActivity extends Activity{
// @Inject Test test;
//@Inject Lazy<Test> test;
@Inject Provider<Test> test;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DaggerTestComponent.builder().build().inject(this);
Log.e("@@", "已经注入完成");
Log.e("@@", "开始第一次get: " + test.get().toString());
Log.e("@@", "开始第二次get: " + test.get().toString());
}
}
可以看到输出为以下,表明了懒加载,每次get都会重新调用provides方法,产生新的对象:
03-06 19:18:09.852 13486-13486/com.tpnet.dagger2test E/@@: 已经注入完成
03-06 19:18:09.852 13486-13486/com.tpnet.dagger2test E/@@: 调用了provideTest方法
03-06 19:18:09.852 13486-13486/com.tpnet.dagger2test E/@@: 开始第一次get: com.tpnet.dagger2test.eight.Test@421af130
03-06 19:18:09.852 13486-13486/com.tpnet.dagger2test E/@@: 调用了provideTest方法
03-06 19:18:09.852 13486-13486/com.tpnet.dagger2test E/@@: 开始第二次get: com.tpnet.dagger2test.eight.Test@421af520
4.3 懒加载注意
Provide不是规定死了每次get都是新的对象。 例如单例,每次get还是同一个对象。在一的栗子的ThreeActivity进行修改:
public class ThreeActivity extends Activity{
//@Inject SingleClassTwo singleClassTwo;
@Inject Provider<SingleClassTwo> singleClassTwo;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DaggerFatherComponent.create()
.getChildComponent()
.inject(this);
Log.e("@@", "还没有进行创建singleClassTwo");
Log.e("@@", "get地址1: "+singleClassTwo.get().toString());
Log.e("@@", "get地址2: "+singleClassTwo.get().toString());
}
}
可以看到输出内容为以下,两次get的对象还是同一个:
03-06 19:22:27.944 17698-17698/? E/@@: 还没有进行创建singleClassTwo
03-06 19:22:27.944 17698-17698/? E/@@: provideSingleClass: singleClass是否为空:false
03-06 19:22:27.944 17698-17698/? E/@@: get地址1: com.tpnet.dagger2test.seven.SingleClassTwo@421b0860
03-06 19:22:27.944 17698-17698/? E/@@: get地址2: com.tpnet.dagger2test.seven.SingleClassTwo@421b0860
五、总结Dagger2注意点
@Component接口的inject方法接收的参数和传递的参数类型必须一致,不能定义为MainActivty,然后传递Activity,会导致注入失败,对象为空
@Component
接口关联的modules中不能有重复的provide类型@Module
类的provide方法使用了Scope,那么对应的@Component
接口也必须使用同一个注解。@Component
的dependencies与@Component
自身的Scope不能相同。需要自定义Scope@Singleton
的组件不能依赖其他Scope组件,只能其他的Scope组件依赖Singleton组件。没有Scope的
@Component
接口不能依赖有Scope的@Component
接口一个
@Component
接口不能同时有多个@Scope
(Subcomponent除外)@Singleton
的生命周期依赖于@Component
,@Component
的注入类销毁了,@Singleton
实例化的类也会没了。@SubComponent
的Scope范围不能大于他的父Component。懒加载Provider根据
@Provides
方法实现的不同,get到的对象可能是同一个,可能是不同。