RXjava+Retrofit+dagger2打造自己的MVP框架

RXjava+Retrofit+dagger的入門

        前言:現在RXjava+Retrofit+dagger可謂是如日中天,當時頭一次接觸都有一種無從入手的感覺,那麼我來分享一下我的學習方式。將RXjava Retrofit dagger分開來學 最後整合到一起


RXjava篇

相信各位看官對RxJava早有耳聞,那麼關於什麼是RxJava我就不再贅述了,不知道的可自行百度。如果你已經大致瞭解過什麼是RxJava,想開始學習,那麼本文不失爲你良好的選擇,爲什麼這麼說呢,因爲我也是剛學幾天,正所謂“知己知彼,百戰不殆”。網上流傳,RxJava的入門門檻高,而經過我這幾天的學習,我反而不那麼認爲,精通我不敢說,但入門確實也不難,不信?我先來個簡單的例子預熱一下。

先創建個數據發射源,很好理解,就是發射數據用的:

   Observable<String> sender = Observable.create(new Observable.OnSubscribe<String>() {

           @Override
            public void call(Subscriber<? super String> subscriber) {

                subscriber.onNext("Hi,Weavey!");  //發送數據"Hi,Weavey!"
            }
        });

再創建個數據接收源,同理,接收數據用的:

   Observer<String> receiver = new Observer<String>() {

            @Override
            public void onCompleted() {

                //數據接收完成時調用
            }

            @Override
            public void onError(Throwable e) {

                //發生錯誤調用
            }

            @Override
            public void onNext(String s) {

               //正常接收數據調用
                System.out.print(s);  //將接收到來自sender的問候"Hi,Weavey!"
            }
        };

好了,將發射源和接收源關聯起來:

  sender.subscribe(receiver);

這樣就形成RxJava一個簡單的用法,sender發射"Hi,Weavey!",將會被receiver的onNext的接收,通過這個例子,也許你會想到“異步”、“觀察者模式”,沒錯,這些都是RxJava所做的事情,並且讓他們變得更簡單和簡潔,而RxJava所有的一切都將圍繞這兩個點展開,一個是發射數據,一個是接收數據,是不是很通俗易懂?如果你理解了這點或者你已經知道RxJava就是這麼一回事,那麼恭喜你,你已經一隻腳跨進RxJava的大門了,如果不是!!!!那也無所謂,請繼續往下看...


論概念的重要性

網上關於RxJava的博文也有很多,我也看過許多,其中不乏有優秀的文章,但絕大部分文章都有一個共同點,就是側重於講RxJava中各種強大的操作符,而忽略了最基本的東西——概念,所以一開始我也看的一臉懵逼,看到後面又忘了前面的,腦子裏全是問號,這個是什麼,那個又是什麼,這兩個長得怎麼那麼像。舉個不太恰當的例子,概念之於初學者,就像食物之於人,當你餓了,你會想吃麪包、牛奶,那你爲什麼不去吃土呢,因爲你知道麪包牛奶是用來幹嘛的,土是用來幹嘛的。同理,前面已經說過,RxJava無非是發送數據與接收數據,那麼什麼是發射源,什麼是接收源,這就是你應該明確的事,也是RxJava的入門條件之一,下面就依我個人理解,對發射源和接收源做個歸類,以及RxJava中頻繁出現的幾個“單詞”解釋一通,說的不好還請海涵,歡迎補充。


基本概念

Observable:發射源,英文釋義“可觀察的”,在觀察者模式中稱爲“被觀察者”或“可觀察對象”;

Observer:接收源,英文釋義“觀察者”,沒錯!就是觀察者模式中的“觀察者”,可接收Observable、Subject發射的數據;

Subject:Subject是一個比較特殊的對象,既可充當發射源,也可充當接收源,爲避免初學者被混淆,本章將不對Subject做過多的解釋和使用,重點放在Observable和Observer上,先把最基本方法的使用學會,後面再學其他的都不是什麼問題;

Subscriber:“訂閱者”,也是接收源,那它跟Observer有什麼區別呢?Subscriber實現了Observer接口,比Observer多了一個最重要的方法unsubscribe( ),用來取消訂閱,當你不再想接收數據了,可以調用unsubscribe( )方法停止接收,Observer 在subscribe() 過程中,最終也會被轉換成 Subscriber 對象,一般情況下,建議使用Subscriber作爲接收源;

Subscription :Observable調用subscribe( )方法返回的對象,同樣有unsubscribe( )方法,可以用來取消訂閱事件;

Action0:RxJava中的一個接口,它只有一個無參call()方法,且無返回值,同樣還有Action1,Action2...Action9等,Action1封裝了含有 1個參的call()方法,即call(T t),Action2封裝了含有 2 個參數的call方法,即call(T1 t1,T2 t2),以此類推;

Func0:與Action0非常相似,也有call()方法,但是它是有返回值的,同樣也有Func0、Func1...Func9;


基本用法

  • Observable的創建
    1.使用create( ),最基本的創建方式:

    normalObservable = Observable.create(new Observable.OnSubscribe<String>() {
      @Override
      public void call(Subscriber<? super String> subscriber) {
          subscriber.onNext("create1"); //發射一個"create1"的String
          subscriber.onNext("create2"); //發射一個"create2"的String
          subscriber.onCompleted();//發射完成,這種方法需要手動調用onCompleted,纔會回調Observer的onCompleted方法
      }});

    2.使用just( ),將爲你創建一個Observable並自動爲你調用onNext( )發射數據:

    justObservable = Observable.just("just1","just2");//依次發送"just1""just2"

    3.使用from( ),遍歷集合,發送每個item:

    List<String> list = new ArrayList<>();
    list.add("from1");
    list.add("from2");
    list.add("from3");
    fromObservable = Observable.from(list);  //遍歷list 每次發送一個
    /** 注意,just()方法也可以傳list,但是發送的是整個list對象,而from()發送的是list的一個item** /

    4.使用defer( ),有觀察者訂閱時才創建Observable,並且爲每個觀察者創建一個新的Observable:

    deferObservable = Observable.defer(new Func0<Observable<String>>() {
      @Override
      //注意此處的call方法沒有Subscriber參數
      public Observable<String> call() {
          return Observable.just("deferObservable");
      }});

    5.使用interval( ),創建一個按固定時間間隔發射整數序列的Observable,可用作定時器:

    intervalObservable = Observable.interval(1, TimeUnit.SECONDS);//每隔一秒發送一次

    6.使用range( ),創建一個發射特定整數序列的Observable,第一個參數爲起始值,第二個爲發送的個數,如果爲0則不發送,負數則拋異常:

    rangeObservable = Observable.range(10, 5);//將發送整數10,11,12,13,14

    7.使用timer( ),創建一個Observable,它在一個給定的延遲後發射一個特殊的值,等同於Android中Handler的postDelay( )方法:

    timeObservable = Observable.timer(3, TimeUnit.SECONDS);  //3秒後發射一個值

    8.使用repeat( ),創建一個重複發射特定數據的Observable:

    repeatObservable = Observable.just("repeatObservable").repeat(3);//重複發射3次
  • Observer的創建

    mObserver = new Observer<String>() {
      @Override
      public void onCompleted() {
          LogUtil.log("onCompleted");
      }
      @Override
      public void onError(Throwable e) {
      }
      @Override
      public void onNext(String s) {
          LogUtil.log(s);
      }};

    ok,有了Observable和Obsever,我們就可以隨便玩了,任取一個已創建的Observable和Observer關聯上,即形成一個RxJava的例子,如:

    justObservable.subscribe(mObserver);

    mObserver的onNext方法將會依次收到來自justObservable的數據"just1""just2",另外,如果你不在意數據是否接收完或者是否出現錯誤,即不需要Observer的onCompleted()onError()方法,可使用Action1subscribe()支持將Action1作爲參數傳入,RxJava將會調用它的call方法來接收數據,代碼如下:

    justObservable.subscribe(new Action1<String>() {
        @Override
        public void call(String s) {
    
              LogUtil.log(s);
         }});

    以上就是RxJava最簡單的用法。看到這裏,我也不知道我寫的是否簡單明瞭,也許你會想,“哎呀,寫個異步的東西,怎麼這麼麻煩,爲什麼不用Thread+Handler呢”,那你就錯了,RxJava也以代碼的簡潔深受廣大用戶喜愛,簡潔不能理解爲代碼量少,而是隨着邏輯的複雜,需求的更改,代碼可依然能保持極強的閱讀性,舉個簡單的例子(前方高能預警~~~),領導要我從數據庫的用戶表查找出所有用戶數據,我二話不說拿出心儀的RxJava就寫:

         Observable.create(new Observable.OnSubscribe<List<User>>() {
              @Override
              public void call(Subscriber<? super List<User>> subscriber) {
                  List<User> userList = null;
                  ···
                  //從數據庫獲取用戶表數據並賦給userList
                  ···
                  subscriber.onNext(userList);
              }
          }).subscribe(new Action1<List<User>>() {
              @Override
              public void call(List<User> users) {
    
                  //獲取到用戶信息列表
              }
          });

    但是,領導突然又不想要所有用戶了,只要名字叫“小明”的用戶,行吧,領導最大,我改(假設名字唯一):

        Observable.create(new Observable.OnSubscribe<List<User>>() {
              @Override
              public void call(Subscriber<? super List<User>> subscriber) {
                  List<User> userList = null;
                  ···
                  //從數據庫獲取用戶表數據並賦給userList
                  ···
                  subscriber.onNext(userList);
              }
          }).flatMap(new Func1<List<User>, Observable<User>>() {
              @Override
              public Observable<User> call(List<User> users) {
                  return Observable.from(users);
              }
          }).filter(new Func1<User, Boolean>() {
              @Override
              public Boolean call(User user) {
                  return user.getName().equals("小明");
              }
          }).subscribe(new Action1<User>() {
              @Override
              public void call(User user) {
                  //拿到謎之小明的數據
              }
          });

    搞定,這時候領導又說,我不要小明瞭,我要小明的爸爸的數據,(坑爹啊~~),我繼續改:

    Observable.create(new Observable.OnSubscribe<List<User>>() {
              @Override
              public void call(Subscriber<? super List<User>> subscriber) {
                  List<User> userList = null;
                  ···
                  //從數據庫獲取用戶表數據並賦給userList
                  ···
                  subscriber.onNext(userList);
              } 
         }).flatMap(new Func1<List<User>, Observable<User>>() {
              @Override
              public Observable<User> call(List<User> users) {
                  return Observable.from(users);
              }
          }).filter(new Func1<User, Boolean>() {
              @Override
              public Boolean call(User user) {
                  return user.getName().equals("小明");
              }
          }).map(new Func1<User, User>() {
              @Override
              public User call(User user) { 
                  //根據小明的數據user從數據庫查找出小明的父親user2
                  return user2;
              }
          }).subscribe(new Action1<User>() {
              @Override
              public void call(User user2) {
                //拿到謎之小明的爸爸的數據
              }
          });

    搞定,“還想怎麼改?領導請說···”。
    以上例子,涉及到幾個操作符,初學者可能無法理解,但是無所謂,這不是重點,我的目的只是爲了向你展示RxJava在需求不斷變更、邏輯愈加複雜的情況下,依舊可以保持代碼簡潔、可閱讀性強的一面,沒有各種回調,也沒有謎之縮進!



Retrofit篇

Retrofit與okhttp共同出自於Square公司,retrofit就是對okhttp做了一層封裝。把網絡請求都交給給了Okhttp,我們只需要通過簡單的配置就能使用retrofit來進行網絡請求了,其主要作者是Android大神JakeWharton


導包:

compile 'com.squareup.retrofit2:retrofit:2.0.0-beta4'//Retrofit2所需要的包
compile 'com.squareup.retrofit2:converter-gson:2.0.0-beta4'//ConverterFactory的Gson依賴包
compile 'com.squareup.retrofit2:converter-scalars:2.0.0-beta4'//ConverterFactory的String依賴包
*這裏需要值得注意的是導入的retrofit2包的版本必須要一致,否則就會報錯。


首先定義我們請求的Api,我們假設是這樣的

http://106.3.227.33/pulamsi/mobileLogin/submit.html
與Okhttp不同的是,Retrofit需要定義一個接口,用來返回我們的Call對象,這裏示範的是Post請求:

public interface RequestServes {
    @POST("mobileLogin/submit.html")
    Call<String> getString(@Query("loginname") String loginname,
                           @Query("nloginpwd") String nloginpwd);
}


Retrofit提供的請求方式註解有@GET@POST,參數註解有@PATH@Query等,我們只介紹常用的;前兩個顧名思義就是定義你的請求方式Get or Post,後面的@PATH指的是通過參數填充完整的路徑,一般用法:

@GET("{name}")
Call<User>
getUser(@Path("name") String name)
;

這裏的參數username會被填充至{name}中,形成完整的Url請求地址,{name}相當於一個佔位符;

@Query就是我們的請求的鍵值對的設置,我們構建Call對象的時候會傳入此參數,

1
2
3
@POST("mobileLogin/submit.html")
   	Call<String> getString(@Query("loginname") String loginname,
                          @Query("nloginpwd") String nloginpwd);
這裏@Query("loginname")就是鍵,後面的loginname就是具體的值了,值得注意的是Get和Post請求,都是這樣填充參數的;


接口寫完了之後我們需要來定義Retrofit對象來進行請求了;

創建一個Retrofit 對象

Retrofit retrofit = new Retrofit.Builder()
               .baseUrl("http://106.3.227.33/pulamsi/")
               //增加返回值爲String的支持
               .addConverterFactory(ScalarsConverterFactory.create())
               //增加返回值爲Gson的支持(以實體類返回)
               .addConverterFactory(GsonConverterFactory.create())
               //增加返回值爲Oservable<T>的支持
               .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
               .build();

這裏的baseUrl加上之前@POST("mobileLogin/submit.html")定義的參數形成完整的請求地址;

addConverterFactory(ScalarsConverterFactory.create())的意思是構建了一個返回支持,如果你的Call對象的泛型接收另外的格式需要添加另外的支持,上述代碼已經列出;

接下來我們用這個Retrofit對象創建一個RequestSerives接口對象,也就是我們之前定義的那個接口,並且得到我們的Call對象;


RequestSerives requestSerives = retrofit.create(RequestSerives.class);//這裏採用的是Java的動態代理模式
Call<String> call = requestSerives.getString("userName", "1234");//傳入我們請求的鍵值對的值
利用得到的Call對象,然後我們就發出網絡請求了:

call.enqueue(new Callback<String>() { @Override public void onResponse(Call<String> call, Response<String> response) { Log.e("===","return:"response.body().toString()); } @Override public void onFailure(Call<String> call, Throwable t) { Log.e("===","失敗"); } });

導入Dagger2

使用Dagger2之前需要一些配置,該配置是在Android Studio中進行操作。

在工程的build.gradle文件中添加android-apt插件(該插件後面介紹)

buildscript {

    ....

    dependencies {

        classpath 'com.android.tools.build:gradle:2.1.0'
        // 添加android-apt 插件
        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
    }
}

在app的中的build.gradle文件中添加配置

apply plugin: 'com.android.application'
// 應用插件
apply plugin: 'com.neenbedankt.android-apt'

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.2"

    defaultConfig {
        applicationId "com.mahao.alex.architecture"
        minSdkVersion 15
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}


dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:23.3.0'

    // dagger 2 的配置
    compile 'com.google.dagger:dagger:2.4'
    apt 'com.google.dagger:dagger-compiler:2.4'
    compile 'org.glassfish:javax.annotation:10.0-b28'// 添加java 註解庫
}

以上兩個配置就可以了。

android-aptGradle編譯器的插件,根據其官方文檔,主要兩個目的:

  • 編譯時使用該工具,最終打包時不會將該插件打入到apk中。

  • 能夠根據設置的源路徑,在編譯時期生成相應代碼。

在導入類庫時,

    compile 'com.google.dagger:dagger:2.4'
    apt 'com.google.dagger:dagger-compiler:2.4'

dagger是主要的工具類庫。dagger-compiler爲編譯時期生成代碼等相關的類庫。

android-apt的文檔中,也推薦使用這種方式。因爲,編譯時期生成代碼的類庫在運行期並不需要,那麼將其分爲兩個庫,(運行類庫dagger)和(編譯器生成代碼類庫(dagger-compiler)),那麼在打包時,就不需要將dagger-compiler打入其中(用不到),減小APK 的大小。

Dagger2篇

Dagger2的使用,需要大量的學習成本,不是很能夠容易的上手並使用。該博客將從簡單入手,儘可能的使用簡單的例子演示Dagger2的功能。

一個東西需要先會用,然後才更好的學習原理。該篇博客的目的主要是講解如何使用。後面會有專門的分析源碼的博客。

在之前的分析中,通過Dagger2的目的是將程序分爲三個部分。
- 實例化部分:對象的實例化。類似於容器,將類的實例放在容器裏。
- 調用者:需要實例化對象的類。
- 溝通橋樑:利用Dagger2中的一些API 將兩者聯繫。

先看實例化部分(容器),在此處是Module


@Module   //提供依賴對象的實例
public class MainModule {

    @Provides // 關鍵字,標明該方法提供依賴對象
    Person providerPerson(){
        //提供Person對象
        return new Person();
    }


}

溝通部分Component

@Component(modules = MainModule.class)  // 作爲橋樑,溝通調用者和依賴對象庫
public interface MainComponent {

    //定義注入的方法
    void inject(MainActivity activity);

}

使用者Actvity中調用。

public class MainActivity extends AppCompatActivity{

    @Inject   //標明需要注入的對象
    Person person;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // 構造橋樑對象
        MainComponent component = DaggerMainComponent.builder().mainModule(new MainModule()).build();

        //注入
        component.inject(this);

    }
}

看一下Person

public class Person {

    public Person(){
        Log.i("dagger","person create!!!");
    }


}

最後結果不在演示。其過程如下:

  • 創建Component(橋樑),並調用注入方法。
        // 構造橋樑對象
        MainComponent component = DaggerMainComponent.builder().mainModule(new MainModule()).build();

        //注入
        component.inject(this);
  • 查找當前類中帶有@Inject的成員變量。
    @Inject   //標明需要注入的對象
    Person person;
  • 根據成員變量的類型從Module中查找哪個有@Provides註解的方法返回值爲當前類型。
    @Provides // 關鍵字,標明該方法提供依賴對象
    Person providerPerson(){
        //提供Person對象
        return new Person();
    }

這裏寫圖片描述

在使用過程出現了很多註解:

  • @Module:作爲實例對象的容器。
  • @Provides:標註能夠提供實例化對象的方法。
  • @Component:作爲橋樑,注入對象的通道。
  • @Inject:需要注入的方法

如上使用有一種變通,修改MainModulePerson類。

@Module   //提供依賴對象的實例
public class MainModule {

/*
    @Provides // 關鍵字,標明該方法提供依賴對象
    Person providerPerson(){
        //提供Person對象
        Log.i("dagger"," from Module");
        return new Person();
    }

*/

}
public class Person {

    @Inject  // 添加註解關鍵字
    public Person(){
        Log.i("dagger","person create!!!");
    }

}

Module中的providePerson()方法註釋,在Person中添加@Inject註解,依然能夠實現。

邏輯如下:
- 先判斷Module中是否有提供該對象實例化的方法。
- 如果有則返回。結束。
- 如果沒有,則查找該類的構造方法,是否有帶有@Inject的方法。如過存在,則返回。

@Singleton 單例註解

假如,對於同一個對象,我們需要注入兩次,如下方式

 public class MainActivity extends AppCompatActivity{

    @Inject
    Person person;

    @Inject
    Person person2;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // 構造橋樑對象
        MainComponent component = DaggerMainComponent.builder().mainModule(new MainModule()).build();

        //注入
        component.inject(this);

        // 打印兩個對象的地址
        Log.i("dagger","person = "+ person.toString()+"; person2 = "+ person2.toString());
    }
}

看一下結果:

person = com.mahao.alex.architecture.dagger2.Person@430d1620; person2 = com.mahao.alex.architecture.dagger2.Person@430d17c8

可見兩個對象不一致。也就是說創建了兩個對象。

可以在提供實例化對象的方法上添加@Singleton註解

 @Provides // 關鍵字,標明該方法提供依賴對象
    @Singleton
    Person providerPerson(){

        return new Person();
    }

同時,對於MainComponent也需要添加註解,不添加會無法編譯

@Singleton
@Component(modules = MainModule.class)  // 作爲橋樑,溝通調用者和依賴對象庫
public interface MainComponent {
    //定義注入的方法
    void inject(MainActivity activity);

}

此時在Log,會發現兩個對象的地址一樣,可見是同一個對象。

person = com.mahao.alex.architecture.dagger2.Person@4310f898; person2 = com.mahao.alex.architecture.dagger2.Person@4310f898

那麼不同的Activity之間,能否保持單例呢?

創建一個新的Activity,代碼如下:

public class Main2Actvity extends AppCompatActivity {

    @Inject
    Person person;


    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // 構造橋樑對象
        MainComponent component = DaggerMainComponent.builder().mainModule(new MainModule()).build();

        //注入
        component.inject(this);

        Log.i("dagger","person = "+ person.toString());
    }
}

結果如下:

 person create!!!
 person = com.mahao.alex.architecture.dagger2.Person@4310f898; person2 = com.mahao.alex.architecture.dagger2.Person@4310f898
 person create!!!
 person = com.mahao.alex.architecture.dagger2.Person@43130058

可見,@Singleton只對一個Component有效,即其單例所依賴Component對象。

需要參數的實例化對象

Person的構造方法發生了變化,需要傳入一個Context,代碼如下:

public class Person {

    private Context mContext;

    public Person(Context context){
        mContext = context;
        Log.i("dagger","create");
    }

}

這樣的話,我們需要修改MainModule


@Module   //提供依賴對象的實例
public class MainModule {

    private Context mContext;

    public MainModule(Context context){
        mContext = context;
    }


    @Provides
    Context providesContext(){
        // 提供上下文對象
        return mContext;
    }

    @Provides // 關鍵字,標明該方法提供依賴對象
    @Singleton
    Person providerPerson(Context context){

        return new Person(context);
    }

}
  • 修改providerPerson方法,傳入Context對象。
  • 添加providesContext(),用以提供Context對象。

看一下使用

 // 構造橋樑對象
        MainComponent component = DaggerMainComponent.builder().mainModule(new MainModule(this)).build();

        //注入
        component.inject(this);

邏輯:

  • 根據@Inject註解,查找需要依賴注入的對象。
  • MainModule中根據返回值,找到providerPerson(Context context)對象。
  • 發現其需要傳入參數Context,找到moudule中具有返回值爲Context的方法providesContext()
  • 最後就成功的構建了實例化對象。

可能會有疑問,我既然module中已經保存了Context對象,那麼爲什麼不直接使用Context對象呢,因爲解耦,如果使用了保存的對象,會導致下次Context獲取發生變化時,需要修改providerPerson(Context context)中的代碼。

在編寫Module中,不能出現傳入參數和返回參數一致的情況,會導致死循環。

很容易理解,需要的和獲取的是同一個方法,循環調用。

依賴一個組件

在使用中,往往會有依賴另一個組件的情況。比如,在AppMoudle中能夠提供Context對象,如下:

@Module
public class AppModule {

    private Context mContext;

    public AppModule(Context context){
        mContext = context;
    }

    @Provides
    Context providesContext(){
        // 提供Context對象 
        return mContext;
    }

}

而在另一個Module中需要依賴Context對象,那麼怎麼寫呢?

首先編寫當前AppModuleComponent

/**
 *
 * 全局的Component 組件
 * Created by MH on 2016/7/18.
 */

@Component(modules = AppModule.class)
public interface AppComponent {

    // 向其下層提供Context 對象
    Context proContext();
}

在此種,因爲Module中需要向下層提供Context對象,而其與下層的聯繫時通過Component
,所以需要在這裏聲明一個其所提供對象的方法。以便下層Module獲取。

/**
 *
 * 下層Module類
 * Created by MH on 2016/7/18.
 */
@Module
public class ActivityMoudule {

    @Provides
    Person providePerson(Context context){
        // 此方法需要Context 對象
        return new Person(context);
    }
}

/**
 *  子的Component
 * Created by MH on 2016/7/18.
 */
@Component(dependencies = AppComponent.class,modules = ActivityMoudule.class)
public interface ActivityComponent {

    // 注入
    void inject(MainActivity activity);
}

在子Component中,有一句關鍵的註解dependencies = AppComponent.class,添加了上層依賴。

看一下使用

        // 依賴對象 Component
        AppComponent appCom = DaggerAppComponent.builder().appModule(new AppModule(this)).build();

        // 子類依賴對象 ,並注入
        DaggerActivityComponent.builder()
                .appComponent(appCom)
                .activityMoudule(new ActivityMoudule())
                .build()
                .inject(this);

在其中使用過程中,有很重的兩點。

  • 父依賴的Component中需要添加提供對象的接口。
  • 子依賴的Component中的註解中添加dependencies = AppComponent.class

@Qualifier 自定義標記

在使用中,會出現兩個方法返回對象相同時的情況,那麼如何區分呢。

Person對象具有兩個構造方法,根據不同的參數值構造不同的方法。

public class Person {

    private Context mContext;

    public Person(Context context){
        mContext = context;
        Log.i("dagger","create");
    }

    public Person(String name){
        Log.i("dagger",name);
    }
}

ActivityModule中添加@Named標記

@Module
public class ActivityMoudule {

    @Named("Context")  // 通過context創建Person 對象
    @Provides
    Person providePersonContext(Context context){
        // 此方法需要Context 對象
        return new Person(context);
    }


    @Named("name")  // 通過name創建Person 對象
    @Provides
    Person providePersonName(){
        // 此方法需要name
        return new Person("1234");
    }
}

使用時,也需要添加此標記

    public class MainActivity extends AppCompatActivity{

    @Named("context") // 標記
    @Inject
    Person person;

    @Named("name")  // 標記
    @Inject
    Person person2;


    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);
        //注入
        component.inject(this);*/

        // 依賴對象 Component
        AppComponent appCom = DaggerAppComponent.builder().appModule(new AppModule(this)).build();

        // 子類依賴對象 ,並注入
        DaggerActivityComponent.builder()
                .appComponent(appCom)
                .activityMoudule(new ActivityMoudule())
                .build()
                .inject(this);
    }

    }

使用時,使用者的@Inject上,必須要加入註解@Named("xxx"),不然編譯期會報錯。

這樣使用過程中,雖然解決了問題,但是通過字符串標記一個對象,容易導致前後不匹配,可以通過自定義註解的方式解決。

添加兩個註解,分別對應Contextname

@Qualifier  // 關鍵詞
@Retention(RetentionPolicy.RUNTIME)  // 運行時仍可用
public @interface PersonForContext {
    // Context 對象的註解
}
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface PersonForName {
    // name 對象的註解
}

在使用@Named("")的地方替換爲上面的註解


   @PersonForContext  // 通過context創建Person 對象
    @Provides
    Person providePersonContext(Context context){
        // 此方法需要Context 對象
        return new Person(context);
    }


    @PersonForName  // 通過name創建Person 對象
    @Provides
    Person providePersonName(){
        // 此方法需要Context 對象
        return new Person("123");
    }

注入時:


    @PersonForContext // 標記
    @Inject
    Person person;


    @PersonForName // 標記
    @Inject
    Person person2;

Scope

在前面中提到@Singleton註解,該註解能夠使同一個Component中的對象保持唯一,即單例。

回憶一下,如下方式:

    @Provides // 關鍵字,標明該方法提供依賴對象
    @Singleton
    Person providerPerson(Context context){

        return new Person(context);
    }

Module中,對應方法中添加@Singleton註解,同時其所在的Component中,類生命上也需要添加註解


@Singleton
@Component(modules = MainModule.class)  // 作爲橋樑,溝通調用者和依賴對象庫
public interface MainComponent {
}

如果我們看這個意思,感覺其內部應該做了很多的實現,用以達到單例。其實,沒我們想的那麼複雜。

看一下@Singleton的實現

@Scope //註明是Scope 
@Documented  //標記在文檔 
@Retention(RUNTIME)  // 運行時級別
public @interface Singleton {}

通過@Scope定義的一個新的註解。

在之前的,我們知道該單例是依託於他所在的Component組件。那麼我們是否可以這樣理解,因爲方法上添加的@Scope標記的註解和Component上添加的@Scope標記的註解相同(確實相同,同爲@Singleton),就表明了該方法提供的實例對象在Component保持唯一。保持唯一的條件是通過@Scope標記的註解相同。

通過在上面的依賴層級上,Android中通常定義兩個生命週期。

全局的生命週期PerApp

/**
 * 全局的生命週期單例
 */
@Scope
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface PerApp {

}

在使用中完全和@Singleton相同。

@Module
public class AppModule {

    private Context mContext;

    public AppModule(Context context){
        mContext = context;
    }

    @Provides
    @PerApp  // 添加該標記表明該方法只產生一個實例
    Context providesContext(){
        // 提供上下文對象
        return mContext;
    }

}
@PerApp // 因爲Module 中使用了該標記,所以需要在此添加
@Component(modules = AppModule.class)
public interface AppComponent {

    // 向其下層提供Context 對象
    Context proContext();
}

因爲單例的依託於他所在的Component中,所以需要在Application中進行實例化。

public class App extends Application {

    // 爲什麼可以使用靜態
    public static AppComponent appComponent;


    @Override
    public void onCreate() {
        super.onCreate();

        // 實例化
        appComponent = DaggerAppComponent.builder().appModule(new AppModule(this)).build();

    }
}

爲什麼可以使用靜態的,因爲該AppComponent對象的生命週期是整個App。那麼在使用中,其所在Module中的實例化對象,可以保持全局單例。

一個Activity的生命週期PerActivity

有全局的單例,而對於一個Activity,他也有些對象需要保持單例。我們需要定義該註解。

/**
 * Activity 單例生命週期
 */
@Scope
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface PerActivity {
}

會發現,除了定義名不一樣,其餘都和PerApp一樣。在前面,說過這樣一句話:保持唯一的條件是通過@Scope標記的註解相同。

@Module
public class ActivityMoudule {

    @PersonForContext
    @Provides
    @PerActivity  // 添加標記,生命其所構造的對象單例
    Person providePersonContext(Context context){
        // 此方法需要Context 對象
        return new Person(context);
    }

    .....
}

@PerActivity  // ActivityMoudule 中使用了該標記
@Component(dependencies = AppComponent.class,modules = ActivityMoudule.class)
public interface ActivityComponent {

    // 注入
    void inject(MainActivity activity);
}

使用方式,因爲其所保持的單例是在Activity中,具體使用如下。

public class MainActivity extends AppCompatActivity{

    @PersonForContext // 標記
    @Inject
    Person person;


    @PersonForName // 標記
    @Inject
    Person person2;


    /**
     * 不使用靜態的,因爲該Component只是針對於該Activity,而不是全局的
     */
    ActivityComponent  activityComponent;


    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);


        activityComponent = DaggerActivityComponent.builder()
                .appComponent(App.appComponent)  // 添加了全局的AppComponent組件,可以使用全局的實例化對象
                .activityMoudule(new ActivityMoudule())
                .build();


        activityComponent.inject(this);

對於具有依賴關係的Component,不能使用相同的Scope,如果使用相同的會帶來語意不明

懶加載Lazy和強制重新加載Provider

public class MainActivity extends AppCompatActivity{

    @PersonForContext // 標記
    @Inject
    Lazy<Person> lazyPerson; // 注入Lazy元素


    @PersonForName // 標記
    @Inject
    Provider<Person> providerPerson; // 注入Provider


    /**
     * 不使用靜態的,因爲該Component只是針對於該Activity,而不是全局的
     */
    ActivityComponent  activityComponent;


    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);


        activityComponent = DaggerActivityComponent.builder()
                .appComponent(App.appComponent)  // 添加了全局的AppComponent組件
                .activityMoudule(new ActivityMoudule())
                .build();


        activityComponent.inject(this);


        Person person = lazyPerson.get();// 調用該方法時纔會去創建Person,以後每次調用獲取的是同一個對象


        // 調用該方法時纔回去創建Person1,以後每次調用都會重新加載Module中的具體方法,根據Module中的實現,可能相同,可能不相同。
        Person person1 = providerPerson.get();
    }
}
說這麼多都沒用,不如實踐一下。下面提供一個鏈接github上的一個開源項目,RXjava+Retrofit+dagger2的框架。希望這篇文章對想要學習RXjava+Retrofit+dagger2有一些幫助
點擊打開鏈接
發佈了20 篇原創文章 · 獲贊 24 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章