2017-07-13 27 views
18

Vấn đề của tôi với mẫu dành riêng cho Android là nếu bạn sử dụng lớp AndroidInjection của mình, không có cách nào để các thành viên tiêm các đối tượng khác ngoài Activities/Fragments/chế độ xem tùy chỉnh/bộ điều hợp, ngoại trừ Thành phần ứng dụng. Điều này là do bạn không thể có được tham chiếu đến số Subcomponent (AndroidInjector) được sử dụng để tiêm Activities/Fragments. Điều này làm cho việc tiêm các hộp thoại (nếu bạn sử dụng DialogFragments).Tôi làm cách nào để sử dụng lớp AndroidInjection trong chế độ xem tùy chỉnh hoặc các lớp học Android khác?

Lớp AndroidInjection dường như chỉ hỗ trợ các loại Android chính.

+0

Bạn rất có thể cần phải sao chép cơ chế dựa trên nguồn AndroidInjector cho cả hộp thoại và chế độ xem. – EpicPandaForce

+0

Không phải nếu đánh bại toàn bộ mục đích nếu tôi phải viết một 'AndroidInjector' tùy chỉnh cho những loại này. Tôi đã hy vọng cập nhật ứng dụng của mình để đơn giản hóa cách mọi thứ được tiêm. –

+0

Tôi không sử dụng nó: D – EpicPandaForce

Trả lời

2

Điều này là do bạn không thể có được một tài liệu tham khảo các các Subcomponent (AndroidInjector) được sử dụng để tiêm Activities/Fragments.

Bạn luôn có thể tự tiêm chính bộ phận đó. Chỉ cần thêm một trường cho thành phần vào Activity/Fragment của bạn và để Dagger tiêm nó cùng với phần còn lại.

// will be injected 
@Inject MainActivitySubcomponent component; 
+0

Tôi đã quan tâm về việc tiêm trong chế độ xem tùy chỉnh và bộ điều hợp về cơ bản những thứ khác với các loại Android lõi. –

+0

@VivekMaskara Bạn nói rằng điều đó là không thể, bởi vì bạn không thể có được thành phần con. Nhưng bạn có thể, và với nó bạn chỉ có thể tiêm các khung nhìn tùy chỉnh của bạn như bạn thường sẽ –

+1

Tôi không thể làm theo đầy đủ cách thức này có thể được sử dụng để tiêm các khung nhìn/bộ điều hợp tùy chỉnh. Bạn có thể giải thích nó một cách chi tiết hơn không. :) –

9

Trước tiên, bạn nên suy nghĩ qua Vasily's answer.

Nhưng hãy suy nghĩ một chút về cách chúng tôi đã thực hiện điều này trước Dagger Android? Chúng tôi đã xây dựng một thành phần con từ thành phần được lấy từ lớp Application. Sau đó, chúng tôi có thể sử dụng tiểu hợp phần này để chèn các trường, ví dụ như chế độ xem tùy chỉnh.

Vì vậy, chúng tôi sẽ cố gắng thực hiện chính xác điều tương tự ngay bây giờ.

Giả sử, mục tiêu của chúng tôi là để tiêm MyAdapter lớp thành một MyButton:

public class MyButton extends AppCompatButton { 

    @Inject MyAdapter adapter; 

    public MyButton(Context context) { 
     super(context); 

     ... 
    } 

} 

Và chúng ta hãy làm adapter có một sự phụ thuộc vào các hoạt động Context, không ứng dụng Context:

public class MyAdapter { 

    @Inject 
    public MyAdapter(@Named("activity") Context context) { 
    } 

} 

Hãy bắt đầu từ lớp tùy chỉnh Application.

MyApplication.java

public class MyApplication extends DaggerApplication { 

    @Inject 
    DispatchingAndroidInjector<Activity> dispatchingActivityInjector; 

    public static MySubcomponent mySubcomponent; 

    @Override 
    protected AndroidInjector<? extends DaggerApplication> applicationInjector() { 

     return DaggerAppComponent.builder() 
       .create(this); 
    } 

} 

AppComponent.java:

@Component(modules = {AndroidSupportInjectionModule.class, ActivityBindingModule.class, AppModule.class}) 
@Singleton 
public interface AppComponent extends AndroidInjector<MyApplication> { 

    @Component.Builder 
    abstract class Builder extends AndroidInjector.Builder<MyApplication> { 
    } 
} 

AppModule.java

@Module 
abstract class AppModule { 

    @Binds 
    @Singleton 
    @Named("app") 
    abstract Context providesContext(Application application); 
} 

ActivityBindingModule.java

@Module(subcomponents = MySubcomponent.class) 
public abstract class ActivityBindingModule { 

    @Binds 
    @IntoMap 
    @ActivityKey(MainActivity.class) 
    abstract AndroidInjector.Factory<? extends Activity> 
    bindMainActivityInjectorFactory(MySubcomponent.Builder builder); 
} 

AndroidSupportInjectionModule.java được vận chuyển với chính nó dao găm. Nếu bạn không sử dụng các lớp học từ gói hỗ trợ (ví dụ: android.app.Fragment thay vì android.support.v4.app.Fragment), thì hãy sử dụng AndroidInjectionModule.java.

MySubcomponent.java

@ActivityScope 
@Subcomponent(modules = {SubcomponentModule.class/*, other modules here, if needed */}) 
public interface MySubcomponent extends AndroidInjector<MainActivity> { 

    void inject(MyButton button); 

    @Subcomponent.Builder 
    abstract class Builder extends AndroidInjector.Builder<MainActivity> { 
     public abstract MySubcomponent build(); 
    } 
} 

SubcomponentModule.java

@Module 
abstract class SubcomponentModule { 

    @Binds 
    @ActivityScope 
    @Named("activity") 
    abstract Context toContext(MainActivity activity); 

} 

MainActivity.java

public class MainActivity extends AppCompatActivity { 

    @Inject 
    MySubcomponent subcomponent; 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     // Will inject `subcomponent` field 
     AndroidInjection.inject(this); 

     // Saving this component in a static field 
     // Hereafter you are taking responsibility of mySubcomponent lifetime 
     MyApplication.mySubcomponent = subcomponent; 

     super.onCreate(savedInstanceState); 
     setContentView(new MyButton(this)); 
    } 
} 

Có tất cả trong số này, hiện nay đây là cách MyButton sẽ trông giống như:

public class MyButton extends AppCompatButton { 

    @Inject MyAdapter adapter; 

    public MyButton(Context context) { 
     super(context); 

     MyApplication.mySubcomponent.inject(this); 
    } 

} 

Tôi thừa nhận rằng điều này có vẻ hacky và chắc chắn không phải là một cách tiếp cận để dính vào. Tôi rất vui khi thấy một cách tiếp cận tốt hơn.

10

Điều gì sau đây không phải là câu trả lời cho câu hỏi của bạn, nhưng giải thích tại sao bạn không nên đặt câu hỏi này chút nào.

Bạn nên tránh tiêm vào tùy chỉnh Views nói chung. Lý do cho điều này được liệt kê trong this article.

Ưu điểm của việc sử dụng phương pháp tiêm trong trường hợp này [tiêm vào tùy chỉnh xem] là:

  • Dependencies sẽ cần phải được truyền từ phần đầu cấp (Hoạt động hoặc Fragment)
  • Phương pháp tiêm không mở cửa cho một nguyên tắc chịu trách nhiệm một lần
  • Không phụ thuộc vào khuôn khổ
  • Hiệu suất tốt hơn

Ưu điểm đầu tiên có thể là một điều bất ngờ vì tuyên truyền từ thành phần cấp cao nhất khó hơn việc thêm chú thích vào các trường và liên quan đến mã bản mẫu nhiều hơn. Đây chắc chắn là một điều xấu, phải không ?. Không phải trong trường hợp này. Trên thực tế, có hai khía cạnh tốt liên quan đến sự truyền bá phụ thuộc như vậy. Trước hết, phụ thuộc sẽ hiển thị ở thành phần cấp cao nhất. Do đó, chỉ cần tìm kiếm , ví dụ: Các trường của Fragment, người đọc mã sẽ ngay lập tức hiểu rằng Fragment này hiển thị hình ảnh. Các tối ưu hóa này cho khả năng đọc giúp hệ thống dễ bảo trì hơn trong thời hạn dài . Thứ hai, không có nhiều trường hợp sử dụng trong đó các lớp con của số Xem cần phụ thuộc bổ sung. Thực tế là bạn cần phải thực sự làm việc để cung cấp những phụ thuộc này sẽ cung cấp cho bạn một chút thời gian để suy nghĩ xem việc cung cấp cho họ có phải là quyết định thiết kế hay không để bắt đầu.

Lợi thế thứ hai liên quan đến xây dựng cộng tác. Bạn có thể là kỹ sư phần mềm rất có kinh nghiệm, nhưng bạn sẽ có lẽ cũng có ít bạn đồng đội có kinh nghiệm hơn. Hoặc có thể bạn sẽ rời khỏi dự án một ngày và anh chàng sẽ tiếp quản sẽ không được tốt như bạn.Bằng cách tiêm một phụ thuộc duy nhất bằng cách sử dụng khung công tác , về cơ bản bạn mở một cánh cửa để tiêm khác. Hãy tưởng tượng rằng một số dữ liệu từ SharedPreferences trở nên bắt buộc trong Chế độ xem tùy chỉnh để ví dụ: sửa lỗi. Một trong những nhà phát triển ít kinh nghiệm có thể quyết định rằng đó là cách tiếp cận tốt để tiêm SharedPreferences vào Chế độ xem tùy chỉnh trực tiếp. Làm điều này vi phạm Nguyên tắc một lần Nguyên tắc, nhưng nhà phát triển đó thậm chí có thể không nhận thức được khái niệm như vậy. Do đó, về lâu dài, việc tiêm “backdoors” như vậy có thể giảm chất lượng thiết kế và dẫn đến các phiên gỡ lỗi dài.

Ưu điểm thứ ba của việc sử dụng Phương pháp Injection với Chế độ xem tùy chỉnh là mà bạn không ghép khung công cụ Xem thành phần phụ thuộc. Chỉ cần hãy tưởng tượng rằng vài năm kể từ bây giờ bạn (hoặc một số anh chàng tội nghiệp khác) cần phải thay thế khuôn khổ. Thực tế là có thể bạn sẽ có hàng chục Hoạt động và Phân đoạn để bắt đầu sẽ làm cho cuộc sống của bạn trở nên khốn khổ. Nếu bạn có thêm hàng chục hoặc hàng trăm Chế độ xem tùy chỉnh để xử lý, thì nó có thể đưa bạn vào tâm trạng tự sát.

Lợi thế cuối cùng (nhưng không kém phần quan trọng) là hiệu suất. Một màn hình có thể chứa một Hoạt động, một số Phân đoạn và hàng chục Chế độ xem tùy chỉnh. Khởi động lại số lượng lớp này bằng cách sử dụng tiêm phụ thuộc khuôn khổ có thể làm giảm hiệu suất của ứng dụng. Nó đặc biệt là đúng cho các khung dựa trên phản chiếu, nhưng thậm chí Dagger còn mang một số chi phí hiệu suất .

Ngoài ra, tôi khuyên bạn nên tránh phương pháp phun mới liên quan đến lớp AndroidInjection. Nó được thảo luận trong this video tutorial.

2

Các vấn đề liệu các lớp dao găm-android như AndroidInjector nên hỗ trợ tiêm bên trong xem hay không đã được thảo luận trong các vấn đề Github sau:

https://github.com/google/dagger/issues/720

Trích dẫn từ một trong những tác giả thư viện:

Có cả điểm triết học và điểm triển khai/hậu cần phải được thực hiện tại đây.

Trước tiên, chúng tôi không hoàn toàn rõ ràng rằng chế độ xem tiêm là điều đúng đắn cần làm. Xem các đối tượng có nghĩa là để vẽ, và không nhiều khác. Bộ điều khiển (trong một mẫu MVC truyền thống) là một bộ điều khiển có thể phối hợp và truyền dữ liệu thích hợp cho một khung nhìn. Việc tiêm một chế độ xem làm mờ các dòng giữa các đoạn và các khung nhìn (có lẽ một đoạn con thực sự là một cấu trúc thích hợp thay thế?)

Từ góc độ triển khai, cũng có một vấn đề trong đó không phải là cách hợp lý để truy xuất Phân đoạn gốc (nếu có) hoặc Hoạt động để truy lục thành phần chính. Đã có những hacks được đề xuất để xây dựng trong mối quan hệ đó, nhưng cho đến nay chúng tôi đã không thấy bất cứ điều gì có vẻ như gợi ý rằng chúng tôi có thể làm điều này một cách chính xác. Chúng ta chỉ có thể gọi View.getContext(). GetApplicationContext() và tiêm từ đó, nhưng bỏ qua các lớp trung gian mà không có bất kỳ tùy chọn nào ở giữa là không phù hợp với phần còn lại của thiết kế và có thể gây nhầm lẫn cho người dùng ngay cả khi nó hoạt động.

Điều này củng cố ý kiến ​​thể hiện trong câu trả lời của Vasily.

Để thêm hơn nữa, mọi người thường có vẻ muốn chèn phụ thuộc lớp mô hình vào bên trong chế độ xem tùy chỉnh của họ. Đây là một ý tưởng tồi khi nó đi ngược lại nguyên tắc kỹ thuật phần mềm của separation of concerns.

Giải pháp đúng để liên kết chế độ xem và mô hình là viết bộ điều hợp như bộ điều hợp cho RecyclerView và ListView. Bạn có thể tiêm phụ thuộc lớp mô hình ở mức Fragment hoặc Presenter và thiết lập bộ điều hợp ở đó.

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