Dagger2使用攻略

brzq3054 8年前發布 | 14K 次閱讀 Android開發 移動開發

來自: 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)添加依賴:(在modulebuild.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時會報注釋沖突。
這里寫圖片描述

解決方法:(在modulebuild.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.參考

1. Dagger 2 API文檔

2. Android Dagger2學習

3. 使用Dagger 2依賴注入 - API

4. Dependency injection with Dagger 2 - Custom scopes


PS:最后說一下,關于Dagger2的學習成本還是挺高的。我自己也是從零開始接觸,利用業余時間前前后后用了近一周時間去學習,一開始看的也是云里霧里。其實對照著自動生成的代碼多看看就比較好理解了。有什么錯誤地方,希望多多交流。就這樣。。。

</div>

 本文由用戶 brzq3054 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!