上一篇详细讲解了Dagger2的大部分使用及基础知识,不太了解的朋友可以去看看http://www.jb51.cc/article/p-yuztquvj-wr.html。这一篇讲讲组件依赖及具体的使用。
组件依赖
顾名思义当然就是组件之间的依赖,即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,这样就可以直接使用了。
至此完毕,所以,你学会了吗