前言
本系列所有文章:
Android 神兵利器Dagger2使用詳解(一)基礎(chǔ)使用
Android 神兵利器Dagger2使用詳解(二)Module&Component源碼分析
Android 神兵利器Dagger2使用詳解(三)MVP架構(gòu)下的使用
Android 神兵利器Dagger2使用詳解(四)Scope注解的使用及源碼分析
告別Dagger2模板代碼:DaggerAndroid使用詳解
告別Dagger2模板代碼:DaggerAndroid原理解析
該系列首發(fā)于我的CSDN專欄 :
Android開發(fā):Dagger2詳解
在我的上一篇文章中,我們通過一點(diǎn)點(diǎn)分析@Module小染、@Inject以及@Component注解生成的源碼翘瓮,了解了Dagger2依賴注入魔法的根源:
1、 @Inject 注解構(gòu)造 生成“大眾”工廠類 或者 @Module +@Providers 提供注入“私有”工廠類
2裤翩、通過Component 創(chuàng)建獲得Activity,獲得工廠類Provider资盅,統(tǒng)一交給Injector
3、Injector將Provider的get()方法提供的對(duì)象踊赠,注入到Activity容器對(duì)應(yīng)的成員變量中
我們就可以直接使用容器(Activity)中對(duì)應(yīng)的成員變量了
借用一張圖更形象一些:(圖片來源地址)感謝@牛曉偉
但是上文的使用方法實(shí)在有些簡(jiǎn)陋呵扛,不足以體現(xiàn)Dagger2為什么好用,或者說好用在哪臼疫,本文將會(huì)以正常實(shí)際開發(fā)為例择份,闡述Dagger2實(shí)際開發(fā)中的使用方式。
一烫堤、全局類 AppComponent & AppModule
我們先簡(jiǎn)單將我們開發(fā)時(shí)需要依賴注入的對(duì)象進(jìn)行簡(jiǎn)單的分類:
1荣赶、全局變量
這類變量一般為單例模式存在,比如SharedPerferences對(duì)象鸽斟、Application對(duì)象拔创,還有ServiceManager(網(wǎng)絡(luò)請(qǐng)求API管理類)等等,這些東西也許我們只想初始化一次富蓄,之后處處使用就好了剩燥。
2、局部變量
這些變量可能我們只想在某個(gè)地方使用立倍,比如Student對(duì)象灭红,我們一般也不會(huì)只聲明一次。
開發(fā)中口注,局部變量尚還好說变擒,我們?cè)跊]有Dagger的時(shí)候,用的時(shí)候new一個(gè)就可以了寝志,類似于SharedPerferences這種對(duì)象我們就比較頭疼娇斑,也沒有必要每次都去context.getSharedPerferences()吧,但是我們有了Dagger材部,我們可以通過依賴注入的方式毫缆,僅僅初始化一次,之后每次想使用乐导,只需要@Inject注解苦丁,就能直接使用了,是不是很方便呀兽叮。
說干就干芬骄,我們創(chuàng)建一個(gè)全局的AppComponent 和 AppModule:
0猾愿、首先創(chuàng)建一個(gè)自定義的Application類:
public class MyApplication extends Application{
@Override
public void onCreate() {
super.onCreate();
}
}
記得在清單文件中使用這個(gè)類哦 ~
1、創(chuàng)建AppModule账阻,提供你想要提供的全局變量
@Module
public class AppModule {
private MyApplication application;
public AppModule(MyApplication application) {
this.application = application;
}
//提供全局的sp對(duì)象
@Provides
SharedPreferences provideSharedPreferences(){
return application.getSharedPreferences("spfile", Context.MODE_PRIVATE);
}
//提供全局的Application對(duì)象
@Provides
MyApplication provideApplication(){
return application;
}
//你還可以提供更多.......
}
2蒂秘、創(chuàng)建AppComponent,然后ctrl+F9編譯
@Component(modules = AppModule.class)
public interface AppComponent {
SharedPreferences sharedPreferences();
MyApplication myApplication();
}
3淘太、編譯成功后姻僧,修改Application類,把Component注入器注入你的Application中:
public class MyApplication extends Application{
@Override
public void onCreate() {
super.onCreate();
inject();
}
private void inject() {
AppComponent appComponent = DaggerAppComponent.builder()
.appModule(new AppModule(this))
.build();
ComponentHolder.setAppComponent(appComponent);
}
}
ComponentHolder類蒲牧,方便獲取appComponent對(duì)象:
public class ComponentHolder {
private static AppComponent myAppComponent;
public static void setAppComponent(AppComponent component) {
myAppComponent = component;
}
public static AppComponent getAppComponent() {
return myAppComponent;
}
}
ok撇贺,這樣,無論何處冰抢,我們需要獲得AppComponent對(duì)象時(shí)松嘶,只需要調(diào)用ComponentHolder.getAppComponent()就可以啦!
注意:
我們需要知道剛才這些文件的作用:
1挎扰、AppModule:初始化全局變量
2翠订、AppComponent:注入器,儲(chǔ)存了我們要用到的全局變量對(duì)象
3遵倦、MyApplication: 在onCreate()方法中唯一一次初始化了AppComponent對(duì)象尽超,并放入了ComponentHolder中。
MyApplication (作為參數(shù)初始化)-> AppModule(初始化全局變量) -> (注入) AppComponent ->(存儲(chǔ)到)ComponentHolder
好的梧躺,現(xiàn)在我們已經(jīng)把所有要用到的全局變量存到了AppComponent中似谁,接下來我們創(chuàng)建一個(gè)普通的容器Activity。
二掠哥、日常開發(fā) Activity相關(guān)
0巩踏、我們先創(chuàng)建一個(gè)普通的Module和Component
@Module
public class A02Module {
private A02Activity activity;
public A02Module(A02Activity activity) {
this.activity = activity;
}
//顯然我們并不是很多地方都需要某對(duì)象,我們?cè)谛枰迷搶?duì)象的界面的Module中提供注入即可
@Provides
Student provideStudent() {
return new Student();
}
}
@Component(modules = A02Module.class, dependencies = AppComponent.class)
public interface A02Component {
void inject(A02Activity activity);
}
請(qǐng)注意Component接口续搀,我們發(fā)現(xiàn)多了這樣一行代碼:
dependencies = AppComponent.class
其實(shí)也很好理解蛀缝,比如說我們需要Student對(duì)象和SharedPerferences對(duì)象,前者是A02Component提供的目代,后者則需要「依賴(dependencies)」AppComponent提供,等于這次inject(A02Activity activity)依賴注入需要兩個(gè)Component共同出力完成嗤练。
1榛了、編譯成功后,在Activity中聲明:
public class A02Activity extends AppCompatActivity {
@Inject
Student student;
@Inject
SharedPreferences sp;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_a02);
inject();
}
private void inject() {
DaggerA02Component.builder()
.appComponent(ComponentHolder.getAppComponent())//添加appComponent注入器
.a02Module(new A02Module(this))
.build()
.inject(this);
Log.i("tag", "注入完畢煞抬,Student = " + student.toString());
Log.i("tag", "注入完畢霜大,sp = " + sp.toString());
}
}
查看一下結(jié)果,依賴注入成功:
是不是很神奇革答!試想一下战坤,如果我們有很多類似的對(duì)象需要經(jīng)常使用曙强,我們?cè)僖膊恍枰看味既コ跏蓟苯覢Inject途茫,然后直接使用就可以了;
更方便的是碟嘴,如果我們需要修改(比如SharedPreferences文件的名字等等),只需要跑到提供它的Module文件中:
@Module
public class AppModule {
...
...
...
//提供全局的sp對(duì)象 -> 修改
@Provides
SharedPreferences provideSharedPreferences(){
// return application.getSharedPreferences("spfile", Context.MODE_PRIVATE);
return application.getSharedPreferences("New spfile", Context.MODE_PRIVATE);
}
...
...
...
}
當(dāng)然囊卜,你的Activity容器中代碼也能變得更加賞心悅目:
三*娜扇、源碼分析:
因?yàn)樵谖业纳弦黄恼?a target="_blank" rel="nofollow">Android 神兵利器Dagger2使用詳解(二)Module&Component源碼分析 中已經(jīng)對(duì)原理有了基本的了解,所以我們可以輕松看懂實(shí)現(xiàn)原理栅组。
簡(jiǎn)單分析下源碼雀瓢,如下為編譯器自動(dòng)生成的代碼:
1、App全局類源碼(global包下的三個(gè)類)
提供對(duì)象的工廠類:
AppModule_ProvideApplicationFactory.java
AppModule_ProvideSharedPreferencesFactory.java
注入器:
DaggerAppComponent.java
工廠類直接略過玉掸,只不過是通過傳入Module對(duì)象初始化刃麸,然后調(diào)用get()方法獲得Module中對(duì)應(yīng)的對(duì)象罷了(若不清楚請(qǐng)參考我的上一篇文章,分析得比較清楚)司浪。
注入器的改變:
public final class DaggerAppComponent implements AppComponent {
private Provider<SharedPreferences> provideSharedPreferencesProvider;
private Provider<MyApplication> provideApplicationProvider;
private DaggerAppComponent(Builder builder) {
...
initialize(builder);
}
...
@SuppressWarnings("unchecked")
private void initialize(final Builder builder) {
//2.初始化2個(gè)工廠類
this.provideSharedPreferencesProvider =
AppModule_ProvideSharedPreferencesFactory.create(builder.appModule);
this.provideApplicationProvider = AppModule_ProvideApplicationFactory.create(builder.appModule);
}
//3.想要sp對(duì)象嗎泊业?appComponent調(diào)用一下這個(gè)方法就有了
@Override
public SharedPreferences sharedPreferences() {
return provideSharedPreferencesProvider.get();
}
@Override
public MyApplication myApplication() {
return provideApplicationProvider.get();
}
public static final class Builder {
//1、參數(shù)傳入断傲,初始化appModule
private AppModule appModule;
...
...
}
}
還是老三樣脱吱,三板斧,沒什么難度认罩。
2箱蝠、A02Component源碼分析:
提供Student對(duì)象的工廠類:
A02Module_ProvideStudentFactory.java
把對(duì)象注入到容器的Injcetor類:
A02Activity_MembersInjector.java
注入器:
DaggerA02Component.java
1、工廠略過不提垦垂,提供Student對(duì)象的類而已宦搬;
2、Injector 我們上一篇文章中可以知道劫拗,通過Component把需要對(duì)象的容器(activity)间校、以及對(duì)象工廠(studentProvider)給Injector后,Injector把對(duì)象賦值給容器中對(duì)應(yīng)的屬性而已页慷,比如:
activity.student = studentProvider.get();
3憔足、DaggerA02Component,執(zhí)行順序參考代碼中注釋:
public final class DaggerA02Component implements A02Component {
private Provider<Student> provideStudentProvider;
private Provider<SharedPreferences> sharedPreferencesProvider;
private MembersInjector<A02Activity> a02ActivityMembersInjector;
private DaggerA02Component(Builder builder) {
assert builder != null;
initialize(builder);
}
public static Builder builder() {
return new Builder();
}
@SuppressWarnings("unchecked")
private void initialize(final Builder builder) {
//3.初始化所有對(duì)象的工廠Provider
//3.1 從builder中的Module中獲得學(xué)生Student工廠
this.provideStudentProvider = A02Module_ProvideStudentFactory.create(builder.a02Module);
//3.2 從builder中的appComponent中獲得SP工廠
this.sharedPreferencesProvider =
new Factory<SharedPreferences>() {
private final AppComponent appComponent = builder.appComponent;
@Override
public SharedPreferences get() {
return Preconditions.checkNotNull(
appComponent.sharedPreferences(),
"Cannot return null from a non-@Nullable component method");
}
};
//4酒繁、把所有工廠都傳到Injector中
this.a02ActivityMembersInjector =
A02Activity_MembersInjector.create(provideStudentProvider, sharedPreferencesProvider);
}
@Override
public void inject(A02Activity activity) {
//5滓彰、把a(bǔ)ctivity放入到Injector中(接下來結(jié)果很明顯了...)
a02ActivityMembersInjector.injectMembers(activity);
}
public static final class Builder {
private A02Module a02Module;
private AppComponent appComponent;
private Builder() {}
public A02Component build() {
if (a02Module == null) {
throw new IllegalStateException(A02Module.class.getCanonicalName() + " must be set");
}
if (appComponent == null) {
throw new IllegalStateException(AppComponent.class.getCanonicalName() + " must be set");
}
return new DaggerA02Component(this);
}
//2.傳入a02Module
public Builder a02Module(A02Module a02Module) {
this.a02Module = Preconditions.checkNotNull(a02Module);
return this;
}
//1.傳入appComponent
public Builder appComponent(AppComponent appComponent) {
this.appComponent = Preconditions.checkNotNull(appComponent);
return this;
}
}
}
代碼雖然變多了,但是州袒,還是很好理解...
小結(jié)
不知道大家有沒有感到Dagger2的好用揭绑,確實(shí)是開發(fā)時(shí)解耦神器啊郎哭!
膜拜他匪!
如果有地方看不懂菇存,建議自己嘗試敲兩遍,然后參考編譯器生成的代碼邦蜜,多想想依鸥,可能一開始會(huì)有點(diǎn)繞,但還是可以看懂的畦徘。
代碼已托管Github毕籽,源碼傳送門,點(diǎn)我查看
在我的下一篇 Android 神兵利器Dagger2使用詳解(四)Scope注解的使用及源碼分析 中井辆,將會(huì)結(jié)合源碼关筒,學(xué)習(xí)并加深@Scope注解。
2017/8/24日更新
事實(shí)上杯缺,Android開發(fā)中使用Dagger,開發(fā)人員仍然需要面對(duì)一些問題蒸播。
google工程師們嘗試彌補(bǔ)Dagger的問題,于是Dagger2-Android,基于Dagger2萍肆,應(yīng)用于Android開發(fā)袍榆,由google開發(fā)的的拓展庫應(yīng)運(yùn)而生: