快速入門Dagger2

1、Dagger2的介紹和簡單使用:

A fast dependency injector for Android and Java.
一個快速的依賴注入庫為java和android,
什么是依賴注入?有興趣的同學可以看一下我之前轉載的一篇依賴注入的文章。
https://github.com/google/dagger
這是Dagger2的github地址,里面介紹了在項目中如何使用和依賴。

1.1 在介紹入門之前,先簡單的學習和認識一下基本的概念和名詞:

@inject

該注解定義在需要依賴的地方使用,即:標有這個注解的地方是告訴Dagger這個類或者字段需要依賴注入,當調用相關的注入方法時候,Dagger就會創(chuàng)建一個該類的實例,將其依賴注入。

@module

Module類里面的方法是專門提供依賴,所以,一個類被@Module所注解,也就是告訴Dagger,構造實例從哪里去找需要的依賴(或者是創(chuàng)建),modules的一個重要的特點是它們設計為分區(qū)和組合在一起,即,假如我們的APP中需要多個module,這個時候,我們可以使用modules這個注解進行將其組合在一塊。

@provide

在module中,我們定義的方法用這個注解,以此來告訴Dagger,我們想要創(chuàng)建這些對象,并提供。

@component

component類似于一個橋梁,也可以說一個注入器,即@Inject和@Module之間的橋梁,它的作用是連接這兩部分。
這里只是先做一個概念的解釋和闡述

2、如何快速使用 ?分為4步:

2.1 在Demo中,我們模擬一個用戶管理類(增加用戶功能)
public class UserManage {

    public void addUser(){
        Log.d("UserManage","Add a new User");
    }
}
2.2 創(chuàng)建Module去提供相應的構建
@Module
public class UserModule {
  
  @Provides
    UserManage provideUserManager(){
        return new UserManage();
    }
}
2.3 創(chuàng)建Component實現(xiàn)橋梁
@Component(modules =UserModule.class )
public interface UserComponent {
    void inject(MainActivity activity);
}
2.4 在Activity中進行依賴注入UserManage,這個類
public class MainActivity extends AppCompatActivity {
    
    @Inject
    UserManage userManage;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        DaggerUserComponent.create().inject(this);
        userManage.addUser();
    }
}

我相信看上面的代碼可能有一定的難度,下面我會詳細的解釋一下一步步是如何實現(xiàn),為什么這么做?在詳細介紹之前,我們先看看我們的Demo是不是可以運行起來,如果可以打印出addUser()中的日志,說明我們在MainActivity中的將UserManage依賴成功。


R(EK1X3JF9UQM6WD{LPVL%F.jpg

說明我們依賴成功,并且調用了UserMange的addUser()方法,切記我們在運行前,一定要進行rebuild project一下,使用apt插件工具幫我們自動生成代碼。

3. Dagger2進階使用:

到這里,我們算是已經(jīng)是一個Dagger2的入門了,學會了簡單的使用。但是如何將Dagger2運用到自己的實際項目中,還需要在實際項目中使用。在這里我們所有使用的類是沒有進行參數(shù)的傳遞,這顯然是不符合在實際項目中的業(yè)務因此,這里我們需要給UserManage傳遞一個User參數(shù),代表實際要注入的對象。

@Module
public class UserModule {

    @Provides
    UserManage provideUserManager(User user){
        return new UserManage(user);
    }
    
}

那么我們?nèi)绾芜M行依賴注入這個User對象,我們重新rebuild的話,控制臺會報下面的錯誤,可以看出,是因為沒有提供User對象,而造成的,那么我們該如何提供這個User對象的實例?


圖片.png

有3種方案:(提供給它User對象不就完事了)

3.1、在UserModule中提供一個User對象 ,即:
@Module
public class UserModule {

    @Provides
    UserManage provideUserManager(User user){
        return new UserManage(user);
    }

    @Provides
    User provideUser(){
        return new User("OneX_Zgj","22");
    }

}

我們在UserManager中打印了相關則信息:

public class UserManage {

    public UserManage(User user){
        Log.d("TAG", "UserManage: "+user.getName() +" : " +user.getAge());
    }

    public void addUser(){
        Log.d("Tag","Add a new User");
    }
}

MainActivity和Component中的代碼沒有動:
運行結果:可以看到執(zhí)行成功了,并且依賴注入了User對象


圖片.png
3.2:單獨創(chuàng)建一個UserCreateModule

單獨創(chuàng)建一個UserCreateModule進行provide 一個特定的User對象,但是我們在UserModule中如何使用呢?

先看UserCreateModule中的實現(xiàn)

@Module
public class UserCreateModule {

    @Provides
    User provideUser(){
        return new User("OneX_zgj","22");
    }

}
@Module(includes = UserCreateModule.class)
public class UserModule {

    @Provides
    UserManage provideUserManager(User user){
        return new UserManage(user);
    }

//    @Provides
//    User provideUser(){
//        return new User("OneX_Zgj","22");
//    }

}

現(xiàn)在觀察在UserModule中是如何進行實現(xiàn)的?細心的同學肯定會發(fā)現(xiàn)在類上面的注解放生了變化,對,就是因為這個注解,我們就可以引用和依賴UserCreateModule中提供的對象。

圖片.png
3.3:注解依賴到UserComponent中

我們將UserCreateModule加入到UserComponent中,也是可以運行的:

@Component(modules ={UserModule.class,UserCreateModule.class} )
public interface UserComponent {
    void inject(MainActivity activity);
}

4. 創(chuàng)建和區(qū)分不同的對象實例

4.1在介紹和使用之前,先介紹一下這兩個注解

@Qualifier:區(qū)分不同的對象實例
@Named :其實是@Qualifier的一種實現(xiàn),我們也可以自己定義。

4.1.1 @Name 注解的使用:標記不同的對象實例

在實際項目中,我們有業(yè)務是進行創(chuàng)建出不同的實例對象,但是從我們的Demo
比如我們開發(fā)一般在測試庫,而實際發(fā)布,需要改用正式版庫,那么,普通的話,我們需要修改不同地址等等。
在這里我們簡單模擬一下功能,我們使用NetUrl假設封裝了地址,我們需要2中不同的url,去適配測試庫和正式庫,所以在代碼中,我們需要根據(jù)某種狀態(tài)或者標識,進行將它們區(qū)分出來:

NetUrl的簡單實現(xiàn):

public class NetUrl {

    private String url;

    public NetUrl(String url) {
        this.url = url;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }
}

在看一下我們在Module中的實現(xiàn)和使用@Name注解區(qū)分不同的實例(正式庫和測試庫)

@Module
public class OkhttpApi {

    @Provides
    @Named("test")
    NetUrl provideNetTestUrl(){
        return new NetUrl("http://test");
    }

    @Provides
    @Named("release")
    NetUrl  provideNetReleaseUrl(){
        return new NetUrl("http://release");
    }
}

在看一下OkhttpApiComponent中的實現(xiàn):

@Component(modules = OkhttpApi.class)
public interface OkhttpApiComponent {
    void inject(MainActivity activity);
}

接下來我們查看下MainActivity中的代碼:需要注意的是,如果在Module中進行加入了@Name注解,我們在MainActivity中的@Inject旁邊也要加入@Name注解,指明當前對象需要注入什么樣的實例。

public class MainActivity extends AppCompatActivity {

//    @Inject
//    UserManage userManage;

    @Inject
    @Named("test")
    NetUrl TestApi;

    @Inject
    @Named("release")
    NetUrl ReleaseApi;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        DaggerOkhttpApiComponent.create().inject(this);
        Log.d("Tag", TestApi.getUrl());
        Log.d("Tag", ReleaseApi.getUrl());
        
    }
}
image.png
4.1.2我們通過自定義Scope來實現(xiàn)和區(qū)分不同的實例
@Qualifier
@Documented
@Retention(RUNTIME)
public @interface Release {
}

1、定義了Release的scope,好奇的同學,可能會問,你是怎么知道這樣定義Scope的,哈哈,答案是:我肯定抄的啦,就是模仿@Name注解進行寫的。
2、定義Test的Scope

@Qualifier
@Documented
@Retention(RUNTIME)
public @interface Test {
}

在Module中替換相應的注解

@Module
public class OkhttpApi {

//    @Provides
//    @Named("test")
//    NetUrl provideNetTestUrl(){
//        return new NetUrl("http://test");
//    }
//
//
//    @Provides
//    @Named("release")
//    NetUrl  provideNetReleaseUrl(){
//        return new NetUrl("http://release");
//    }


    @Provides
    @Test
    NetUrl provideNetTestUrl(){
        return new NetUrl("http://test");
    }


    @Provides
    @Release
    NetUrl  provideNetReleaseUrl(){
        return new NetUrl("http://release");
    }
}

在MainActivity中進行替換為我們自定義的Scope

public class MainActivity extends AppCompatActivity {

//    @Inject
//    UserManage userManage;

//    @Inject
//    @Named("test")
//    NetUrl TestApi;
//
//    @Inject
//    @Named("release")
//    NetUrl ReleaseApi;


    @Inject
    @Test
    NetUrl TestApi;

    @Inject
    @Release
    NetUrl ReleaseApi;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        DaggerOkhttpApiComponent.create().inject(this);
        Log.d("Tag", TestApi.getUrl());
        Log.d("Tag", ReleaseApi.getUrl());

    }
}
image.png

@Singleton的使用,在實際項目開發(fā)中,我們會對網(wǎng)絡請求對象進行單例(比如okhttp實例),來節(jié)約內(nèi)存,這個時候我們就需要進行使用Singleton,來表明該對象是要用單例創(chuàng)建的。

4.2 如何在實際項目中進行單例的操作:(比如確保在App中有only one 個okhttp的實例?)

下面的Demo將演示如何在Application中創(chuàng)建一個單例:

4.2.1 創(chuàng)建Module
@Module
public class AppMoudle {

    private MyApp context;

    public AppMoudle(MyApp context) {
        this.context = context;
    }

    @Singleton
    @Provides
    public OkhttpUtils provideOkhttpUtils() {
        OkhttpUtils okService = new OkhttpUtils(context);
        Log.d("TAG", "provideApiService: " + okService);
        return okService;
    }
}
4.2.2 創(chuàng)建AppComponent
@Singleton
@Component (modules = AppMoudle.class)
public interface AppComponent {
    /**
     * 全局單例。所以不用Inject Activity
     *
     * @return 向下返回ApiService實例
     */
    OkhttpUtils getApiService();
}
4.2.3在App中實例化出來AppComponent,確保是唯一的實例
public class MyApp extends Application {

    private AppComponent mAppComponent;

    @Override
    public void onCreate() {
        super.onCreate();
        mAppComponent = DaggerAppComponent.builder().appMoudle(new AppMoudle(this)).build();

    }

    public AppComponent getAppComponent() {
        return mAppComponent;
    }
}
4.2.4 在子類的Component中使用:

UserComponet 中的實現(xiàn):

@PreActivity
@Component(modules = UserModule.class ,dependencies = AppComponent.class)
public interface UserComponet {
    void inject(MainActivity activity);
}

LoginComponent 中的實現(xiàn):

@PreActivity
@Component(modules = UserModule.class,dependencies = AppComponent.class)
public interface LoginComponent {
    void  inject(LoginActivity activity);
}

這里我們?yōu)槭裁匆脗€@PreActivity,因為在AppComponent中使用了Singleton注解,沒有scope的component不能依賴有scope的component,而且要不同于依賴的Component,所以這里我們需要自定義一個Scope。
PreActivity的實現(xiàn)

@Scope
@Documented
@Retention(RUNTIME)
public @interface PreActivity {
}
4.2.5在Activity中的使用:
public class MainActivity extends AppCompatActivity {


    @Inject
    OkhttpUtils okhttpUtils;

    @Inject
    OkhttpUtils okhttpUtils2;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button btnStart = (Button) findViewById(R.id.btn_start);

        DaggerUserComponet.builder().appComponent(((MyApp) getApplication()).getAppComponent()).build().inject(this);

        okhttpUtils.logId();
        okhttpUtils2.logId();

        btnStart.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startActivity(new Intent(MainActivity.this, LoginActivity.class));
            }
        });

    }
}

LoginActivity中的實現(xiàn)和MainActivity中的實現(xiàn)完全一樣。
最后一步,運行一下程序,我們觀察okhttpClient實例的內(nèi)存地址:


image.png

這樣我們實現(xiàn)了App中的單例操作。

5.在使用Dagger2中我們經(jīng)常會范的一下錯誤:

1.component的inject方法接受父類型的參數(shù),而調用的時候傳入的子類型對象,對象則無法進行注入。

比如:我們在Component使用的是BaseActivity,而實際要注入的是MainActivity,雖然MainActivity extends BaseActivity,但是在Dagger2中,依舊無法進行注入。

2.component關聯(lián)的Modules中不能有重復的Provide,因為Dagger2在注入的時候,是根據(jù)類型去匹配,然后創(chuàng)建對象,如果有多處Provide的話,就不知道去創(chuàng)建出哪一個實例。
3.module的provide方法使用了scope,那么我們在Component中也使用該注解
4.module的provide方法沒有使用scope,那么component和module是否加注解沒有影響
5.component的dependencies與component自身的scope不能相同,即組件之間的scope不同
6.Ssingleton的組件不能依賴其他scope的組件,只能其他的scope依賴singleton的組件
7.沒有scope的component不能依賴有scope的component
8.一個component不能同時有多個scope
9.singleton注解的生命周期是component,意思是說:在同一個Component中,標記了@singleton的注解是單例,但是在不同的Component中,就會創(chuàng)建出來不同的實例對象。
最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容