Dagger2使用攻略
來自: http://blog.csdn.net/qq_17766199/article/details/50606011
Dagger2使用攻略
Dagger 2 是 Square 的 Dagger 分支,是一種依賴注入框架。目前由 Google 接手進行開發,Dagger2是使用代碼自動生成和手寫代碼來實現依賴注入。據說在 Dagger 的基礎上效率又提升了13%,并且同樣功能強大。
</blockquote>
1.Gradle配置
(1)需要配置apt 插件:(在project根目錄
build.gradle
文件中添加如下代碼)dependencies { ... classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' }(2)添加依賴:(在module
build.gradle
文件中添加如下代碼)apply plugin: 'com.neenbedankt.android-apt'// 注釋處理dependencies { ... compile 'com.google.dagger:dagger:2.0.2' apt 'com.google.dagger:dagger-compiler:2.0.2' compile 'org.glassfish:javax.annotation:10.0-b28' // Java標注 }</pre>
● 當前最新版本是2.0.2
● 添加2、3條依賴的原因參考:Dagger2入坑指南
● 如果在項目中同時用了
Butterknife
,在Build時會報注釋沖突。
解決方法:(在module
build.gradle
文件中添加如下代碼)packagingOptions { exclude 'META-INF/services/javax.annotation.processing.Processor' }(3)最后點擊
Build-->Make Project
就可以開始使用Dagger2
了。2.Dagger2 常用注解
寫了一個簡單的Demo,下面根據Demo進行介紹。Dagger2要理解必須看Dagger 2自動生成的代碼,Build后代碼在
項目-->app-->build-->generated-->source-->apt-->debug
目錄下。1.Inject
@Inject
:在需要依賴的地方使用這個注解,告訴Dagger這個類或者字段需要依賴注入,這樣Dagger會構造一個這個類實例來滿足依賴。1.構造器注入:首先舉一個簡單的例子,無參構造方法。
public class Person { private String name; private int age;@Inject public Person() { } public String getName() { return "Jack"; } public int getAge() { return 15; }
}</pre>
這個的局限性是我們不能給這個類中的多個構造器作
@Inject
注解。2.注解成員變量:
接著上面我們要使用這個實例化類。
public class MainActivity extends AppCompatActivity {@Inject Person mPerson; StorageComponent mStorageComponent; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mStorageComponent = ((MyApplication)this.getApplication()).getStorageComponent(); mStorageComponent.inject(this);//注入MainActivity Toast.makeText(this,mPerson.getName() + "----" +mPerson.getAge(),Toast.LENGTH_SHORT).show(); }
}</pre>
這里我們可以查看生成的代碼:
@Generated("dagger.internal.codegen.ComponentProcessor") public final class MainActivity_MembersInjector implements MembersInjector<MainActivity> { private final MembersInjector<AppCompatActivity> supertypeInjector; private final Provider<Person> mPersonProvider;public MainActivity_MembersInjector(MembersInjector<AppCompatActivity> supertypeInjector, Provider<Person> mPersonProvider) {
assert supertypeInjector != null; this.supertypeInjector = supertypeInjector; assert mPersonProvider != null; this.mPersonProvider = mPersonProvider;
}
@Override public void injectMembers(MainActivity instance) {
if (instance == null) { throw new NullPointerException("Cannot inject members into a null reference"); } supertypeInjector.injectMembers(instance); instance.mPerson = mPersonProvider.get();//這里mPersonProvider替我們實例化了Person }public static MembersInjector<MainActivity> create(MembersInjector<AppCompatActivity> supertypeInjector, Provider<Person> mPersonProvider) {
return new MainActivity_MembersInjector(supertypeInjector, mPersonProvider); } }</pre>同時我們也可以了解到
@Inject Person mPerson;
為什么不能使用private
。上面代碼中的injectMembers
方法調用后面會說到。3.方法注入
public class LoginActivityPresenter {private LoginActivity loginActivity; @Inject //構造方法注入 public LoginActivityPresenter(LoginActivity loginActivity) { this.loginActivity = loginActivity; } @Inject //方法注入 public void enableWatches(Watches watches) { watches.register(this); }
}</pre>
如當我們希望傳入類的當前實例(this引用)到被注入的依賴中。方法注入會在構造器調用后馬上被調用,所以這表示我們可以傳入完全被構造的this。
2.Module
@Module
:用來修飾類,表示此類的方法是用來提供依賴的,它告訴Dagger在哪里可以找到依賴。@Module public class StorageModule {private final MyApplication application; public StorageModule(MyApplication application) { this.application = application; } @Provides @Singleton SharedPreferences provideSharedPreferences(){ return PreferenceManager.getDefaultSharedPreferences(application); }
}</pre>
@Provides
下面說到,@Singleton
單例,使用@Singleton
注解之后,對象只會被初始化一次,之后的每次都會被直接注入相同的對象。@Singleton
就是一個內置的作用域。3.Provides
@Provides
:在@Module
中使用,我們定義的方法用這個注解,用于告訴 Dagger 我們需要構造實例并提供依賴。為什么要使用
@Provides
,因為默認情況下,Dagger滿足依賴關系是通過調用構造方法得到的實例,比如上面的Person類使用。但是有時因為@Inject
的局限性,我們不能使用@Inject
。比如構造方法有多個、三方庫中的類我們不知道他是怎么實現的等等。例如下面代碼中的SharedPreferences
,我們使用@Provides
返回一個創建好的實例,這樣做也顯得靈活不是嗎?@Provides @Singleton SharedPreferences provideSharedPreferences(){ return PreferenceManager.getDefaultSharedPreferences(application); }注意:
● 按照習慣
@Providers
方法都會用provide作為前綴,@Module
類都用Module作為后綴。● 如果
@Provides
方法有參數,這個參數也要保證能夠被Dagger得到(例如通過其他的@Provides
方法或者@Inject注解的構造方法。)4.Component
@Component
: 是@Inject
和@Module
的橋梁,需要列出所有的@Modules以組成該組件。@Singleton @Component(modules = { StorageModule.class , ScheduleModule.class }) public interface StorageComponent {Storage execute(); void inject(MainActivity mMainActivity);
}</pre>
Dagger會按照上面接口生成一個實現類,生成類以Dagger為前綴,提供builder()來生成實例。調用方法:(因為是單例,所以這里放到了MyApplication)
public class MyApplication extends Application {private StorageComponent component; @Override public void onCreate() { super.onCreate(); component = DaggerStorageComponent .builder()//調用構建類 .storageModule(new StorageModule(this)) //傳入Module .build();//生成實例 } public StorageComponent getStorageComponent() { return component; }
}</pre>
MainActivity代碼:
public class MainActivity extends AppCompatActivity {@Bind(R.id.button1) Button mButton1; @Bind(R.id.button2) Button mButton2; @Inject SharedPreferences mPreferences;//全局的SharedPreferences @Inject Person mPerson; StorageComponent mStorageComponent; private final String KEY = "Dagger 2"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); mStorageComponent = ((MyApplication)this.getApplication()).getStorageComponent(); mStorageComponent.inject(this);//注入MainActivity mStorageComponent.execute().storage();//執行儲存操作 } @OnClick({R.id.button1,R.id.button2}) void onButtonClicked(View v) { switch (v.getId()) { case R.id.button1: Toast.makeText(this,mPreferences.getString(KEY,"---"),Toast.LENGTH_SHORT).show(); //上面是示例獲取mPreferences,實際中將SharedPreferences操作都可以封裝進Storage中,如下 //Toast.makeText(this, //mStorageComponent.execute().getStorage(),Toast.LENGTH_SHORT).show(); break; case R.id.button2: Toast.makeText(this, mPerson.getName() + "----" + mPerson.getAge(),Toast.LENGTH_SHORT).show(); break; } }
}</pre>
下來把整個流程走一遍:首先進入
MyApplication
執行DaggerStorageComponent.builder().storageModule(new StorageModule(this)).build();
方法獲取實例化StorageComponent
。那我我們查看DaggerStorageComponent
類:@Generated("dagger.internal.codegen.ComponentProcessor") public final class DaggerStorageComponent implements StorageComponent { private Provider<SharedPreferences> provideSharedPreferencesProvider; private Provider<ScheduleImpl> provideScheduleProvider; private Provider<Storage> storageProvider; private MembersInjector<MainActivity> mainActivityMembersInjector;private DaggerStorageComponent(Builder builder) {
assert builder != null; initialize(builder); }public static Builder builder() {
return new Builder(); }private void initialize(final Builder builder) {
this.provideSharedPreferencesProvider = ScopedProvider.create(StorageModule_ProvideSharedPreferencesFactory.create(builder.storageModule)); this.provideScheduleProvider = ScopedProvider.create(ScheduleModule_ProvideScheduleFactory.create(builder.scheduleModule)); this.storageProvider = Storage_Factory.create(provideSharedPreferencesProvider, provideScheduleProvider); this.mainActivityMembersInjector = MainActivity_MembersInjector.create((MembersInjector) MembersInjectors.noOp(), provideSharedPreferencesProvider, Person_Factory.create());//實例化到這里結束 }@Override public Storage execute() {
return storageProvider.get(); }@Override public void inject(MainActivity mMainActivity) {
mainActivityMembersInjector.injectMembers(mMainActivity); }public static final class Builder { private StorageModule storageModule; private ScheduleModule scheduleModule;
private Builder() { } public StorageComponent build() { if (storageModule == null) { throw new IllegalStateException("storageModule must be set"); } if (scheduleModule == null) { this.scheduleModule = new ScheduleModule(); } return new DaggerStorageComponent(this); } public Builder storageModule(StorageModule storageModule) { if (storageModule == null) { throw new NullPointerException("storageModule"); } this.storageModule = storageModule; return this; } public Builder scheduleModule(ScheduleModule scheduleModule) { if (scheduleModule == null) { throw new NullPointerException("scheduleModule"); } this.scheduleModule = scheduleModule; return this; }
} }</pre>
上面代碼最后執行到MainActivity_MembersInjector.create(…)查看
MainActivity_MembersInjector
類:@Generated("dagger.internal.codegen.ComponentProcessor") public final class MainActivity_MembersInjector implements MembersInjector<MainActivity> { private final MembersInjector<AppCompatActivity> supertypeInjector; private final Provider<SharedPreferences> mPreferencesProvider; private final Provider<Person> mPersonProvider;public MainActivity_MembersInjector(MembersInjector<AppCompatActivity> supertypeInjector, Provider<SharedPreferences> mPreferencesProvider, Provider<Person> mPersonProvider) {
assert supertypeInjector != null; this.supertypeInjector = supertypeInjector; assert mPreferencesProvider != null; this.mPreferencesProvider = mPreferencesProvider; assert mPersonProvider != null; this.mPersonProvider = mPersonProvider; }@Override public void injectMembers(MainActivity instance) {
if (instance == null) { throw new NullPointerException("Cannot inject members into a null reference"); } supertypeInjector.injectMembers(instance); instance.mPreferences = mPreferencesProvider.get();//賦值 instance.mPerson = mPersonProvider.get(); }public static MembersInjector<MainActivity> create(MembersInjector<AppCompatActivity> supertypeInjector, Provider<SharedPreferences> mPreferencesProvider, Provider<Person> mPersonProvider) {
return new MainActivity_MembersInjector(supertypeInjector, mPreferencesProvider, mPersonProvider); } }</pre>下來進入到了
MainActivity
,在通過((MyApplication)this.getApplication()).getStorageComponent()
獲取到component
后執行mStorageComponent.inject(this);
方法注入MainActivity,最終回調到上面代碼中的injectMembers
方法中,可以看出MainActivity
中的成員變量全部初始完成。之后就可以直接使用了。5.Lazy 類
Lazy類是實現懶加載,調用的時候才創建實例,通過Lazy對象實現,得到對象的實例使用get()方法。例如:
public class Storage {private SharedPreferences mPreferences; private Lazy<ScheduleImpl> mScheduleImpl;//Lazy 類 private final String KEY = "Dagger 2"; @Inject public Storage(SharedPreferences mPreferences ,Lazy<ScheduleImpl> mScheduleImpl) { this.mPreferences = mPreferences; this.mScheduleImpl = mScheduleImpl; } public void storage() { mScheduleImpl.get().start();//get()方法 mPreferences.edit().putString(KEY, "Dagger 2 -- Example").commit(); mScheduleImpl.get().end(); }
}</pre>
6.Scope
@Scope
:注解作用域,通過自定義注解限定對象的作用范圍。它是JSR-330標準的一部分,其實@Singleton
就是一種@Scope
。在Dagger 2中,@Scope
被用于標記自定義的scope注解。簡單說它們可以類似單例地標記依賴。被作注解的依賴會變成單例,但是這會與component的生命周期(不是整個應用)關聯。首先創建一個LoginScope:
@Scope @Retention(RetentionPolicy.RUNTIME) public @interface LoginScope { }Module:
@Module public class LoginModule {@Provides @LoginScope //<---這里為單例 Person providePerson() { Person mPerson = new Person(); mPerson.setAge(23); mPerson.setName("WeiLu"); return mPerson; } @Provides Login provideLogin() { Login mLogin = new Login(); mLogin.setPassword("######"); mLogin.setName("小關"); return mLogin; }
}</pre>
Component:
@LoginScope @Component(modules = { LoginModule.class }) public interface LoginComponent { void inject(MyApplication myApplication); }調用:
mLoginComponent = DaggerLoginComponent.builder() .loginModule(new LoginModule()) .build(); mLoginComponent.inject(this);這里我們看一下生成代碼:
@Generated("dagger.internal.codegen.ComponentProcessor") public final class DaggerLoginComponent implements LoginComponent { private Provider<Person> providePersonProvider; private Provider<Login> provideLoginProvider; private MembersInjector<MyApplication> myApplicationMembersInjector;private DaggerLoginComponent(Builder builder) {
assert builder != null; initialize(builder); }public static Builder builder() {
return new Builder(); }public static LoginComponent create() {
return builder().build(); }private void initialize(final Builder builder) {
this.providePersonProvider = ScopedProvider.create(LoginModule_ProvidePersonFactory.create(builder.loginModule)); this.provideLoginProvider = LoginModule_ProvideLoginFactory.create(builder.loginModule); this.myApplicationMembersInjector = MyApplication_MembersInjector.create((MembersInjector) MembersInjectors.noOp(), providePersonProvider, provideLoginProvider); }@Override public void inject(MyApplication myApplication) {
myApplicationMembersInjector.injectMembers(myApplication); }public static final class Builder { private LoginModule loginModule;
private Builder() { } public LoginComponent build() { if (loginModule == null) { this.loginModule = new LoginModule(); } return new DaggerLoginComponent(this); } public Builder loginModule(LoginModule loginModule) { if (loginModule == null) { throw new NullPointerException("loginModule"); } this.loginModule = loginModule; return this; }
} }</pre>
看
initialize
方法:沒有加@LoginScope
的Login類,那么他的創建是就是利用工廠模式new了一個Login。相反加了@LoginScope
的Person類,是利用ScopedProvider
儲存了起來。源碼如下:public final class ScopedProvider<T> implements Provider<T> { private static final Object UNINITIALIZED = new Object(); private final Factory<T> factory; private volatile Object instance;private ScopedProvider(Factory<T> factory) { this.instance = UNINITIALIZED; assert factory != null; this.factory = factory; } public T get() { Object result = this.instance; if(result == UNINITIALIZED) { synchronized(this) { result = this.instance; if(result == UNINITIALIZED) { this.instance = result = this.factory.get(); } } } return result; } public static <T> Provider<T> create(Factory<T> factory) { if(factory == null) { throw new NullPointerException(); } else { return new ScopedProvider(factory); } }
}</pre>
7.Qualifier
@Qualifier
:限定符,當類的類型不足以鑒別一個依賴的時候會使用到。如果我們沒有去區分,會報錯:xxx cannot be provided without an @Provides-annotated method。例如上面的Person類,我們現在準備返回兩個:小明與小關,返回的都是Person類,怎么區分依賴?首先自定義一個
@Qualifier
:@Qualifier @Retention(RetentionPolicy.RUNTIME) public @interface User { }下來是Module:
@Module public class UserModule {@Provides @User//加上這個自定義注解用于區分 Login provideXiaoMingUser() { Login xiaomin = new Login(); xiaomin.setPassword("******"); xiaomin.setName("小明"); return xiaomin; } @Provides Login provideXiaoGuanUser() { Login xiaoguan = new Login(); xiaoguan.setPassword("######"); xiaoguan.setName("小關"); return xiaoguan; }
}</pre>
Component:
@Subcomponent(modules = { UserModule.class }) public interface UserComponent { void inject(SecondActivity mSecondActivity); }這里用到了
@Subcomponent
,我們想復用組件時,可以使用它,下面是父組件使用方法。另一種是注解屬性添加dependencies。@Singleton @Component( modules ={ AppModule.class } ) public interface AppComponent {Context getAppContext(); UserComponent createUserComponent(UserModule userModule);
}</pre>
這種復用組件其實是在在父組件中創建了子組件的內部類。如下:
@Generated("dagger.internal.codegen.ComponentProcessor") public final class DaggerAppComponent implements AppComponent { private Provider<Context> provideContextProvider;private DaggerAppComponent(Builder builder) {
assert builder != null; initialize(builder); }public static Builder builder() {
return new Builder(); }private void initialize(final Builder builder) {
this.provideContextProvider = ScopedProvider.create(AppModule_ProvideContextFactory.create(builder.appModule)); }@Override public Context getAppContext() {
return provideContextProvider.get(); }@Override public UserComponent createUserComponent(UserModule userModule) {
return new UserComponentImpl(userModule); }public static final class Builder { private AppModule appModule;
private Builder() { } public AppComponent build() { if (appModule == null) { throw new IllegalStateException("appModule must be set"); } return new DaggerAppComponent(this); } public Builder appModule(AppModule appModule) { if (appModule == null) { throw new NullPointerException("appModule"); } this.appModule = appModule; return this; }
}
private final class UserComponentImpl implements UserComponent {//內部類 private final UserModule userModule; private Provider<Login> provideXiaoMingUserProvider; private Provider<Login> provideXiaoGuanUserProvider; private MembersInjector<SecondActivity> secondActivityMembersInjector;
private UserComponentImpl(UserModule userModule) { if (userModule == null) { throw new NullPointerException(); } this.userModule = userModule; initialize(); } private void initialize() { this.provideXiaoMingUserProvider = UserModule_ProvideXiaoMingUserFactory.create(userModule); this.provideXiaoGuanUserProvider = UserModule_ProvideXiaoGuanUserFactory.create(userModule); this.secondActivityMembersInjector = SecondActivity_MembersInjector.create((MembersInjector) MembersInjectors.noOp(), provideXiaoMingUserProvider, provideXiaoGuanUserProvider); } @Override public void inject(SecondActivity mSecondActivity) { secondActivityMembersInjector.injectMembers(mSecondActivity); }
} }</pre>
初始化:(MyApplication中)
mAppComponent = DaggerAppComponent.builder() .appModule(new AppModule(this)) .build(); mUserComponent = mAppComponent.createUserComponent(new UserModule());調用:
public class SecondActivity extends AppCompatActivity {UserComponent userComponent; @Inject @User Login xiaoming; @Inject Login xiaoguan; @Bind(R.id.button4) Button mButton4; @Bind(R.id.button5) Button mButton5; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_second); ButterKnife.bind(this); userComponent = ((MyApplication)this.getApplication()).getUserComponent(); userComponent.inject(this); } @OnClick({ R.id.button4, R.id.button5, }) void onButtonClicked(View v) { switch (v.getId()) { case R.id.button4: Toast.makeText(this, xiaoming.getName() + "----" + xiaoming.getPassword(),Toast.LENGTH_SHORT).show(); break; case R.id.button5: Toast.makeText(this, xiaoguan.getName() + "----" + xiaoguan.getPassword(),Toast.LENGTH_SHORT).show(); break; } }
}</pre>
具體生成的代碼,大家下載Demo后自行查看。
通過自動生成的代碼可以看出Dagger 2主要用到了Builder模式、Factory模式,代碼不難理解。同時因為Dagger 2沒有使用反射,雖然效率提高了,但是缺乏靈活性。這也是為了提高效率的代價。
3.Dagger2 練習Demo
Demo下載鏈接:Dagger2Example
4.參考
4. Dependency injection with Dagger 2 - Custom scopes
PS:最后說一下,關于Dagger2的學習成本還是挺高的。我自己也是從零開始接觸,利用業余時間前前后后用了近一周時間去學習,一開始看的也是云里霧里。其實對照著自動生成的代碼多看看就比較好理解了。有什么錯誤地方,希望多多交流。就這樣。。。
</div>