上一篇詳細講解了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,這樣就可以直接使用了。
至此完畢,是以,你學會了嗎
源碼位址