關於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
物件,那麼問題來了,那name
和score
是哪裡來的呢?同樣我們看到了它提供另外兩個方法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的確在Activity
的Student2
物件中了。是不是很簡單呢?是啊,我們的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
物件也被傳入進去了,其實就是我們的StudentContextModule
被new
時傳入的this
寫入的。
依賴Module
這個意義,就相當我們每個應用中只有一個Application
,我們的Activity
可以依賴於這個Application
,然後可以Toast
,SharePreference
我們想要的東西。那麼就來一個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
將會依賴AppComponent
,AppComponent
將會提供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
物件,可以看到它有一個name
和context
屬性
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
物件分別被注入了Name
和Context
。
當然這只是其中一種方法,還有一種方法就是自定義的Qualifier識別符號。
@Qualifier 自定義識別符號
@Qualifier識別符號也是為了解決上面的問題,我們來同時定義一下兩個註解識別符號@StudentForName
和StudentForScore
:
@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
應該為zhangsan
和22
;qualifier2
應該為這Tom
和77
,結果為:
結果一致。
@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());
}
}
我們此時開看一下st1
和st2
的結果:
可以看出,它們在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);
}
方法。
好了,基本上總結的差不多了,文章寫了很久。稍後會把程式碼上傳一下,也算是給自己的一個交代了吧。
基本上樣式如下: