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);//注意這個注入引數,宣告的是什麼型別,就必須在什麼型別中注入,不能在這裡宣告一個父類,而跑到子類中去注入。
}
@Module module的用途是提供dagger註解需要的例項物件(無法通過在構造方法上加@inject獲得例項的),@module用於提供例項的類的註解,@provides用於註解提供類例項的方法。貌似注入框架是優先查詢module來獲得需要被注入的示例,沒有在module中找到的話,再去找建構函式。
@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 [email protected] @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();