如何使用 dagger2 将 Activity 注入适配器
How to inject an Activity into an Adapter using dagger2
Android Studio 3.0 金丝雀 8
我正在尝试将我的 MainActivity 注入我的 Adapter。然而,我的解决方案工作正常,但我认为它有代码味道而不是正确的方法。
我的适配器代码段看起来像这样,但我不喜欢的是我必须将 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;
}
}
在我的模块中,我在模块的构造函数中传递 Activity 并将其传递给适配器。
@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);
}
}
在我的应用程序中 class 我创建了组件并为适配器使用了 SubComponent。在这里,我必须通过 Activity,我不确定这是个好主意。
@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));
}
我的片段是这样注入的:
@Inject RecipeAdapter recipeAdapter;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((BusbyBakingApplication)getActivity().getApplication())
.createRecipeListComponent(getActivity())
.inject(this);
}
尽管上述设计有效,但我认为这是一种代码味道,因为我必须将 Activity 转换为 MainActivity。我使用 Activity 的原因是我想让这个模块更通用。
只是想知道有没有更好的方法
=============== 使用界面更新
界面
public interface RecipeItemClickListener {
void onRecipeItemClick(Recipe recipe);
}
实施
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);
}
}
在我的模块中,我有以下供应商
@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);
}
}
然后我通过 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;
}
}
只有一个问题,RecipeAdapter 中的构造函数需要@Inject 注释。无论是否使用 @Inject.
不要将 Activity 传递给适配器 - 这是一个非常糟糕的做法。
只注入您关心的字段。
在您的示例中:将接口传递到适配器以跟踪项目点击。
如果您需要 MainActivity
,那么您也应该提供。而不是 Activity
为您的模块声明 MainActivity
。
@Module
public class RecipeListModule {
private MainActivity activity;
public RecipeListModule(MainActivity activity) {
this.activity = activity;
}
}
并且您的适配器应该只请求它(非 Android 框架类型的构造函数注入!)
@RecipeListScope
class RecipeAdapter {
@Inject
RecipeAdapter(MainActivity activity,
Map<Integer, RecipeListViewHolderFactory> viewHolderFactories) {
// ...
}
}
如果您希望您的模块使用 Activity
而不是 MainActivity
,那么您将需要声明一个已经提到的接口。然后,您的适配器会将接口声明为它的依赖项。
但在某些模块中,您仍然需要将该接口绑定到您的 MainActivity
,并且一个模块需要知道如何提供依赖项。
// in some abstract module
@Binds MyAdapterInterface(MainActivity activity) // bind the activity to the interface
解决问题的更新部分
Just one question, is the @Inject
annotation need for the constructor in the RecipeAdapter. As it works with or without the @Inject
.
没有它也能工作,因为你还没有使用构造函数注入。您仍然在 providesRecipeAdapter()
中调用构造函数 您自己 。作为一般经验法则——如果你想正确使用 Dagger——永远不要自己调用 new
。如果您想使用 new
问问自己是否可以改用构造函数注入。
您展示的同一模块可以如下编写,利用 @Binds
将实现绑定到接口,并实际使用构造函数注入来创建适配器(这就是为什么我们没有为它编写任何方法!维护的代码更少,错误更少,可读性更高类)
如您所见,我不需要自己使用 new
— Dagger 会为我创建对象。
public abstract class RecipeListModule {
@RecipeListScope
@Binds
RecipeItemClickListener providesRecipeClickListener(RecipeItemClickListenerImp listener);
}
就我个人而言,我会使用以下技巧
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;
}
}
Android Studio 3.0 金丝雀 8
我正在尝试将我的 MainActivity 注入我的 Adapter。然而,我的解决方案工作正常,但我认为它有代码味道而不是正确的方法。
我的适配器代码段看起来像这样,但我不喜欢的是我必须将 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;
}
}
在我的模块中,我在模块的构造函数中传递 Activity 并将其传递给适配器。
@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);
}
}
在我的应用程序中 class 我创建了组件并为适配器使用了 SubComponent。在这里,我必须通过 Activity,我不确定这是个好主意。
@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));
}
我的片段是这样注入的:
@Inject RecipeAdapter recipeAdapter;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((BusbyBakingApplication)getActivity().getApplication())
.createRecipeListComponent(getActivity())
.inject(this);
}
尽管上述设计有效,但我认为这是一种代码味道,因为我必须将 Activity 转换为 MainActivity。我使用 Activity 的原因是我想让这个模块更通用。
只是想知道有没有更好的方法
=============== 使用界面更新
界面
public interface RecipeItemClickListener {
void onRecipeItemClick(Recipe recipe);
}
实施
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);
}
}
在我的模块中,我有以下供应商
@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);
}
}
然后我通过 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;
}
}
只有一个问题,RecipeAdapter 中的构造函数需要@Inject 注释。无论是否使用 @Inject.
不要将 Activity 传递给适配器 - 这是一个非常糟糕的做法。
只注入您关心的字段。
在您的示例中:将接口传递到适配器以跟踪项目点击。
如果您需要 MainActivity
,那么您也应该提供。而不是 Activity
为您的模块声明 MainActivity
。
@Module
public class RecipeListModule {
private MainActivity activity;
public RecipeListModule(MainActivity activity) {
this.activity = activity;
}
}
并且您的适配器应该只请求它(非 Android 框架类型的构造函数注入!)
@RecipeListScope
class RecipeAdapter {
@Inject
RecipeAdapter(MainActivity activity,
Map<Integer, RecipeListViewHolderFactory> viewHolderFactories) {
// ...
}
}
如果您希望您的模块使用 Activity
而不是 MainActivity
,那么您将需要声明一个已经提到的接口。然后,您的适配器会将接口声明为它的依赖项。
但在某些模块中,您仍然需要将该接口绑定到您的 MainActivity
,并且一个模块需要知道如何提供依赖项。
// in some abstract module
@Binds MyAdapterInterface(MainActivity activity) // bind the activity to the interface
解决问题的更新部分
Just one question, is the
@Inject
annotation need for the constructor in the RecipeAdapter. As it works with or without the@Inject
.
没有它也能工作,因为你还没有使用构造函数注入。您仍然在 providesRecipeAdapter()
中调用构造函数 您自己 。作为一般经验法则——如果你想正确使用 Dagger——永远不要自己调用 new
。如果您想使用 new
问问自己是否可以改用构造函数注入。
您展示的同一模块可以如下编写,利用 @Binds
将实现绑定到接口,并实际使用构造函数注入来创建适配器(这就是为什么我们没有为它编写任何方法!维护的代码更少,错误更少,可读性更高类)
如您所见,我不需要自己使用 new
— Dagger 会为我创建对象。
public abstract class RecipeListModule {
@RecipeListScope
@Binds
RecipeItemClickListener providesRecipeClickListener(RecipeItemClickListenerImp listener);
}
就我个人而言,我会使用以下技巧
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;
}
}