iT邦幫忙

2023 iThome 鐵人賽

DAY 5
0
Mobile Development

Android Studio 30天進階學習系列 第 5

Android Studio 30天進階學習-DAY05_Dagger2_03(架構實作_中)

  • 分享至 

  • xImage
  •  

今天要來繼續Dagger第二階段撰寫,今天的進度會與@Singleton@Binds的更進階一點的註解標籤來新增並實作,在實作之前我要再來說明何謂Singleton單例化的功能與用途。

Singleton (單例化模式)

Singleton 是一種設計模式,其目的是確保某個類別只有一個實例,並提供一個全局訪問點來存取該實例。

它會在內存中建立該對象的唯一實例。每次注入該對象時,Dagger 2 都會返回這個唯一的實例。

於同一個class只會做一次New的動作,後續若再次呼叫class時會判定class是否已經被實例化了,若有被實例化過就不會再次進行New的動作,而是直接使用原本已經實例化的物件。

  • 此目的在於:希望該物件在整個程式裡保持唯一狀態。
    • 希望的效果:希望物件只有一個實體。
    • 問題:幾個不同客戶物件需要參照同一物件,而且希望確保這些類型的物件數目不超過一個。
    • 解決方案:保證只有一個實體。

Dagger標籤說明_二部曲

  • @Singleton
    單例化模式,在此功能中有使用一種名為 Double-checked locking(雙重檢查鎖定模式) 的方式來檢查是否有被初始化過。

創造一個靜態的特殊 method,呼叫這個方法時,先檢查物件是否已被實體化,若已被實體化,直接回傳該物件的一個參照;尚未被實體化的話,就回傳一個新實體的參照。

這邊先放上效果呈現圖,此圖是我在過去第一次實作時的結果圖,由此可見到在Worker添加了@Singleton之後的效果,圖例-壹可看到Worker有@59a2db@92da051兩個代號,而添加後的圖例-貳中只有@59a2db一個代號。

  1. 圖例-壹
    • 這邊創建一個工人,若沒有加上@Singleton註解標籤,log出來的結果會是兩個不同的人物。
  2. 圖例-貳
    • 加上@Singleton標籤註解之後,所顯示的Log結果是同一個人物。
  • @Binds
    中文直譯:綁定;綑綁
    • @Binds 注釋必須用於抽象方法上,並且方法必須返回指定的類型。此外,該方法必須位於Module的抽象類別中。
    • 在於綁定單個Class,無法同時輸出多個結果,要同時輸出多個結果必須使用@Provides註解。

開始修改實作

  • 首先先放上資料夾圖片對比

    • 今日的資料夾檔案
      https://ithelp.ithome.com.tw/upload/images/20230915/20150370iA3XiH4zo9.png
    • 昨天的資料夾檔案
      https://ithelp.ithome.com.tw/upload/images/20230915/20150370bwVyxD2FYl.png
  • 建立@Binds的修改步驟

    1. 新增EducationSystem.class在外部
    2. 新增Independent_SchoolState_School在school資料夾
    3. School類型改成Interface。
    4. 在module資料夾新增對應Independent_SchoolState_School的Module檔案。

@Binds的添加

School資料夾

  • School
public interface School {
    // 將School改成Interface介面。

    void buildSchoolType();

}

下面的各個學校類別的class都要implement interfaceSchool,並引入interface中的方法時做

  • Independent_School.class
public class Independent_School implements School {
    // 私立學校
    private static final String TAG = "Independent_School";

    @Inject
    public Independent_School() {
    }

    @Override
    public void buildSchoolType() {
        Log.e(TAG, "buildSchoolType: "+ TAG );
    }
}
  • State_School.class
public class State_School implements School{
    // 公立&國立學校

    private static final String TAG = "State_School";

    @Inject
    public State_School() {
        Log.e(TAG, "State_School OK!");
    }

    @Override
    public void buildSchoolType() {
        Log.e(TAG, "buildSchoolType: "+ TAG );
    }
}

建立School類別對應的Module

  • IndependentSchoolModule
@Module
public abstract class IndependentSchoolModule {

    @Binds
    abstract School bindIndependentSchool(Independent_School independent_school);

}
  • StateSchoolModule
@Module
public abstract class StateSchoolModule {

    @Binds
    abstract School bindStateSchool(State_School state_school);
}

Activity和Component修改

  • Activity
public class MainActivity extends AppCompatActivity {

    @Inject
    EducationSystem educationSystem;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // ... 建立時產生的程式碼省略

        SchoolComponent schoolComponent = DaggerSchoolComponent.create();

        schoolComponent.inject(this);

        educationSystem.start();
    }
}
  • SchoolComponent
@Component(modules = {StudentModule.class,
                    TeacherModule.class,
                    StateSchoolModule.class})
public interface SchoolComponent {
    School getSchool();
    void inject(MainActivity mainActivity);
}

以上是添加@Binds的結果,在這邊前面建立好的StudentTeacher不需要更改。

@Singleton的添加

Director(主任)

@Singleton
public class Director {

    @Inject
    public Director(){

    }
}

EducationSystem

public class EducationSystem {
    
    // ...前面已建立過的程式碼 省略
    
    private Director director;

    // 建構元中添加Director。
    @Inject
    public EducationSystem(School school, Student student, Teacher teacher, Director director) {
        this.school = school;
        this.student = student;
        this.teacher = teacher;
        this.director = director;
    }

    // 稍微修改一下此Function。
    public void start(){
        school.buildSchoolType();
        Log.e(TAG, "EducationSystem: \n" +
                "school: " + school + "\n" +
                "director: " + director + "\n" +
                "teacher: " + teacher + "\n" +
                "student: " + student + "\n" +
                "Prepare OK!");
    }
}

到這邊可能會覺得已經建立好了@Singleton,但會發現運行後還是會出錯,這時只要在回到Component的類別中,在最上方加入@Singleton的標籤即可。

Component

@Singleton
// 以下程式碼不須變動
@Component(modules = {StudentModule.class,
                    TeacherModule.class,
                    StateSchoolModule.class})
public interface SchoolComponent {
    School getSchool();
    void inject(MainActivity mainActivity);
}

以上這邊就完全建立好@Singleton以及@Binds的功能。
下面來看一下有@Singleton以及沒有@Singleton的結果:

  • @Singleton
    https://ithelp.ithome.com.tw/upload/images/20230915/20150370CtAHMdoV02.png

  • 沒有@Singleton
    https://ithelp.ithome.com.tw/upload/images/20230915/20150370gLqAlLX6IE.png

可以看到Director的結果產生了兩種不同的結果。

建立@Binds的注意事項

  • 注意事項:
    若同時添加公立/私立的Module時,在Build的時候就會出現問題,錯誤訊息截圖如下:
    https://ithelp.ithome.com.tw/upload/images/20230915/20150370HeFNpNTIx4.png
  1. 錯誤的訊息大致上是存在重複綁定(Duplicate Bindings)的問題。
  2. 因為我有兩個Module(IndependentSchoolModule 和 StateSchoolModule),它們都試圖綁定同一個類型的依賴 School。這是造成錯誤的原因之一。

建立@Singleton的注意事項

  • 注意事項
    若沒有在Component添加@Singleton標籤並Debug或Run則會出現問題,錯誤訊息截圖如下:
    https://ithelp.ithome.com.tw/upload/images/20230915/20150370SU7mqXHBu0.png
  1. 它指出了我的SchoolComponent(未設置**Scope**)試圖使用具有@Singleton ScopeDirector(主任類別),這是不允許的,因為Scope不相容。
  2. 解決這個錯誤問題的方法是確保所有相關的依賴項都使用相同的Scope
  3. 在要建立單例化的物件類別以及Component上方都需要加入@Singleton標籤註解。

以上是今天@Singleton@Binds的說明與添加實作,由於在添加了@Name以及制式化標籤註解後可能篇幅會偏多,這邊放到明天繼續實作,並在明天添加說明@Scope自製標籤以及其他功能標籤的說明。


上一篇
Android Studio 30天進階學習-DAY04_Dagger2_02(架構實作_上)
下一篇
Android Studio 30天進階學習-DAY06_Dagger2_04(架構實作_下)
系列文
Android Studio 30天進階學習30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言