2015-03-28 17 views
27

Tôi sử dụng Dagger2 cho DI trong ứng dụng Android của mình. Tôi thấy rằng tôi phải viết phương thức tiêm cho mỗi lớp sử dụng trường @Inject. Có cách nào mà tôi có thể chỉ cần tiêm lớp cha mẹ để tôi không phải gọi tiêm trên mỗi phân lớp? Lấy hoạt động chẳng hạn. Tôi có một số BaseActivity mà mọi Hoạt động mở rộng. Có cách nào mà tôi chỉ có thể tạo ra một phương pháp tiêm trong thành phần cho BaseActivity và chỉ cần gọi tiêm trong onCreate BaseActivity, và @inject lĩnh vực trong các hoạt động phụ được tiêm tự động?Tôi có thể tiêm siêu lớp khi sử dụng dagger2 để tiêm phụ thuộc không?

+0

Bạn có thể thêm một số mã mẫu để hiển thị ý của bạn không? – nhaarman

Trả lời

21

Hiện không thể thực hiện được. Giải thích bởi Gregory Kick:

Đây là cách các phương pháp thành viên tiêm làm việc:

  1. Bạn có thể tạo ra một phương pháp tiêm các thành viên cho bất kỳ loại có @Inject bất cứ nơi nào trong hệ thống phân cấp các lớp học. Nếu không, bạn sẽ gặp lỗi .
  2. Tất cả @Inject thành viên được chỉnh sửa trong toàn bộ phân cấp loại sẽ được tiêm: loại đối số và tất cả siêu kiểu.
  3. Không có thành viên nào được @Inject chỉnh sửa cho loại phụ của loại đối số.

Vấn đề này đã được thảo luận herehere, theo dõi những thông tin cập nhật. Nhưng nó không có khả năng thay đổi sớm, gây ra Dagger 2 là close to release.

+0

Dường như họ đã đưa ra quyết định này vì một lý do. Nhưng nó vẫn là một điều đáng tiếc là họ không hỗ trợ điều này vì nó là một IMHO phản trực giác nhỏ. Tuy nhiên, cảm ơn bạn đã trả lời! –

+0

@ Chris.Zou Để hỗ trợ tiêm lớp con, bạn sẽ cần phải thực hiện phản chiếu khi chạy. Nhóm Dagger 2 đã quyết định sớm rằng họ muốn tránh làm công cụ trong thời gian chạy, vì nó chậm hơn và bạn không tìm hiểu về lỗi cho đến khi bạn chạy ứng dụng. – vaughandroid

35

Tôi gặp phải tình huống tương tự. Một cách để giảm bớt một chút tiêm từ một thành phần chung trong tất cả các Hoạt động như sau:

1) Mở rộng lớp Ứng dụng để có thể tạo thành phần chung và giữ tham chiếu đến nó.

public class ApplicationDagger extends Application { 

    private ApplicationComponent component; 

    @Override 
    public void onCreate(){ 
     super.onCreate(); 
     component = DaggerApplicationComponent.builder().applicationModule(new ApplicationModule(this)).build(); 
    } 

    public ApplicationComponent getComponent(){ 
      return component; 
    } 
} 

2) Tạo một DaggerActivity trừu tượng mà được các thành phần phổ biến từ ứng dụng và gọi một phương pháp trừu tượng injectActivity, đem lại cho các thành phần như một cuộc tranh cãi. Như thế này:

public abstract class DaggerActivity extends Activity { 

    @Override 
    public void onCreate(Bundle saved){ 
     super.onCreate(saved); 
     ApplicationComponent component = ((ApplicationDagger) getApplication()).getComponent(); 
     injectActivity(component); 
    } 

    public abstract void injectActivity(ApplicationComponent component); 
} 

3) Cuối cùng, bạn phải thực sự tiêm mỗi Activity kéo dài DaggerActivity. Nhưng điều này có thể được thực hiện với ít nỗ lực hơn, vì bạn phải thực hiện phương thức abstract nếu không bạn sẽ nhận được các lỗi biên dịch. Ở đây chúng tôi đi:

public class FirstActivity extends DaggerActivity { 

    @Inject 
    ClassToInject object; 

    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     //initialize your Activity 
    } 

    @Override 
    public void injectActivity(ApplicationComponent component) { 
     component.inject(this); 
    } 
} 

Tất nhiên, bạn vẫn phải khai báo từng Hoạt động một cách rõ ràng trong Hợp phần của bạn.

UPDATE: tiêm chích @ActivityScope đối tượng vào mảnh vỡ

Tại một số điểm, tôi cần phải sử dụng custom scopes để ràng buộc đối tượng đến một chu kỳ sống Activity. Tôi quyết định mở rộng bài đăng này vì nó có thể giúp một số người.

Hãy nói rằng bạn có một lớp @ModuleActivityModule@Subcomponent giao diện ActivityComponent.

Bạn cần sửa đổi DaggerActivity. Việc mở rộng Activities mở rộng DaggerActivity sẽ cần phải triển khai phương thức mới (thay đổi chữ ký).

public abstract class ActivityDagger extends AppCompatActivity { 

    ComponentActivity component; 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     component = ((ApplicationDagger) getApplication()).getComponent().plus(new ActivityModule(this)); 
     injectActivity(component); 
     super.onCreate(savedInstanceState); 
    } 

    ActivityComponent getComponent() { 
     return component; 
    } 

    public abstract void injectActivity(ActivityComponent component); 
} 

Sau đó, một lớp FragmentDagger mở rộng Fragment thể được tạo ra như thế này:

public abstract class FragmentDagger extends Fragment { 

    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     ActivityDagger activityDagger = (ActivityDagger) getActivity(); 
     ActivityComponent component = activityDagger.getComponent(); 
     injectFragment(component); 
    } 

    public abstract void injectFragment(ActivityComponent component); 

} 

Đối với Activities, các Fragments mở rộng FragmentDagger chỉ có một phương pháp để thực hiện:

public abstract void injectFragment(ActivityComponent component); 

Bạn sẽ có thể sử dụng lại Fragments bất cứ nơi nào bạn muốn. Lưu ý rằng phương pháp super.onCreated() trong ActivityDagger nên được gọi sau khi khởi tạo thành phần. Nếu không, bạn sẽ nhận được NullPointerException khi trạng thái Activity được tạo lại, bởi vì phương pháp super.onCreate() của số Fragment sẽ được gọi.

+0

Cảm ơn lời khuyên! –

2

Bạn có thể làm một ít hack sử dụng phản ánh:

public class UiInjector { 
 

 
    private static final String METHOD_NAME = "inject"; 
 

 
    private final UIComponent component; 
 

 
    public UiInjector(final UIComponent component) { 
 
     this.component = component; 
 
    } 
 

 
    public void inject(final Object subject) { 
 
     try { 
 
      component.getClass() 
 
        .getMethod(METHOD_NAME, subject.getClass()) 
 
        .invoke(component, subject); 
 
     } catch (final NoSuchMethodException exception) { 
 
      throwNoInjectMethodForType(component, subject.getClass()); 
 
     } catch (final Exception exception) { 
 
      throwUnknownInjectionError(exception); 
 
     } 
 
    } 
 

 
    private void throwNoInjectMethodForType(final Object component, final Class subjectType) { 
 
     throw new RuntimeException(component.getClass().getSimpleName() + 
 
       " doesn't have inject method with parameter type : " + subjectType); 
 
    } 
 

 
    private void throwUnknownInjectionError(final Exception cause) { 
 
     throw new RuntimeException("Unknown injection error", cause); 
 
    } 
 
}

Trong trường hợp này, bạn vẫn cần phải viết tiêm phương pháp trong một thành phần, nhưng bạn không cần phải 'bơm' phương pháp trong mỗi hoạt động, phân đoạn, xem, bất cứ điều gì.

Tại sao nó hoạt động? khi chúng tôi sử dụng getClass() trên chủ đề tiêm sẽ có được một lớp hậu duệ, không phải cơ sở.

Thận trọng! Trong trường hợp bạn sử dụng Proguard, bạn cần thêm -keep class <ComponentClass> { *; } vào quy tắc của mình để giữ các phương pháp tiêm như trong thành phần

Các vấn đề liên quan