2017-07-31 18 views
6

Android Studio 3.0 Canary 8Cách đưa Hoạt động vào Bộ điều hợp bằng dagger2

Tôi đang cố gắng đưa MainActivity vào Bộ điều hợp của mình. Tuy nhiên, giải pháp của tôi hoạt động tốt, nhưng tôi nghĩ rằng đó là một mùi mã và không đúng cách để làm điều đó.

đoạn chuyển đổi của tôi trông như thế này, nhưng tôi không thích về việc này là tôi phải cast Activity-MainActivity:

public class RecipeAdapter extends RecyclerView.Adapter<RecipeListViewHolder> { 
    private List<Recipe> recipeList = Collections.emptyList(); 
    private Map<Integer, RecipeListViewHolderFactory> viewHolderFactories; 
    private MainActivity mainActivity; 

    public RecipeAdapter(Activity activity, Map<Integer, RecipeListViewHolderFactory> viewHolderFactories) { 
     this.recipeList = new ArrayList<>(); 
     this.viewHolderFactories = viewHolderFactories; 
     this.mainActivity = (MainActivity)activity; 
    } 

    @Override 
    public RecipeListViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) { 
     /* Inject the viewholder */ 
     final RecipeListViewHolder recipeListViewHolder = viewHolderFactories.get(Constants.RECIPE_LIST).createViewHolder(viewGroup); 

     recipeListViewHolder.itemView.setOnClickListener(new View.OnClickListener() { 
      @Override 
      public void onClick(View v) { 
       /* Using the MainActivity to call a callback listener */ 
       mainActivity.onRecipeItemClick(getRecipe(recipeListViewHolder.getAdapterPosition())); 
      } 
     }); 

     return recipeListViewHolder; 
    } 
} 

Trong mô-đun của tôi, tôi vượt qua Hoạt động trong constructor của mô-đun và chuyển nó vào Adapter.

@Module 
public class RecipeListModule { 
    private Activity activity; 

    public RecipeListModule() {} 

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

    @RecipeListScope 
    @Provides 
    RecipeAdapter providesRecipeAdapter(Map<Integer, RecipeListViewHolderFactory> viewHolderFactories) { 
     return new RecipeAdapter(activity, viewHolderFactories); 
    } 
} 

Trong lớp Ứng dụng của tôi, tôi tạo thành phần và tôi đang sử dụng SubComponent cho bộ điều hợp. Ở đây tôi phải vượt qua Hoạt động mà tôi không chắc chắn là một ý tưởng hay.

@Override 
public void onCreate() { 
    super.onCreate(); 

    applicationComponent = createApplicationComponent(); 
    recipeListComponent = createRecipeListComponent(); 
} 

public BusbyBakingComponent createApplicationComponent() { 
    return DaggerBusbyBakingComponent.builder() 
      .networkModule(new NetworkModule()) 
      .androidModule(new AndroidModule(BusbyBakingApplication.this)) 
      .exoPlayerModule(new ExoPlayerModule()) 
      .build(); 
} 

public RecipeListComponent createRecipeListComponent(Activity activity) { 
    return recipeListComponent = applicationComponent.add(new RecipeListModule(activity)); 
} 

Fragment Mỹ I tiêm như thế này:

@Inject RecipeAdapter recipeAdapter; 

    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 

     ((BusbyBakingApplication)getActivity().getApplication()) 
       .createRecipeListComponent(getActivity()) 
       .inject(this); 
    } 

Mặc dù các tác phẩm thiết kế trên, tôi nghĩ rằng đó là một mùi mã như tôi phải cast Hoạt động với MainActivity. Lý do tôi sử dụng Hoạt động vì tôi muốn làm cho mô-đun này trở nên chung chung hơn.

Chỉ cần tự hỏi nếu có một cách tốt hơn

=============== CẬP NHẬT sử dụng giao diện

Interface

public interface RecipeItemClickListener { 
    void onRecipeItemClick(Recipe recipe); 
} 

Thực hiện

public class RecipeItemClickListenerImp implements RecipeItemClickListener { 
    @Override 
    public void onRecipeItemClick(Recipe recipe, Context context) { 
     final Intent intent = Henson.with(context) 
       .gotoRecipeDetailActivity() 
       .recipe(recipe) 
       .build(); 

     context.startActivity(intent); 
    } 
} 

Trong mô-đun của mình, tôi có các nhà cung cấp sau đây

@Module 
public class RecipeListModule { 
    @RecipeListScope 
    @Provides 
    RecipeItemClickListener providesRecipeItemClickListenerImp() { 
     return new RecipeItemClickListenerImp(); 
    } 

    @RecipeListScope 
    @Provides 
    RecipeAdapter providesRecipeAdapter(RecipeItemClickListener recipeItemClickListener, Map<Integer, RecipeListViewHolderFactory> viewHolderFactories) { 
     return new RecipeAdapter(recipeItemClickListener, viewHolderFactories); 
    } 
} 

Sau đó, tôi sử dụng nó thông qua constructor injection trong RecipeAdapter

public class RecipeAdapter extends RecyclerView.Adapter<RecipeListViewHolder> { 
    private List<Recipe> recipeList = Collections.emptyList(); 
    private Map<Integer, RecipeListViewHolderFactory> viewHolderFactories; 
    private RecipeItemClickListener recipeItemClickListener; 

    @Inject /* IS THIS NESSESSARY - AS IT WORKS WITH AND WITHOUT THE @Inject annotation */ 
    public RecipeAdapter(RecipeItemClickListener recipeItemClickListener, Map<Integer, RecipeListViewHolderFactory> viewHolderFactories) { 
     this.recipeList = new ArrayList<>(); 
     this.viewHolderFactories = viewHolderFactories; 
     this.recipeItemClickListener = recipeItemClickListener; 
    } 

    @Override 
    public RecipeListViewHolder onCreateViewHolder(final ViewGroup viewGroup, int i) { 
     /* Inject the viewholder */ 
     final RecipeListViewHolder recipeListViewHolder = viewHolderFactories.get(Constants.RECIPE_LIST).createViewHolder(viewGroup); 

     recipeListViewHolder.itemView.setOnClickListener(new View.OnClickListener() { 
      @Override 
      public void onClick(View v) { 
       recipeItemClickListener.onRecipeItemClick(getRecipe(recipeListViewHolder.getAdapterPosition()), viewGroup.getContext()); 
      } 
     }); 

     return recipeListViewHolder; 
    } 
} 

Chỉ cần một câu hỏi, là chú thích cần @Inject cho các nhà xây dựng trong RecipeAdapter. Vì nó hoạt động có hoặc không có @Inject.

+0

Hãy có một cái nhìn tại Constructor Injection và làm thế nào để sử dụng nó. Việc tạo các phương thức 'cung cấp *' chỉ gọi một hàm tạo là không có gì ngoài tiếng ồn và mã để duy trì. –

Trả lời

7

Nếu bạn cần MainActivity thì bạn cũng nên cung cấp. Thay vì Activity, hãy khai báo MainActivity cho mô-đun của bạn.

@Module 
public class RecipeListModule { 
    private MainActivity activity; 

    public RecipeListModule(MainActivity activity) { 
    this.activity = activity; 
    } 
} 

Và Adaptor của bạn chỉ nên yêu cầu nó (Constructor tiêm cho loại phi Android Khung!)

@RecipeListScope 
class RecipeAdapter { 

    @Inject 
    RecipeAdapter(MainActivity activity, 
      Map<Integer, RecipeListViewHolderFactory> viewHolderFactories) { 
    // ... 
    } 

} 

Nếu bạn muốn mô-đun của bạn để sử dụng Activity và không MainActivity sau đó bạn sẽ cần phải khai báo một giao diện như đã đề cập. Bạn adapter sau đó sẽ tuyên bố giao diện như là phụ thuộc của nó.

Nhưng trong một số mô-đun, bạn sẽ vẫn phải liên kết giao diện đó với MainActivity và một mô-đun cần biết cách cung cấp sự phụ thuộc.

// in some abstract module 
@Binds MyAdapterInterface(MainActivity activity) // bind the activity to the interface 

Phát biểu tại phần cập nhật của câu hỏi

Chỉ cần một câu hỏi, là sự cần thiết @Inject chú thích cho các nhà xây dựng trong RecipeAdapter. Vì nó hoạt động có hoặc không có @Inject.

Nó hoạt động mà không có nó vì bạn vẫn không sử dụng công cụ xây dựng. Bạn vẫn đang gọi cho nhà xây dựng mình trong providesRecipeAdapter(). Theo nguyên tắc chung của ngón tay cái — nếu bạn muốn sử dụng Dagger đúng cách — không bao giờ tự gọi new. Nếu bạn muốn sử dụng new hãy tự hỏi mình nếu bạn có thể sử dụng phương thức khởi tạo để thay thế. Cùng một mô-đun bạn trình bày có thể được viết như sau, sử dụng @Binds để ràng buộc thực hiện với giao diện và thực sự sử dụng hàm tạo bộ tạo để tạo bộ điều hợp (đó là lý do tại sao chúng tôi không phải viết bất kỳ phương thức nào cho ít mã hơn để duy trì, ít lỗi hơn, các lớp dễ đọc hơn)

Như bạn thấy, tôi không cần sử dụng new bản thân mình — Dagger sẽ tạo đối tượng cho tôi.

public abstract class RecipeListModule { 
    @RecipeListScope 
    @Binds 
    RecipeItemClickListener providesRecipeClickListener(RecipeItemClickListenerImp listener); 
} 
8

Không chuyển các hoạt động vào bộ điều hợp - Đây thực sự là một thực tế không tốt.

Chỉ bơm các trường mà bạn quan tâm.

Trong ví dụ của bạn: Chuyển giao diện vào bộ điều hợp để theo dõi nhấp chuột vào mục.

+0

mrsegev sử dụng giao diện như được chỉ định trong câu trả lời của bạn. Tôi đã cập nhật câu hỏi của mình với giải pháp sử dụng giao diện. Chỉ cần tự hỏi là chú thích @Inject cần thiết cho hàm tạo? – ant2009

+0

Tôi hy vọng tôi hiểu câu hỏi của bạn - Nếu bạn muốn các phụ thuộc được tiêm vào sử dụng @Inject annoatation. – mrsegev

+0

Nếu không có chú thích @Inject, Dagger sẽ không sử dụng biểu đồ đối tượng để đáp ứng các phụ thuộc. – mrsegev

2

Cá nhân tôi sẽ làm các trick sau

public class MainActivity extends AppCompatActivity { 
    private static final String TAG = "__ACTIVITY__"; 

    public static MainActivity get(Context context) { 
     // noinspection ResourceType 
     return (MainActivity)context.getSystemService(TAG); 
    } 

    @Override 
    protected Object getSystemService(String name) { 
     if(TAG.equals(name)) { 
      return this; 
     } 
     return super.getSystemService(name); 
    } 
} 

public class RecipeAdapter extends RecyclerView.Adapter<RecipeListViewHolder> { 
    private List<Recipe> recipeList = Collections.emptyList(); 
    private Map<Integer, RecipeListViewHolderFactory> viewHolderFactories; 

    public RecipeAdapter(Map<Integer, RecipeListViewHolderFactory> viewHolderFactories) { 
     this.recipeList = new ArrayList<>(); 
     this.viewHolderFactories = viewHolderFactories; 
    } 

    @Override 
    public RecipeListViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) { 
     /* Inject the viewholder */ 
     final RecipeListViewHolder recipeListViewHolder = viewHolderFactories.get(Constants.RECIPE_LIST).createViewHolder(viewGroup); 

     recipeListViewHolder.itemView.setOnClickListener(new View.OnClickListener() { 
      @Override 
      public void onClick(View v) { 
       MainActivity mainActivity = MainActivity.get(v.getContext()); 
       if(recipeListViewHolder.getAdapterPosition() != -1) { 
        mainActivity.onRecipeItemClick(
         getRecipe(recipeListViewHolder.getAdapterPosition())); 
       } 
      } 
     }); 

     return recipeListViewHolder; 
    } 
} 
+0

Cảm ơn, nhưng tôi nghĩ rằng nó là một hack, và có thể không kiểm tra được. – ant2009

+0

Bạn cũng có thể chuyển các giao diện – EpicPandaForce

+0

Tôi đã cập nhật câu hỏi của mình để sử dụng giao diện thay thế. Xin hãy xem nếu bạn nghĩ rằng có bất kỳ off. – ant2009

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