Dagger2運行原理分析以及MVP案例

Dagger2:

用途:假設有類A,類B,A類中包含一個B的實例

,那麼生成這種關係有幾種方法,方法一:a.setB(B b);方法二:在A的構造函數中傳入B,public A(B b){}.不管用什麼方法,都需要new B().如果哪一天業務需求有變,就需要改動所有new B()的地方,耦合性很強。如果使用dagger依賴注入後,後面去如果需要修改B對象,改動的地方會很少,降低了耦合性,減少了new B()代碼的編寫。

框架流程:

屬於編譯期間生效,自動生成相關代碼。

先搬一個簡單的dagger注入的示例代碼:首先添加相關jar包以及apt 插件依賴。

 

類B:

public class QueryModel {

        

         @Inject //這個表示QueryModel可以被依賴注入,該構造方法會被注入。

         publicQueryModel() {

         }

         publicvoid queryNum(String number,IQueryListener listener){

                 

                  for(int i = 0; i < 10000; i++) {

                  }

                  listener.querySuccess();

                 

         }

}

類A:

public class QueryPresenter implementsIQueryListener {

        

         privateIQueryView view;

         @Inject這裏表示這個示例可以被依賴注入。

          QueryModel model;

        

         @Inject

         publicQueryPresenter(IQueryView view) {

                  this.view= view;

                  DaggerQueryProcessorComponent.builder().build().inject(this);//開始進行注入

         }

        

         publicvoid startQuery(String number) {

                  model.queryNum(number,this);

         }

        

         @Override

         publicvoid querySuccess() {

                  view.showSuccessMsg();

         }

        

         @Override

         publicvoid queryFail() {

                  view.showErrorMsg();

         }

}

 

類C:

@Component //哪個對象需要依賴注入,那麼就需要有對應的Component組件。需要寫成interface或者是abstractclass. 以類A類B的關係來比喻,就是類A中含有類B的示例,並且B的示例是通過依賴注入拿到,那麼A就需要這個Component.框架自動生成的component是容器實例是:Dagger+component名字,示例中的就是DaggerQueryProcessorComponent

@Component

public interface QueryProcessorComponent {

         voidinject(QueryPresenter presenter);//注意這個注入參數,聲明的是什麼類型,就必須在什麼類型中注入,不能在這裏聲明一個父類,而跑到子類中去注入。

}

 

最簡單的dagger的框架使用就是如上,紅色的註釋已經做了簡單說明。當類B需要被注入時,就在B的構造方法上加一個@Inject註解,在使用的地方,在聲明的變量上面加一個@Inject參數。這樣導致問題是,只能注入我們自己寫的類,如果是android.jar的類,或者第三方jar中的類,我們就無法進行注入。那麼如何解決呢?就需要用到@Module以及@Provides註解了。

 

@Module module的用途是提供dagger註解需要的實例對象(無法通過在構造方法上加@inject獲得實例的),@module用於提供實例的類的註解,@provides用於註解提供類實例的方法。貌似注入框架是優先查找module來獲得需要被注入的示例,沒有在module中找到的話,再去找構造函數。

還有一點,在最簡單的dagger框架使用示例中,構造函數QueryModel()是不帶參數的,那麼如果帶了參數呢?參數怎麼傳遞進去,也是通過@module和@provides來提供(當dagger框架注入對象的時候,如果使用@module註解的類的構造函數需要參數,那麼需要通過.appModule(newModule(形參)來傳入))。

@Module

public class ActivityModule {

        

         privateIQueryView view;

         privateContext context;

         publicActivityModule(IQueryView view, Context context) {

                  this.view= view;

                  this.context= context;

         }

        

         @Provides

         IQueryViewprovideIQueryView(){

                  returnview;

         }

        

         @Provides

         SharedPreferencesproviderGlobalSharedPreference(){

                  returnPreferenceManager.getDefaultSharedPreferences(context);

         }

}

 

@Component(modules = ActivityModule.class)

public interface ActivityComponent {

        

         voidinject(DaggerActivity activity);

}

//這裏的modules也可以有多個,總之voidinject(DaggerActivity activity)中的參數activity依賴注入的對象需要哪些通過module拿到的參數,都需要在modudles屬性中列出來

 

 

使用依賴注入的activity:

public class DaggerActivity extendsAppCompatActivity implements IQueryView {

        

         @Inject

         QueryPresentermPresenter;//因爲presenter 的構造函數需要參數,所以需要對daggerActivity的注入容器提供一個方法,用來提供生成presenter所需要的參數。

        

         @Override

         protectedvoid onCreate(Bundle savedInstanceState) {

                  super.onCreate(savedInstanceState);

                  setContentView(R.layout.activity_dagger);

 

在這裏進行注入,這個類是自動生成的

                  DaggerNewActivityComponent.builder().activityModule(newActivityModule(this,this)).build().inject(this);//

                 

//              mPresenter= new QueryPresenter(this);

//              DaggerActivityComponent.builder().build().inject(this);第一個

                  //對比這個2個注入方式,發現如果需要參數的話,那麼就需要在builder()和build()之間插入對應的module(就是提供參數的module),注意:這個方法activityModule()是框架自動生成的。

         }

        

         @Override

         publicvoid showSuccessMsg() {

                  Toast.makeText(DaggerActivity.this,"成功", Toast.LENGTH_SHORT).show();

         }

        

         @Override

         publicvoid showErrorMsg() {

                  Toast.makeText(DaggerActivity.this,"失敗", Toast.LENGTH_SHORT).show();

                 

         }

}

 

***************************************************

 

Dagger2 編譯後代碼注入流程分析:這裏不是原理,這是根據dagger框架自動生成的代碼做流程分析。

分析起點是:在類A中寫了一個類B的實例b,分析這個b是如何生成的。就根據注入代碼

 DaggerAppComponent.builder().

                appModule(newAppModule(getApplicationContext())).

                httpModule(newHttpModule(getApplicationContext())).build().inject(this);根據這個代碼的執行過程就可以了。

首先看其內部類Builder:

public static final class Builder {

   private HttpModule httpModule; // 這裏對應的是你添加的每個module

 

   private AppModule appModule;

 

   private Builder() {}

 

    publicAppComponent build() {//調用build()方法時,會檢測是否有set每一個module,沒有的話就拋異常

      if(httpModule == null) {

       throw new IllegalStateException(HttpModule.class.getCanonicalName() +" must be set");

      }

      if(appModule == null) {

       throw new IllegalStateException(AppModule.class.getCanonicalName() +" must be set");

      }

     return new DaggerAppComponent(this);//這裏也需要分析

    }

 

    publicBuilder appModule(AppModule appModule) {

     this.appModule = Preconditions.checkNotNull(appModule);

     return this;

    }

 

    publicBuilder httpModule(HttpModule httpModule) {

     this.httpModule = Preconditions.checkNotNull(httpModule);

     return this;

    }

  }

 

DaggerAppComponent自己的構造方法:

private DaggerAppComponent(Builder builder) {

    assertbuilder != null;

    initialize(builder);

  }

initialize(builder);

 @SuppressWarnings("unchecked")

  privatevoid initialize(final Builder builder) {

 

*包括的都是對象實例提供者

 **************************************************

   this.provideTestInstanceProvider =

        HttpModule_ProvideTestInstanceFactory.create(builder.httpModule);這裏也是關鍵方法。對應的是我們的加的@provides註解

 

this.provideSharedPrefProvider= AppModule_ProvideSharedPrefFactory.create(builder.appModule);

*******************************************

 

下面的是injector是真正的注入執行者,在create()將所有需要的被加入到容器中的對象通過參數傳遞進去。

   this.baseMvpActivityMembersInjector =

       BaseMvpActivity_MembersInjector.create(

           User_Factory.create(), provideTestInstanceProvider,provideSharedPrefProvider);

  }

經過分析可以發現,針對於每個module中的每一個provider方法,都會生成一個對應的factory方法。以其中一個爲例:

public final classAppModule_ProvideSharedPrefFactory implements Factory<SharedPreferences>{

  privatefinal AppModule module;

 

  publicAppModule_ProvideSharedPrefFactory(AppModule module) {

    assertmodule != null;

   this.module = module;

  }

 

 @Override

  publicSharedPreferences get() {//這裏的get()調用的是傳進來的module的我們註解了@provide的方法,這樣就可以通過這個工廠對象的get()獲得我們定義的註解對象。

    returnPreconditions.checkNotNull(

       module.provideSharedPref(), "Cannot return null from anon-@Nullable @Provides method");

  }

 

  publicstatic Factory<SharedPreferences> create(AppModule module) {

    returnnew AppModule_ProvideSharedPrefFactory(module);

  }

}

接下來就是關鍵了,調用鏈最後的inject(this):

 @Override

 public void inject(BaseMvpActivity mvpActivity) {

   baseMvpActivityMembersInjector.injectMembers(mvpActivity);

  }

 

public BaseMvpActivity_MembersInjector(

     Provider<User> userProvider,

     Provider<TestInstance> testInstanceProvider,

      Provider<SharedPreferences>sharedPreferencesProvider) {

    assertuserProvider != null;

   this.userProvider = userProvider;

    asserttestInstanceProvider != null;

   this.testInstanceProvider = testInstanceProvider;

    assertsharedPreferencesProvider != null;

   this.sharedPreferencesProvider = sharedPreferencesProvider;

  }

 

  publicstatic MembersInjector<BaseMvpActivity> create(

     Provider<User> userProvider,

     Provider<TestInstance> testInstanceProvider,

     Provider<SharedPreferences> sharedPreferencesProvider) {

    returnnew BaseMvpActivity_MembersInjector(

       userProvider, testInstanceProvider, sharedPreferencesProvider);

  }

 

 

  @Override

  public void injectMembers(BaseMvpActivityinstance) {

    if (instance == null) {

      throw newNullPointerException("Cannot inject members into a null reference");

}

在這裏將BaseMvpActivity中的被@inject標註的依賴注入的對象進行賦值。這樣就完成了整個依賴注入過程。

    instance.user = userProvider.get();

    instance.testInstance =testInstanceProvider.get();

    instance.sharedPreferences =sharedPreferencesProvider.get();

  }

 

  publicstatic void injectUser(BaseMvpActivity instance, Provider<User>userProvider) {

   instance.user = userProvider.get();

  }

 

  publicstatic void injectTestInstance(

     BaseMvpActivity instance, Provider<TestInstance>testInstanceProvider) {

   instance.testInstance = testInstanceProvider.get();

  }

 

  publicstatic void injectSharedPreferences(

     BaseMvpActivity instance, Provider<SharedPreferences>sharedPreferencesProvider) {

   instance.sharedPreferences = sharedPreferencesProvider.get();

  }

}

用uml表示如下:

 

再來講講@Scope註解的使用:

Scope字面理解就是範圍的意思,在java中可以理解爲作用域。

在android中,有些對象是全局application的,有些對象可能會有多個activity共享,這個時候就需要用到scope註解了。注意:如果對於自己寫的對象,使用單例的話,就需要在module中提供provide方法,不能通過inject構造方法的形式來提供。

使用步驟如下:

1、建立一個scope,命名隨意,形式固定。


@Scope

@Retention(RetentionPolicy.RUNTIME)

public @interface ActivityScope {

}

2、在需要使用scope的module中的provide方法上加上scope註解。

@Module

public class AppModule {

private Context context;

 

    publicAppModule(Context context) {

       this.context = context;

}

 

    @Provides

    @ActivityScope//加上scope註解

    User providersssssssUser(){

        return new User();

    }

}
3、在聲明的註解容器上加上scope。

@ActivityScope

@Component(modules = {AppModule.class,HttpModule.class})//凡是module使用到了scope,那麼容器中必須寫上scope,否則編譯不過。

public interface AppComponent {

    //

    voidinject(BaseMvpActivity mvpActivity);

 

    voidinject(BaseMvp2Activity second_activity);

}

4、在application中要生成容器(需要保證只能實例化容器一次,否則不可能是單例)

public class DaggerApplication extendsApplication {

 

   AppComponent appComponent;

   @Override

    publicvoid onCreate() {

       super.onCreate();

       getAppComponent();

    }

    publicAppComponent getAppComponent(){

        if(appComponent == null) {

           appComponent = DaggerAppComponent.builder().

                    appModule(newAppModule(getApplicationContext())).

                    httpModule(newHttpModule(getApplicationContext())).build();

        }

       return appComponent;

    }

}

5、在需要注入的activity中進行注入。

((DaggerApplication)getApplication()).getAppComponent().inject(this);

這樣下來,就能保證2個activity拿到是同一個user對象了。

至於爲什麼這樣加上scope就能實現單例,可以通過dagger框架生成的代碼來分析。就以user對象來分析。

在前面的代碼中有提高,自動生成的註解容器對象DaggerAppComponent針對module中的每一個provide方法都會生成Provider<T>的泛型對象,然後通過provider.get()方法拿到對應的實例。

當我們不加scope註解時,生成usrprovider的代碼如下:

userProvider = AppModule_ProvidersssssssUserFactory.create(builder.appModule);

當需要user實例時,就會調用appmodule.providerUser(),實際就會生成一個user實例,如果多次調用inject(),肯定會生成多個user對象,自然不是單例的。

那麼如果加了scope操作後呢,框架生成的代碼如下。

Userprovider =DoubleCheck.provider(AppModule_ProvidersssssssUserFactory.create(builder.appModule));

會發現對usrprovider進行了一次doublecheck的包裝。那麼當調用userprovider.get()實際調用的是doubleCheck.get(),接下來看看doubleCheck.get()的具體實現。

private static final Object UNINITIALIZED = newObject();

  privatevolatile Object instance = UNINITIALIZED;

@SuppressWarnings("unchecked") // castonly happens when result comes from the provider

 @Override

  public Tget() {

    Object result = instance;

    if (result == UNINITIALIZED) {通過這裏判斷result是否有被初始化過

      synchronized (this) {//代碼同步,這裏還保證了線程安全

       result = instance;

        if(result == UNINITIALIZED) {

         instance = result = provider.get();

         /* Null out the reference to the provider. We are never going to need itagain, so we

          * can make it eligible for GC. */

         provider = null;

        }

      }

    }

    return(T) result;

  }

所以,當我們在不同的地方注入(即獲取user對象時),如果該對象已經生成了,就會直接返回,保證了單例。

 

還有一些不常用的註解:

@dependencies @Named

 

@dependencies:用於組件(component)間的依賴。

用途:假設我有一個application級別的component(裏面注入的對象肯定有單一實例的sharedPreference\context\登陸的用戶等等),那麼我activity級別的component肯定會用到這些實例對象,這個時候activity component就需要依賴application component.

寫法:

@Component(dependencies={Appcomponent.class},modules={})

Public interfaceActivityComponent{

 

}

規則:1、假設activitycomponent希望使用application component中的sharedPreference,那麼就必須在application component 中提供一個暴露sharedpreference的方法。

@ActivityScope

@Component(dependencies= {},modules = {AppModule.class, HttpModule.class})

public interfaceAppComponent {

//    void inject(BaseMvpActivity mvpActivity);

    void inject(BaseMvp2Activitysecond_activity);

    SharedPreferences SharedPreferences();

}

2、依賴與被依賴的組件的scope必須不同。

 

@named:用於區別多個同一類型的實例。

用途:假設我要提供2種sharedpreference,一種是默認的,一種是保存cache的。我們就可以在AppModule中寫2個方法,分別提供2種不同的sharedpreference。

 

AppModule:

@Named("default")

   @Provides

   SharedPreferences provideSharedPref(){

       return PreferenceManager.getDefaultSharedPreferences(context);

    }

   @Named("cached")

   @Provides

   SharedPreferences provideCachedSharedPref(){

       return context.getSharedPreferences("cached",Context.MODE_PRIVATE);

    }

在被注入的地方的實例名上加上@named註解,就可以獲取指定的sharedpreference了。

MainActivity:

 

 @Named("cached")

   @Inject

   protected SharedPreferences sharedPreferences;

 

   @Named("default")

   @Inject

   protected SharedPreferences sharedPreferencesDefault;

這裏需要注意的一點是:如果有依賴關係,activitycomponent用到了default和cached兩種sharedpreference,因爲application component需要暴露對象的方法,那麼現在就需要提供兩個針對@Named的方法。

ApplicationComponent:

@Named("cached")

   SharedPreferences SharedPreferences();

   @Named("default")

   SharedPreferences SharedPreferences2();

 

基本dagger2的用法就分析完畢了。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章