อะไรเป็นตัวกำหนดวงจรชีวิตของส่วนประกอบ (กราฟวัตถุ) ใน Dagger 2


134

ฉันพยายามพันหัวของฉันรอบขอบเขตใน Dagger 2 โดยเฉพาะวงจรชีวิตของกราฟที่กำหนดขอบเขต คุณจะสร้างส่วนประกอบที่จะล้างเมื่อคุณออกจากขอบเขตได้อย่างไร

ในกรณีของแอปพลิเคชัน Android โดยใช้ Dagger 1.x โดยทั่วไปคุณจะมีขอบเขตรูทที่ระดับแอปพลิเคชันซึ่งคุณจะขยายเพื่อสร้างขอบเขตย่อยในระดับกิจกรรม

public class MyActivity {

    private ObjectGraph mGraph;

    public void onCreate() {
        mGraph = ((MyApp) getApplicationContext())
            .getObjectGraph()
            .plus(new ActivityModule())
            .inject(this);
    }

    public void onDestroy() {
        mGraph = null;
    }
}

ขอบเขตย่อยมีอยู่ตราบเท่าที่คุณยังคงอ้างอิงอยู่ซึ่งในกรณีนี้คือวงจรชีวิตของกิจกรรมของคุณ การทิ้งข้อมูลอ้างอิงใน onDestroy ทำให้มั่นใจได้ว่ากราฟที่กำหนดขอบเขตนั้นไม่มีค่าใช้จ่ายในการเก็บขยะ

แก้ไข

Jesse Wilson เพิ่งโพสต์mea culpa

Dagger 1.0 ทำให้ชื่อขอบเขตของมันแย่ลง ... คำอธิบายประกอบ @Singleton ใช้สำหรับทั้งกราฟรูทและกราฟที่กำหนดเองดังนั้นจึงเป็นเรื่องยากที่จะเข้าใจว่าขอบเขตที่แท้จริงของสิ่งนั้นคืออะไร

และสิ่งอื่น ๆ ที่ฉันได้อ่าน / ได้ยินประเด็นเกี่ยวกับ Dagger 2 ในการปรับปรุงวิธีการทำงานของขอบเขต แต่ฉันกำลังพยายามที่จะเข้าใจความแตกต่าง ตามความคิดเห็นของ @Kirill Boyarshinov ด้านล่างวงจรชีวิตของส่วนประกอบหรือการพึ่งพายังคงถูกกำหนดตามปกติโดยการอ้างอิงที่เป็นรูปธรรม ดังนั้นความแตกต่างระหว่างขอบเขต Dagger 1.x และ 2.0 เป็นเรื่องของความชัดเจนทางความหมายหรือไม่?

ความเข้าใจของฉัน

กริช 1.x

การพึ่งพามี@Singletonหรือไม่ นี่เป็นความจริงอย่างเท่าเทียมกันของการอ้างอิงในกราฟรูทและกราฟย่อยซึ่งนำไปสู่ความคลุมเครือว่ากราฟใดที่การอ้างอิงถูกผูกไว้ (ดูใน Dagger คือ Singletons ภายในกราฟย่อยที่แคชไว้หรือจะถูกสร้างขึ้นใหม่เสมอเมื่อมีกราฟย่อยกิจกรรมใหม่ ถูกสร้างขึ้น? )

กริช 2.0

ขอบเขตที่กำหนดเองช่วยให้คุณสามารถสร้างขอบเขตที่ชัดเจนในเชิงความหมาย แต่จะมีฟังก์ชันเทียบเท่ากับการใช้@Singletonใน Dagger 1.x

// Application level
@Singleton
@Component( modules = MyAppModule.class )
public interface MyAppComponent {
    void inject(Application app);
}

@Module
public class MyAppModule {

    @Singleton @Named("SingletonScope") @Provides
    StringBuilder provideStringBuilderSingletonScope() {
        return new StringBuilder("App");
    }
}

// Our custom scope
@Scope public @interface PerActivity {}

// Activity level
@PerActivty
@Component(
    dependencies = MyAppComponent.class,
    modules = MyActivityModule.class
)
public interface MyActivityComponent {
    void inject(Activity activity);
}

@Module
public class MyActivityModule {

    @PerActivity @Named("ActivityScope") @Provides
    StringBuilder provideStringBuilderActivityScope() {
        return new StringBuilder("Activity");
    }

    @Name("Unscoped") @Provides
    StringBuilder provideStringBuilderUnscoped() {
        return new StringBuilder("Unscoped");
    }
}

// Finally, a sample Activity which gets injected
public class MyActivity {

    private MyActivityComponent component;

    @Inject @Named("AppScope")
    StringBuilder appScope

    @Inject @Named("ActivityScope")
    StringBuilder activityScope1

    @Inject @Named("ActivityScope")
    StringBuilder activityScope2

    @Inject @Named("Unscoped")
    StringBuilder unscoped1

    @Inject @Named("Unscoped")
    StringBuilder unscoped2

    public void onCreate() {
        component = Dagger_MyActivityComponent.builder()
            .myApplicationComponent(App.getComponent())
            .build()
            .inject(this);

        appScope.append(" > Activity")
        appScope.build() // output matches "App (> Activity)+" 

        activityScope1.append("123")
        activityScope1.build() // output: "Activity123"

        activityScope2.append("456")
        activityScope1.build() // output: "Activity123456"

        unscoped1.append("123")
        unscoped1.build() // output: "Unscoped123"

        unscoped2.append("456")
        unscoped2.build() // output: "Unscoped456"

    }

    public void onDestroy() {
        component = null;
    }

}

การนำกลับไปใช้เป็นการ@PerActivityสื่อสารถึงความตั้งใจของคุณเกี่ยวกับวงจรชีวิตของส่วนประกอบนี้ แต่ท้ายที่สุดคุณสามารถใช้ส่วนประกอบได้ทุกที่ทุกเวลา คำสัญญาเดียวของ Dagger คือสำหรับส่วนประกอบที่กำหนดวิธีการที่มีคำอธิบายประกอบขอบเขตจะส่งคืนอินสแตนซ์เดียว ฉันยังถือว่า Dagger 2 ใช้คำอธิบายประกอบขอบเขตบนส่วนประกอบเพื่อตรวจสอบว่าโมดูลให้การอ้างอิงที่อยู่ในขอบเขตเดียวกันหรือไม่ใช่ขอบเขตเท่านั้น

สรุป

การพึ่งพายังคงเป็นแบบซิงเกิลตันหรือไม่ใช่ซิงเกิลตัน แต่@Singletonตอนนี้มีไว้สำหรับอินสแตนซ์ซิงเกิลระดับแอปพลิเคชันและขอบเขตที่กำหนดเองเป็นวิธีที่ต้องการสำหรับการใส่คำอธิบายประกอบการอ้างอิงซิงเกิลตันที่มีวงจรชีวิตสั้นลง

นักพัฒนามีหน้าที่จัดการวงจรชีวิตของส่วนประกอบ / การอ้างอิงโดยทิ้งการอ้างอิงที่ไม่จำเป็นอีกต่อไปและรับผิดชอบในการตรวจสอบให้แน่ใจว่าส่วนประกอบถูกสร้างขึ้นเพียงครั้งเดียวในขอบเขตที่ต้องการ แต่คำอธิบายประกอบขอบเขตที่กำหนดเองทำให้ง่ายต่อการระบุขอบเขตนั้น .

คำถาม $ 64,000 *

ความเข้าใจของฉันเกี่ยวกับขอบเขตและวงจรชีวิตของ Dagger 2 ถูกต้องหรือไม่?

* ไม่ใช่คำถาม 64,000 เหรียญ


5
คุณพลาดอะไรไป การจัดการ livecycle ของแต่ละองค์ประกอบเป็นแบบแมนนวล จากประสบการณ์ของฉันเองใน Dagger 1 ก็เช่นกัน เมื่อซับกราฟอ็อบเจ็กต์ ObjectGraph ระดับแอ็พพลิเคชันโดยใช้plus()การอ้างอิงไปยังกราฟใหม่ถูกเก็บไว้ใน Activity และถูกผูกไว้กับ livecycle (dereferenced in onDestroy) สำหรับขอบเขตจะช่วยให้มั่นใจได้ว่าการติดตั้งส่วนประกอบของคุณจะถูกสร้างขึ้นโดยไม่มีข้อผิดพลาดในเวลาคอมไพล์โดยมีการพึ่งพาทุกครั้ง ดังนั้นจึงไม่ใช่แค่เพื่อวัตถุประสงค์ในการจัดทำเอกสารเท่านั้น ดูตัวอย่างจากหัวข้อนี้
Kirill Boyarshinov

1
เพื่อความชัดเจนในเรื่องนี้วิธีการของผู้ให้บริการ "ที่ไม่ได้กำหนดขอบเขต" จะส่งคืนอินสแตนซ์ใหม่ในการฉีดทุกครั้งหรือไม่
user1923613

2
ทำไมคุณถึงตั้งค่า component = null; ใน onDestroy ()?
Marian Paździoch

คำตอบ:


70

สำหรับคำถามของคุณ

อะไรเป็นตัวกำหนดวงจรชีวิตของส่วนประกอบ (กราฟวัตถุ) ใน Dagger 2

คำตอบสั้น ๆคุณตรวจสอบมัน ส่วนประกอบของคุณสามารถกำหนดขอบเขตได้เช่น

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

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

สิ่งเหล่านี้มีประโยชน์สำหรับคุณสองประการ:

  • การตรวจสอบขอบเขต: คอมโพเนนต์สามารถมีได้เฉพาะผู้ให้บริการที่ไม่ได้กำหนดขอบเขตหรือผู้ให้บริการขอบเขตที่มีขอบเขตเดียวกันกับส่วนประกอบของคุณ

.

@Component(modules={ApplicationModule.class})
@ApplicationScope
public interface ApplicationComponent {
    Something something();
    AnotherThing anotherThing();

    void inject(Whatever whatever);
}

@Module
public class ApplicationModule {
    @ApplicationScope //application-scoped provider, only one can exist per component
    @Provides
    public Something something() {
         return new Something();
    }

    @Provides //unscoped, each INJECT call creates a new instance
    public AnotherThing anotherThing() {
        return new AnotherThing();
    }
}
  • อนุญาตให้มีการกำหนดขอบเขตการอ้างอิงตามขอบเขตของคุณซึ่งจะช่วยให้คุณสร้างคอมโพเนนต์ "ซับสโคป" ที่ใช้อินสแตนซ์ที่มีให้จากคอมโพเนนต์ "superscoped"

ซึ่งสามารถทำได้โดยใช้@Subcomponentคำอธิบายประกอบหรือการอ้างอิงส่วนประกอบ ฉันชอบการอ้างอิงเป็นการส่วนตัว

@Component(modules={ApplicationModule.class})
@ApplicationScope
public interface ApplicationComponent {
    Something something();
    AnotherThing anotherThing();

    void inject(Whatever whatever);

    ActivityComponent newActivityComponent(ActivityModule activityModule); //subcomponent factory method
}

@Subcomponent(modules={ActivityModule.class})
@ActivityScope
public interface ActivityComponent {
    ThirdThingy thirdThingy();

    void inject(SomeActivity someActivity);
}

@Module
public class ActivityModule {
    private Activity activity;

    public ActivityModule(Activity activity) {
        this.activity = activity;
    }

    //...
}

ApplicationComponent applicationComponent = DaggerApplicationComponent.create();
ActivityComponent activityComponent = applicationComponent.newActivityComponent(new ActivityModule(SomeActivity.this));

หรือคุณสามารถใช้การอ้างอิงองค์ประกอบเช่นนั้น

@Component(modules={ApplicationModule.class})
@ApplicationScope
public class ApplicationComponent {
    Something something(); 
    AnotherThing anotherThing();

    void inject(Whatever whatever);
}

@Component(dependencies={ApplicationComponent.class}, modules={ActivityModule.class})
@ActivityScope
public interface ActivityComponent extends ApplicationComponent {
    ThirdThingy thirdThingy();

    void inject(SomeActivity someActivity);
}

@Module
public class ActivityModule {
    private Activity activity;

    public ActivityModule(Activity activity) {
        this.activity = activity;
    }

    //...
}

ApplicationComponent applicationComponent = DaggerApplicationComponent.create();
ActivityComponent activityComponent = DaggerActivityComponent.builder().activityModule(new ActivityModule(SomeActivity.this)).build();

สิ่งสำคัญที่ควรทราบ:

  • ผู้ให้บริการกำหนดขอบเขตสร้างหนึ่งตัวอย่างสำหรับขอบเขตที่กำหนดว่าในแต่ละองค์ประกอบ ความหมายคอมโพเนนต์ติดตามอินสแตนซ์ของตัวเอง แต่ส่วนประกอบอื่น ๆ ไม่มีพูลขอบเขตที่ใช้ร่วมกันหรือเวทมนตร์บางอย่าง หากต้องการมีหนึ่งอินสแตนซ์ในขอบเขตที่กำหนดคุณต้องมีส่วนประกอบหนึ่งอินสแตนซ์ นี่คือเหตุผลที่คุณต้องระบุApplicationComponentเพื่อเข้าถึงการอ้างอิงตามขอบเขตของมันเอง

  • คอมโพเนนต์สามารถย่อขอบเขตได้เพียงคอมโพเนนต์ที่กำหนดขอบเขตเดียว ไม่อนุญาตให้มีการอ้างอิงองค์ประกอบที่กำหนดขอบเขตหลายรายการ


คอมโพเนนต์สามารถย่อขอบเขตได้เพียงคอมโพเนนต์ที่กำหนดขอบเขตเดียว ไม่อนุญาตให้มีการอ้างอิงองค์ประกอบที่กำหนดขอบเขตหลายรายการ (แม้ว่าทั้งหมดจะมีขอบเขตที่แตกต่างกันแม้ว่าฉันจะคิดว่าเป็นข้อบกพร่องก็ตาม) ไม่เข้าใจความหมายจริงๆ
Damon Yuan

แต่สิ่งที่เกี่ยวกับ livecycle ผู้สมัคร ActivityComponent สำหรับคนเก็บขยะจะถูกทำลายหรือไม่
ตัด

ถ้าคุณไม่ได้เก็บไว้ที่อื่นใช่
EpicPandaForce

1
ดังนั้นหากเราต้องการองค์ประกอบและวัตถุที่ฉีดผ่านกิจกรรมเราจะสร้างส่วนประกอบภายในกิจกรรม หากเราเพียงต้องการที่จะอยู่รอดผ่าน Fragment ฉันควรสร้างส่วนประกอบภายในส่วนย่อยใช่ไหม? คุณเก็บอินสแตนซ์คอมโพเนนต์ไว้ที่ไหนทำให้ขอบเขต?
Thracian

ฉันควรทำอย่างไรหากต้องการให้อยู่รอดผ่านกิจกรรมใดกิจกรรมหนึ่ง
Thracian
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.