上一篇详细讲解了Dagger2的大部分使用及基础知识,不太了解的朋友可以去看看http://blog.csdn.net/lylodyf/article/details/52981910。这一篇讲讲组件依赖及具体的使用。
组件依赖
顾名思义当然就是组件之间的依赖,即Component之间的依赖,不知是否发现Component有个属性是dependencies,用以指定依赖的Component。那么组件依赖有什么用处呢?想象这样一个场景,有一个MainModule里面的实例化方法需要一个参数假设为Context,现在无法获得这个Context,但是在另一个组件AppComponent里面有,现在我们只需要让管理MainModule的MainComponent组件依赖APPComponent就可以轻松实现。也就是说组件依赖可以让子组件获得父组件暴露出来的对象。
使用组件依赖有几点需要注意:
1.两个依赖的组件不能共享作用域,什么意思,也就是他们指定的作用域一定要不同
2.父组件必须暴露出子组件所需要的对象
下面通过一个具体实例来说明,其中包含了Dagger2的具体使用实践。
目录结构
AppComponent
@Singleton
@Component(modules = AppModule.class)
public interface AppComponent {
//Exposed to sub-graphs.
Context context();
ToastUtil toastUtil();
SharedPreferences sharedPreferences();
}
可以看到这里面有三个方法,之前不是说过实现组件依赖父组件必须暴露出方法吗,这里就是向子组件暴露了三个方法。
AppModule
@Module
public class AppModule {
private MyApplication application;
public AppModule(MyApplication application) {
this.application = application;
}
@Provides
@Singleton
Context provideContext() {
return application;
}
@Singleton
@Provides
ToastUtil provideToastUtil(Context context) {
return new ToastUtil(context);
}
@Singleton
@Provides
SharedPreferences provideSP(Context context) {
return context.getSharedPreferences("config", Context.MODE_PRIVATE);
}
}
这里面有三个实例化的方法,方法上面的Scope必须要和对应的Component的Scope一致。也许有人会问其中有两个方法传入的参数Context是从哪里来的,这是由于这里面还有一个方法provideContext(),返回的正是需要的Context。所以如果在这里还有一个方法需要参数ToastUtil,这里也有返回ToastUtil的方法,也可以提供。
MainComponent
@UserScope
@Component(modules = MainModule.class, dependencies = AppComponent.class)
public interface MainComponent {
void inject(MainActivity activity);
这里指定了依赖AppComponent,有一个注射到MainActivity得方法,很好理解吧。至于@UserScope是一个自定义的Scope,不了解的可以去看看我上篇博客。之前也说到和依赖的父组件的Scope必须不同,所以不能使用@Singleton
MainModule
@Module
public class MainModule {
@Named("a")
@UserScope
@Provides
User provideUser1(Child child) {
return new User(child);
}
@Named("b")
@UserScope
@Provides
User provideUser2(Child child) {
return new User(child);
}
}
只有两个返回User的实例化方法,@Named是限定符,不了解的也可以去看看上篇博客。User和Child就是两个普通的类,这里发现传入的参数是Child,那么有人又有疑问了,这里并没有返回Child的方法啊,这个Child又是从哪里来呢?下面我们先看看User类和Child类
User
public class User {
public User(Child child) {
Log.i("lzy", "调用User类无参构造方法");
}
}
Child
public class Child {
@Inject
public Child() {
Log.i("lzy", "调用Child类的无参构造方法");
}
}
可以看到User并没有什么特别,只是构造函数需要传入一个Child。但是看看Child,构造函数上面加了@Inject注解,上一篇博客我们不是讲过提供构造方法有两种方式吗,先到Module中找,没有找到会找有@Inject的构造函数。所以上面的Child就是从这里来的。
MyApplication
public class MyApplication extends Application {
private AppComponent appComponent;
@Override
public void onCreate() {
super.onCreate();
appComponent = DaggerAppComponent.builder().appModule(new AppModule(this)).build();
}
public AppComponent getAppComponent() {
return appComponent;
}
}
在Application中实例化了APPComponent,并且向外提供一个获取它的方法。当然要记得在manifest文件中配置。
MainActivity
public class MainActivity extends AppCompatActivity {
private static final String TAG = "lzy";
@Named("a")
@Inject
User user;
@Inject
SharedPreferences sp;
@Inject
ToastUtil toastUtil;
@Inject
TestClass test;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerMainComponent.builder().appComponent(getAppComponent()).mainModule(new MainModule())
.build().inject(this);
Log.i(TAG, "User: " + user);
Log.i(TAG, "SharedPreference: " + sp);
Log.i(TAG, "test: " + test);
toastUtil.showToast("呵呵哈哈哈");
}
private AppComponent getAppComponent() {
return ((MyApplication) getApplication()).getAppComponent();
}
}
在这里调用了 DaggerMainComponent.builder().appComponent(getAppComponent()).mainModule(new MainModule()).build().inject(this)把MainActivity注入到MainComponent里面,并且声明了四个对象,User、SharedPreference、ToastUtil和TestClass
看看打印出来的日志
发现全部都不是空的,并且显示出了Toast。但是记得我们只在MainModule中提供了User的实例化方法吗,然后SharedPreference和ToastUtil也不为空,这自然就是因为依赖的APPComponent的原因。
对了这个TestClass有是什么东东,好像在其他地方都没有使用出现过,为什么打印出来又不为空呢
TestClass
@UserScope
public class TestClass {
@Inject
public TestClass() {
}
}
很简单的一个自定义类,只是制定了和MainComponent一样的作用域和在构造函数上面添加了@Inject,这样就可以直接使用了。
至此完毕,所以,你学会了吗