1. 程式人生 > >關於Dagger2的一些個人理解

關於Dagger2的一些個人理解

寫作目的

現在基本上有一些規模的專案,都使用了Dagger2,也不是裝13,個人感覺也的確是大勢所趨,Dagger2的確有它的優勢。藉著手上的專案,學習了一下Dagger2,打算用在公司的專案中。今天就來以自己初學者的角度來談談dagger2的認識。

問題由來

我是目前是移動端開發者,主要從事的是Android端開發。在andorid開發過程中,按照套路我們會有SharePreferenceManager、DatabaseManager、NetWorkManager、CacheManager等各種Manager,當然你的叫法也可以不同,他們都會存在初始化,考慮到有種可能的初始化順序,如下圖:
這裡寫圖片描述

我們可能需要先使用Application初始化SpManager(SharePreferenceManager)拿到一些關鍵資料,從而通過這些資料來初始化DbManager,NWManager或者CacheManager。可以看到我們的各個物件就存在初始化先後的問題,試想如果系統過於複雜,像這種物件初始化有相互依賴的各種關係,想必是非常複雜的;如果是我們用程式碼去寫,也是很繁雜的而且沒多大效率的;還有一個問題,看下圖:
這裡寫圖片描述
如果有一天,我們的E物件突然改變了初始化方法,而且很多地方都需要E物件的初始化,那我們是不是很懵,因為有很多地方需要修改,那苦逼的程式設計師是不是很難受,這也就是我們常說的程式的耦合性,改一處,就要變100處地方。很顯然dagger的出現就是為了解決這個問題的。具體怎麼解決,我還有想用一張圖來說明我的想法:
這裡寫圖片描述

假設dagger在初始化某物件時,同時需要a b c g四個物件,它就會在容器中尋找(這裡的尋找有可能是上一次建立的),找到了就拿來使用,沒有找到就需要靠自身去建立了。你可以看到a d g物件都會存在,此時c物件不存在,那麼此時的c物件的建立又變成了XObject物件的建立,此時需要找到c物件建立時,需要m物件和p物件,一環一環套下去,然後直到所有的需要的物件都建立完成,然後再遞迴到需要它需要建立的物件,直到所有的物件初始化完成。明白了這個道理,感覺學習dagger2就不難了。

開始Dagger2

配置

AS , gradle配置:

    //dagger2
    compile 'com.google.dagger:dagger:2.14.1'
annotationProcessor 'com.google.dagger:dagger-compiler:2.14.1'

新建Student類:

public class Student {
    private String name ;
    private int score ;

    @Inject
    public Student(){
        this.name = "zhangsan" ;
        this.score = 30 ;
    }

    public Student(String name, int score) {
        this.name = name;
        this.score = score;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", score=" + score +
                '}';
    }
}

和普通java類一致,只是在構造方法上使用了註解@javax.inject.Inject註解,第一步完成;

建立介面StudentComponent,當然這個介面名字你可以亂寫,但是為了規範我還是建議你初始化啥物件,就寫這個名字,介面很簡單:

@Component
public interface StudentComponent {
    void inject(SecondActivity activity);
}

我們需要一個@dagger.Component的註解,和一個SecondActivity,這個SecondActivity是我們需要用到的Activity,在這個Activity中我們需要對Student物件進行初始化:
SecondActivity原始碼:

public class SecondActivity extends AppCompatActivity {

    @Inject
    Student su;

    TextView tv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        tv = findViewById(R.id.id_tv_content);
    }

    public void getStudentInfo(View view) {
        tv.setText(su.toString());
    }
}

在我們需要的初始化的物件上Student也加上@Inject註解,其他的好像真的完事了,就這麼簡單?當然,你需要將make一下你的project(這裡吐槽一下當年使用window系統最終放棄了dagger2無數次,make專案絕對是我不想堅持下去的最大原因,太尼瑪花時間了),然後加上一句話:
這裡寫圖片描述
編譯執行,我們可以看到:
這裡寫圖片描述
的確,程式沒有我們想象中報了異常,而是真的被初始化,而且被賦值了。第一次用Dagger2的感覺就是這麼神奇。當然這算得上Dagger2最簡單的應用場景吧。

不過,距離真正去使用Dagger2還有有一定的差距,因為我們還缺少一樣叫module的類。真正的Dagger2是存在三部分的。
1. 物件的例項化部分,相當於我們的new Object(); Dagger提供類似容器功能,提供例項化的功能;這一個叫module
2. 需要例項化的部分,你例項化的物件需要被用得到,這是個非常抽象的部分,可以使是Activity,也可以是Fragment,或者是任意的Java物件
3. 連線部分,將例項化的物件和需要用到這些物件的部分連線起來。這一個在dagger中叫Component

好了,有了上面的簡單說明,我們來個真正的dagger用法。來個Student2物件,相當於我們的資料:


public class Student2 {
    private String name ;
    private int score ;

    public Student2(String name, int score) {
        this.name = name;
        this.score = score;
    }

    @Override
    public String toString() {
        return "Student2{" +
                "name='" + name + '\'' +
                ", score=" + score +
                '}';
    }
}

現在我們需要module元件,給我們提供物件例項化的,這個可能稍微有點拐彎:

@Module
public class Student2Module {

    @Provides
    public String provideName() {
        return "lisi" ;
    }

    @Provides
    public int provideScore(){
        return 98 ;
    }

    @Provides
    public Student2 provideStudent2(String name , int score){
        return new Student2(name,score);
    }
}

我們看到了@provides,這是來自Dagger的註解,正如翻譯過來,就是給你提供資料量,它提供了一個Student2物件,那麼問題來了,那namescore是哪裡來的呢?同樣我們看到了它提供另外兩個方法provideScore()provideName(),同樣也被@Provides註解修飾,意思就是我給你提供資料量。

已經有資料可以提供了,那哪裡需要這個資料呢?來,看一下我們的Activity,在Activity中有地方需要它:

public class ThirdActivity extends AppCompatActivity {

    @Inject     //這裡使用@Inject 說明我需要被初始化
    Student2 stu ;

    TextView tv ;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
       tv = findViewById(R.id.id_tv_content);
    }

    //這裡是按鈕的點選作用,我們需要看一下這個Student2物件是否被初始化  
    public void getStudentInfo(View v) {
        tv.setText(stu.toString());
    }

}

資料來源和資料去向都有了,那我們就需要一箇中間的連線著Component了,好了,我們來造一個Component,如下:

@Component(modules = Student2Module.class)  //這就是中間的連結部分,資料來源就是這個modules所對應的資料類
public interface Student2Component {    //Component是介面,相當於中介軟體

    void inject(ThirdActivity activity);  //這個是資料去向,注意不要使用什麼BaseActivity,一定要寫具體的activity
}

好了,然後我們make(build,reBuild)一下我們的專案,然後在ThirdActivity中新增一句程式碼:
這裡寫圖片描述
這個DaggerStudentComponent是Dagger框架自動生成的,它是由你的Component介面然後加上Dagger字首生成的,作用是連線我們的資料來源和資料接收者。點選按鈕,可以看到結果:
這裡寫圖片描述
結果就是我們想要的,也可以看到Student2Module中提供的name和score的確在ActivityStudent2物件中了。是不是很簡單呢?是啊,我們的dagger注入框架就好了,這就是注入的效果。

但是你有沒有一種感覺,為啥Student類和Student2物件中,Student構造器中使用了@Inject註解,但是Student2構造器中沒有使用@Inject呢;為啥Student對應的StudentComponent沒有提供獲取Student方法,而Student2Component中提供了獲取Student2的方法呢?

這就需要我們理解Module作為資料提供者,提供資料也是有先後順序的,一般順序如下:
1. 我現在module資料提供者中找有沒有提供初始化物件的方法;
2. 我找到了,我就返回該物件;
3. 我沒有找到,我就去檢視這個物件的構造器是否被@Inject找到
4. 找到了,那麼自己執行new Object過程;沒有找到,對不起,編譯出錯,說明你dagger方法使用錯誤。

所以,綜上所述,一個物件的構造器Constructor沒有被@Inject修飾,那麼在Module中必須有方法提供初始化物件。

好了,我們接下來就說一說工作和生活中用得比較多的物件,也就是關於dagger的識別符號問題了。

@Singleton

和Java思想中一致,就是單例的意思。這裡的單例依據個人的理解,一個數據接收容器中存在多個數據接收者,那麼多個數據接收者指向的物件是同一個。說的自己都有點不信了,舉個例子:
定義StudentSingleton資料結構:

@Singleton
public class StudentSingleton {

    private String name ;
    private int score ;

    @Inject //看這裡被@Inject修飾了
    public StudentSingleton(String name, int score) {
        this.name = name;
        this.score = score;
    }

}

資料提供者StudentSingletonModule

@Module
public class StudentSingletonModule {

    @Provides
    public String getName(){
        return "tom";
    }

    @Provides
    public int score() {
        return 20;
    }

}

資料需求者SingletonActivity,可以看得到裡面有兩個StudentSingleton物件等待被初始化:

public class SingletonActivity extends AppCompatActivity {

    @Inject
    StudentSingleton singleton1 ;

    @Inject
    StudentSingleton singleton2 ;

    TextView tv;

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

        tv = findViewById(R.id.id_tv_content);

        //build之後 進行物件注入
        DaggerStudentSingleComponent.builder().build().inject(this);
    }

    public void getStudentInfo(View v) {
        tv.setText(singleton1 + "\n" + singleton2);
    }
}

我們看一下結果:
這裡寫圖片描述
我們可以看到兩個StudentSingleton物件指向的相同的地址,那麼說明它們是同一個物件。
同樣,我們在另外一個Activity中也需要一個StudentSingleton,如下程式碼:

public class AnotherStudentSingletonActivity extends AppCompatActivity {

    @Inject
    StudentSingleton stu ;

    TextView tv;

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

        tv = findViewById(R.id.id_tv_content);

        DaggerStudentSingleComponent.builder().build().inject(this);
    tv.setText(stu.toString());
    }
}

獲取的結果為:
這裡寫圖片描述
由此可見,@Singleton註解並不是我們平常意義上的單例,只是在同一個容器的中,指向同一個物件才可能被稱為單例。

當然了,你去掉@Singleton註解,去看看在一個Activity同時獲取兩個StudentSingleton物件,地址是不是一樣呢?答案肯定是不一樣的。

初始化物件時,需要獲取外部引數

這個在Andorid中需求的比較多,很多時候我們需要一個Context物件,到處都是Context物件,舉個例子:
先來個StudentContext物件:

public class StudentContext {
    private String name ;
    private int score ;
    private Context context;

    public StudentContext(){}

    @Inject  //注意@Inject註解修飾的這個構造器
    public StudentContext(String name, int score, Context context) {
        this.name = name;
        this.score = score;
        this.context = context;
    }

    @Override
    public String toString() {
        return "StudentContext{" +
                "name='" + name + '\'' +
                ", score=" + score +
                ", context=" + context +
                '}';
    }
}

在我們的Module中,我們這時需要傳入一下個Context物件:

@Module
public class StudentContextModule {

    private Context context;

    //此時需要你傳入一個Context物件進入  
    public StudentContextModule(Context context) {
        this.context = context;
    }

    @Provides
    public String provideName() {
        return "jerry" ;
    }

    @Provides
    public int provideScore() {
        return 20 ;
    }

    //這裡提供一個Context物件方法 
    @Provides
    public Context provideContext() {
        return context;
    }
}

我們橋接物件為:

@Component(modules = StudentContextModule.class)
public interface StudentContextComponent {
    void inject(StudentContextActivity activity);
}

make一下我們的專案,在資料接收容器StudentContextActivity中,這麼寫:

public class StudentContextActivity extends AppCompatActivity {
    @Inject
    StudentContext sContext;

    TextView tv ;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        tv = findViewById(R.id.id_tv_content);

    //這裡我們就需要提供一個StudentContextModule物件
    //是的,這個物件是我們New的
        DaggerStudentContextComponent.builder().studentContextModule(new StudentContextModule(this)).build().inject(this);
    }

    public void getStudentInfo(View v) {
        tv.setText(sContext.toString());
    }
}

結果為:
這裡寫圖片描述
你可以看到,我們的Context物件也被傳入進去了,其實就是我們的StudentContextModulenew時傳入的this寫入的。

依賴Module

這個意義,就相當我們每個應用中只有一個Application,我們的Activity可以依賴於這個Application,然後可以ToastSharePreference我們想要的東西。那麼就來一個AppModule,全域性來一個Context

@Module
public class AppModule {

    private Context context ;

    public AppModule(Context context) {
        this.context = context;
    }

    @Provides
    public Context provideContext() {
        return context;
    }

    @Provides
    public String provideGlobalName() {
        return "globalName" ;
    }

}

那麼它對應的Component為:

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

    /**
     * 向下提供Context
     * @return
     */
    Context getContext();

    /**
     * 向下提供Name
     * @return
     */
    String getStringName();
}

這裡有一點需要宣告一下,APPComponent將要被依賴,那麼它需要暴露出提供初始化物件的方法,然後依賴它的Component將找不到。
好了,基類被依賴的APPComponent已經有了,現在來一個ActivityComponent,它提供了StudentContext物件方法和score物件方法。

@Module
public class ActivityModule {

    @Provides
    public StudentContext provideStudentContext(Context context,String name , int score) {
        return new StudentContext(name,score,context);
    }

    @Provides
    public int provideActivityScore() {
        return 78 ;
    }

}

那麼重點來了,ActivityComponent將會依賴AppComponentAppComponent將會提供Context物件方法和name物件方法,注意dependencies關鍵字,它指向了依賴的物件,同時依賴的物件也是一個Component

@Component(modules = ActivityModule.class, dependencies = AppComponent.class)
public interface ActivityComponent {

    void inject(DependencyActivity activity);
}

DependencyActivity中:

public class DependencyActivity extends AppCompatActivity {

    @Inject
    StudentContext sContext;

    TextView tv ;

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

        tv  = findViewById(R.id.id_tv_content);

        //先獲取AppComponent物件
        AppComponent appComponent =  DaggerAppComponent.builder().appModule(new AppModule(this)).build();

        //ActivityComponent物件將會依賴於AppComponent
        DaggerActivityComponent.
                builder().
                appComponent(appComponent).
                build().inject(this);
    }

    public void getStudentInfo(View v) {
        tv.setText(sContext.toString());
    }

}

同樣我們列印結果為:
這裡寫圖片描述

@Name註解

主要解決同一個容器中,同時又多個相同物件需要被注入,如果你不做任何指導,那麼dagger就不確定資料注入的唯一性(資料提供者 -> 資料接收者),此時有一種方法就是使用@Name註解:
還是定義一個StudentForNameAt物件,可以看到它有一個namecontext屬性

public class StudentForNameAt {

    private String name ;

    private Context context;

    public StudentForNameAt(String name) {
        this.name = name;
    }

    public StudentForNameAt(Context context) {
        this.context = context;
    }

    @Override
    public String toString() {
        return "StudentForNameAt{" +
                "name='" + name + '\'' +
                ", context=" + context +
                '}';
    }
}

那麼NameAtModule需要這麼寫:

@Module
public class NameAtModule {

    private Context context ;
    private String name ;


    public NameAtModule(Context context,String name) {
        this.context = context;
        this.name = name;
    }

    @Provides
    public Context provideContext() {
        return context;
    }

    @Named("context")  //這裡使用"contex"標識
    @Provides
    public StudentForNameAt provideStudentForNameAtContext() {
        return new StudentForNameAt(context);
    }

    @Named("name")   //這裡使用"name"標識
    @Provides
    public StudentForNameAt provideStudentForNameAtName(){
        return new StudentForNameAt(name);
    }

}

最後,在我們的NameAtActivity中,同時也需要@Name("name")@Name("context")標識物件的注入:

public class NameAtActivity extends AppCompatActivity {

    @Named("name")   //這裡標識
    @Inject
    StudentForNameAt at1 ;

    @Named("context")  //這裡標識
    @Inject
    StudentForNameAt at2 ;

    TextView tv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        tv = findViewById(R.id.id_tv_content);

        DaggerNameAtComponent.builder().nameAtModule(new NameAtModule(this,"hello world")).build().inject(this);
    }

    public void getStudentInfo(View v) {
        tv.setText(at1.toString() + "\n" + at2.toString());

    }
}

獲取結果為:
這裡寫圖片描述
可以看到,我們的兩個StudentForNameAt物件分別被注入了NameContext

當然這只是其中一種方法,還有一種方法就是自定義的Qualifier識別符號。

@Qualifier 自定義識別符號

@Qualifier識別符號也是為了解決上面的問題,我們來同時定義一下兩個註解識別符號@StudentForNameStudentForScore


@Qualifier       //dagger Qualifier識別符號
@Retention(RetentionPolicy.RUNTIME)  //標識為執行時狀態
public @interface StudentForName {}
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface StudentForScore {
}

定義一個StudentQualifier物件,如下:

public class StudentQualifier {

    private String name ;
    private int score ;

    public StudentQualifier(){  //空的構造器 name是Tom score是77
        this.name = "Tom";
        this.score = 77 ;
    }

    public StudentQualifier(String name , int score) {
        this.name = name;
        this.score = score;
    }

    @Override
    public String toString() {
        return "StudentQualifier{" +
                "name='" + name + '\'' +
                ", score=" + score +
                '}';
    }
}

資料提供者Module為:

@Module
public class StudentQualifierModule {

    @Provides
    public String provideName(){
        return "zhangsan" ;
    }

    @Provides
    public int provideScore(){
        return 22 ;
    }

    @StudentForScore    //預設構造器被@StudentForScore修飾
    @Provides
    public StudentQualifier provideStudent(){
        return new StudentQualifier();
    }

    @StudentForName     //有參構造器被@StudentForName修飾
    @Provides
    public StudentQualifier provideStudentWithParams(String name , int score){
        return new StudentQualifier(name,score);
    }
}

然後在資料QualifierActivity接收器中:

public class QualifierActivity extends AppCompatActivity {

    TextView tv;

    @StudentForName     //被@StudentForName修飾,按道理應該是呼叫了有參構造器
    @Inject
    StudentQualifier qualifier1 ;

    @StudentForScore   //這個就是無引數的
    @Inject
    StudentQualifier qualifier2 ;


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

        tv = findViewById(R.id.id_tv_content);

        DaggerStudentQualifierComponent.builder().build().inject(this);
    }

    public void getStudentInfo(View v) {

        tv.setText(qualifier1.toString() + "\n" + qualifier2.toString());
    }

}

我們猜測一下結果,qualifier1應該為zhangsan22qualifier2應該為這Tom77,結果為:
這裡寫圖片描述
結果一致。

@PerApp and @PerActivity

我也不知道為什麼,上github上看dagger寫的程式碼,都少不了這個兩個註解。如果理解了@Singleton註解的意義,那麼@PerApp@PerActivity意義就差不多。上面已經說過,一個App中只有一個Applicataion,那麼我們可以定義一個註解@PerApp
生命週期可以與Application一致,那麼就可以理解為@PerApp修飾的方法,提供的物件在全域性範圍是單例的;如果定義一個@PerActivity,那麼@PerActivity修飾的方法返回的物件,生命週期是與Activity是一致的。舉個例子吧:
定義一個@AndroidPerApp

@Scope        //這裡是@Scope 注意不是@Qualifier
@Documented  //文件標記
@Retention(RetentionPolicy.RUNTIME)
public @interface AndroidPerApp {

}

再定義一個Module,此時有個方法被@AndroidPerApp修飾:

@Module
public class AndroidPerAppModule {

    private Context context;

    public AndroidPerAppModule(Context context) {
        this.context = context ;
    }

    @Provides
    @AndroidPerApp  //標記該方法只產生一個例項 該例項的生命週期與繫結的Context生命週期一致
    public Context provideContext(){
        return context;
    }

}

此時我們的AndroidPerAppComponent

@AndroidPerApp   //AndroidPerAppModule有方法被@AndroidPerApp修飾 那麼此時AndroidPerAppComponent也要被@AndroidPerApp修飾
@Component(modules = AndroidPerAppModule.class)
public interface AndroidPerAppComponent {

    /**
     * 向下提供Context
     * @return
     */
    Context getContext();
}

此時,既然是AppComponent,那麼需要與App綁定了:

public class MyApp extends Application{

   //這裡使用靜態的 因為MyApp全域性就一個   
    static AndroidPerAppComponent component ;

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

        component = DaggerAndroidPerAppComponent.builder().androidPerAppModule(new AndroidPerAppModule(this)).build();
    }

    public static AndroidPerAppComponent getAppComponent(){
       return component;
    }

}

那麼我們可以全域性呼叫MyApp.getAppComponent()了,這個等會再用,現在再來頂一個@PerActivity


@Scope
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface AndroidPerActivity {}

AndroidPerActivityModule中,Activity生命週期範圍內,返回的資料是單例的,其實是和@Singleton作用是一致的,它並沒有像市面上說的,具有和Activity相同生命週期的說法。


@Module
public class AndroidPerActivityModule {

    @AndroidPerActivity   
    @Provides
    public Student provideStudent(){
        return new Student();
    }

}

然後最重要的是PerActivityComponent了:

@AndroidPerActivity    
@Component(modules = AndroidPerActivityModule.class,
           dependencies = AndroidPerAppComponent.class)
public interface AndroidPerActivityComponent {

    void inject(AndroidAnnotationActivity activity);
}

AndroidAnnotationActivity中:

public class AndroidAnnotationActivity extends AppCompatActivity {

    @Inject
    Student st1 ;

    @Inject
    Student st2;

    TextView tv ;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        tv = findViewById(R.id.id_tv_content);
        //直接使用App.getAppComponent()獲取全域性的AppComponent物件
            DaggerAndroidPerActivityComponent.builder().androidPerAppComponent(MyApp.getAppComponent()).build().inject(this);
    }

    public void getStudentInfo(View v) {
        //Toast.makeText(this, "---->>" + MyApp.getAppComponent(), Toast.LENGTH_SHORT).show();

        tv.setText(st1.toString() + "--\n--" + st2.toString());
    }
}

我們此時開看一下st1st2的結果:
這裡寫圖片描述
可以看出,它們在AndroidAnnotationActivity範圍內是單例的,這個和@Singleton註解是一致的。工作中我們會用到@PerApp,它解決了我們全域性需要Context的問題。

Lazy 和 Provider

根據詞義,Lazy相當於我們懶載入機制,呼叫了本方法才會去載入,沒有呼叫就不載入;Provider很類似@Provides註解,但是它在的含義是每次呼叫都會去重複呼叫Module@Provides修飾的方法。舉個例子:
定義一個物件型別StudentOther

public class StudentOther {
    private String name ;
    private int score ;
    private String address ;

    //初始化過程中會使用random物件 以此來模擬是否為重複建立    
    public StudentOther(Random random) {
        this.name  = "zhansgan" + random.nextDouble()  ;
        this.score  = random.nextInt(200);
        this.address = "shanghai" + random.nextGaussian();
    }

    @Override
    public String toString() {
        return "StudentOther{" +
                "name='" + name + '\'' +
                ", score=" + score +
                ", address='" + address + '\'' +
                '}';
    }
}

定義一個OtherActivityModule物件,提供了Random方法,還有兩個@StudentForName@StudentForScore註解修飾的方法:

@Module
public class OtherActivityModule {

    public Random random ;

    public OtherActivityModule(){
        random  = new Random();
    }

    @Provides
    public Random provideRandom(){
        return random;
    }

    @StudentForName
    @Provides
    public StudentOther provideStudentOther(Random random){
        return new StudentOther(random);
    }

    @StudentForScore
    @Provides
    public StudentOther provideStudentOtherWidth(Random random){
        return new StudentOther(random);
    }
}

在資料接收器OtherActivity中,如下:

public class OtherActivity extends AppCompatActivity {

    @StudentForScore
    @Inject
    Lazy<StudentOther> other;

    @StudentForName
    @Inject
    Provider<StudentOther> other2 ;

    TextView tv;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        tv = findViewById(R.id.id_tv_content);

        DaggerOtherActivityComponent.builder().build().inject(this);
    }

    public void getStudentInfo(View v){
        StudentOther innerOther = other.get();       //呼叫該方法時才會去建立
        StudentOther innerOther2 = other2.get();     //呼叫該方法時才會去建立,但是每次都會重新載入Module中的方法 返回值有可能相同 也有可能不同

        tv.setText(innerOther.toString() + "---\n---" + innerOther2.toString());
    }
}

我們來看一下結果:
這裡寫圖片描述
我們可以看到innerOther始終是同一個值,而innerOther2卻一直是變化的,說明每次呼叫Provide.get()時都去重新整理了

@StudentForName
    @Provides
    public StudentOther provideStudentOther(Random random){
        return new StudentOther(random);
    }

方法。

好了,基本上總結的差不多了,文章寫了很久。稍後會把程式碼上傳一下,也算是給自己的一個交代了吧。
基本上樣式如下:
這裡寫圖片描述