Điều gì quyết định vòng đời của một thành phần (đồ thị đối tượng) trong Dagger 2?


134

Tôi đang cố gắng quấn đầu quanh phạm vi trong Dagger 2, cụ thể là vòng đời của đồ thị có phạm vi. Làm thế nào để bạn tạo ra một thành phần sẽ được làm sạch khi bạn rời khỏi phạm vi.

Trong trường hợp ứng dụng Android, sử dụng Dagger 1.x, bạn thường có phạm vi gốc ở cấp ứng dụng mà bạn sẽ mở rộng để tạo phạm vi con ở cấp độ hoạt động.

public class MyActivity {

    private ObjectGraph mGraph;

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

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

Phạm vi con tồn tại miễn là bạn giữ một tham chiếu đến nó, trong trường hợp này là vòng đời của Hoạt động của bạn. Việc bỏ tham chiếu trong onDestroy đảm bảo đồ thị có phạm vi được tự do thu gom rác.

BIÊN TẬP

Jesse Wilson gần đây đã đăng một mea culpa

Dagger 1.0 đã làm hỏng tên phạm vi của nó ... Chú thích @Singleton được sử dụng cho cả biểu đồ gốc và biểu đồ tùy chỉnh, vì vậy thật khó để tìm ra phạm vi thực tế của một sự vật.

và mọi thứ khác tôi đã đọc / nghe điểm về Dagger 2 cải thiện cách thức hoạt động của phạm vi, nhưng tôi đang cố gắng để hiểu sự khác biệt. Theo nhận xét của @Kirill Boyarshinov bên dưới, vòng đời của một thành phần hoặc phụ thuộc vẫn được xác định, như thường lệ, bằng các tham chiếu cụ thể. Vì vậy, sự khác biệt giữa phạm vi Dagger 1.x và 2.0 hoàn toàn là vấn đề rõ ràng về ngữ nghĩa?

Sự hiểu biết của tôi

Dao găm 1.x

Phụ thuộc là một trong hai @Singletonhoặc không. Điều này cũng đúng với sự phụ thuộc trong biểu đồ gốc và biểu đồ con, dẫn đến sự mơ hồ về biểu đồ mà sự phụ thuộc bị ràng buộc (xem Trong Dagger là Singletons trong biểu đồ con được lưu trong bộ nhớ cache hoặc chúng sẽ luôn được tạo lại khi biểu đồ con hoạt động mới được xây dựng? )

Dao găm 2.0

Phạm vi tùy chỉnh cho phép bạn tạo phạm vi rõ ràng về ngữ nghĩa, nhưng tương đương về mặt chức năng với việc áp dụng @Singletontrong 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;
    }

}

Việc sử dụng @PerActivitytruyền đạt ý định của bạn về vòng đời của thành phần này, nhưng cuối cùng bạn có thể sử dụng thành phần này ở bất cứ đâu / bất cứ lúc nào. Lời hứa duy nhất của Dagger là, đối với một thành phần nhất định, các phương thức chú thích phạm vi sẽ trả về một thể hiện duy nhất. Tôi cũng giả sử Dagger 2 sử dụng chú thích phạm vi trên thành phần để xác minh rằng các mô-đun chỉ cung cấp các phụ thuộc có cùng phạm vi hoặc không nằm trong phạm vi.

Tóm tắt

Các phụ thuộc vẫn là singleton hoặc non-singleton, nhưng @Singletonhiện được dành cho các trường hợp singleton cấp ứng dụng và phạm vi tùy chỉnh là phương pháp ưa thích để chú thích các phụ thuộc singleton với vòng đời ngắn hơn.

Nhà phát triển chịu trách nhiệm quản lý vòng đời của các thành phần / phụ thuộc bằng cách bỏ các tham chiếu không còn cần thiết và chịu trách nhiệm đảm bảo rằng các thành phần chỉ được tạo một lần trong phạm vi mà chúng được dự định, nhưng chú thích phạm vi tùy chỉnh giúp dễ dàng xác định phạm vi đó .

Câu hỏi $ 64k *

Sự hiểu biết của tôi về phạm vi Dagger 2 và vòng đời có đúng không?

* Không thực sự là một câu hỏi $ 64'000.


5
Bạn không bỏ lỡ điều gì. Quản lý vòng đời của từng thành phần là thủ công. Theo kinh nghiệm của riêng tôi, điều tương tự cũng xảy ra với Dagger 1. Khi biểu đồ con đối tượng ObjectGraph cấp ứng dụng sử dụng plus()tham chiếu đến biểu đồ mới được lưu trữ trong Activity và bị ràng buộc với vòng đời của nó (được quy định trong onDestroy). Đối với phạm vi, chúng đảm bảo việc triển khai thành phần của bạn được tạo mà không có lỗi tại thời gian biên dịch, với mọi phụ thuộc được thỏa mãn. Vì vậy, nó không chỉ cho mục đích tài liệu. Kiểm tra một số ví dụ từ chủ đề này .
Kirill Boyarshinov 10/2/2015

1
Để rõ ràng về điều này, các phương thức của nhà cung cấp "không được kiểm soát" sẽ trả về các trường hợp mới trên mỗi lần tiêm?
dùng1923613

2
Tại sao bạn đặt thành phần = null; trong onDestroy ()?
Mary Paździoch

Câu trả lời:


70

Đối với câu hỏi của bạn

Điều gì quyết định vòng đời của một thành phần (đồ thị đối tượng) trong Dagger 2?

Câu trả lời ngắn gọn là bạn xác định nó . Các thành phần của bạn có thể được cung cấp một phạm vi, chẳng hạn như

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

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

Đây là hữu ích cho bạn cho hai điều:

  • Xác thực phạm vi: một thành phần chỉ có thể có các nhà cung cấp không có phạm vi hoặc các nhà cung cấp có phạm vi có cùng phạm vi với thành phần của bạn.

.

@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();
    }
}
  • Cho phép phân chia phạm vi phụ thuộc phạm vi của bạn, do đó cho phép bạn tạo một thành phần "phân cấp" sử dụng các phiên bản được cung cấp từ thành phần "siêu lớp".

Điều này có thể được thực hiện với @Subcomponentchú thích, hoặc phụ thuộc thành phần. Cá nhân tôi thích phụ thuộc.

@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));

Hoặc bạn có thể sử dụng các phụ thuộc thành phần như vậy

@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();

Những điều quan trọng cần biết:

  • Một nhà cung cấp phạm vi tạo một thể hiện cho phạm vi đã cho cho từng thành phần . Có nghĩa là một thành phần theo dõi các thể hiện của chính nó, nhưng các thành phần khác không có nhóm phạm vi dùng chung hoặc một số phép thuật. Để có một thể hiện trong một phạm vi nhất định, bạn cần một thể hiện của thành phần. Đây là lý do tại sao bạn phải cung cấp ApplicationComponentđể truy cập các phụ thuộc phạm vi của chính nó.

  • Một thành phần chỉ có thể subscope một thành phần phạm vi. Nhiều phụ thuộc thành phần phạm vi không được phép.


Một thành phần chỉ có thể subscope một thành phần phạm vi. Nhiều phụ thuộc thành phần phạm vi không được phép (ngay cả khi tất cả chúng có phạm vi khác nhau, mặc dù tôi nghĩ đó là một lỗi). không thực sự hiểu ý nghĩa của nó
Damon Yuan

Nhưng những gì về vòng đời. Ứng viên ActivityComponent cho người thu gom rác nếu hoạt động bị phá hủy?
Sever

Nếu bạn không lưu trữ nó ở nơi nào khác thì có
EpicPandaForce

1
Vì vậy, nếu chúng ta cần thành phần và đối tượng được tiêm trực tiếp thông qua Activity, chúng ta sẽ xây dựng thành phần bên trong Activity. Nếu chúng ta chỉ muốn tồn tại thông qua một mảnh vỡ, tôi nên xây dựng thành phần bên trong mảnh vỡ, phải không? Trường hợp bạn giữ cá thể thành phần làm cho phạm vi?
Thracian

Tôi nên làm gì nếu tôi muốn nó tồn tại thông qua một Hoạt động cụ thể?
Thracian
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.