如何使用dagger2将活动注入适配器

ant*_*009 6 android dagger-2

Android Studio 3.0 Canary 8

我正在尝试将我的MainActivity注入我的适配器.但是,我的解决方案工作正常,但我认为它的代码味道并不是正确的方法.

我的适配器代码段看起来像这样,但我不喜欢这是我必须转换ActivityMainActivity:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

在我的模块中,我在模块的构造函数中传递Activity并将其传递给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);
    }
}
Run Code Online (Sandbox Code Playgroud)

在我的应用程序类中,我创建组件,我使用SubComponent作为适配器.在这里,我必须通过我不确定是一个好主意的活动.

@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));
}
Run Code Online (Sandbox Code Playgroud)

我的片段我这样注入:

@Inject RecipeAdapter recipeAdapter;

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

        ((BusbyBakingApplication)getActivity().getApplication())
                .createRecipeListComponent(getActivity())
                .inject(this);
    }
Run Code Online (Sandbox Code Playgroud)

即使上面的设计有效,我认为这是一个代码味道,因为我必须将Activity转换为MainActivity.我之所以使用Activity,是因为我想让这个模块更通用.

只是想知道是否有更好的方法

===============更新使用接口

接口

public interface RecipeItemClickListener {
    void onRecipeItemClick(Recipe recipe);
}
Run Code Online (Sandbox Code Playgroud)

履行

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

在我的模块中,我有以下提供者

@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);
    }
}
Run Code Online (Sandbox Code Playgroud)

然后我通过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;
    }
}
Run Code Online (Sandbox Code Playgroud)

只有一个问题,是RecipeAdapter中构造函数的@Inject注释需要.因为它可以使用或不使用@Inject.

Yos*_*gev 11

不要将活动传递给适配器 - 这是一个非常糟糕的做法.

仅注入您关心的字段.

在您的示例中:将接口传递到适配器以跟踪项目单击.


Dav*_*jak 7

如果你需要,MainActivity那么你也应该提供它.而不是为您的模块Activity声明MainActivity.

@Module
public class RecipeListModule {
  private MainActivity activity;

  public RecipeListModule(MainActivity activity) {
    this.activity = activity;
  }
}
Run Code Online (Sandbox Code Playgroud)

你的适配器应该只是请求它( Android Framework类型的构造函数注入!)

@RecipeListScope
class RecipeAdapter {

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

}
Run Code Online (Sandbox Code Playgroud)

如果你想要你的模块使用Activity而不是MainActivity那么你需要声明一个已经提到过的接口.然后,适配器会将接口声明为其依赖项.

但在某些模块中,您仍然需要将该接口绑定到您的MainActivity模块,并且一个模块需要知道如何提供依赖项.

// in some abstract module
@Binds MyAdapterInterface(MainActivity activity) // bind the activity to the interface
Run Code Online (Sandbox Code Playgroud)

解决问题的更新部分

只有一个问题,是@InjectRecipeAdapter中构造函数的注释需求.因为它可以使用或不使用@Inject.

它没有它,因为你还没有使用构造函数注入.你还在调用构造自己providesRecipeAdapter().作为一般的经验法则 - 如果你想正确使用Dagger - 不要自称new.如果你new想问自己是否可以使用构造函数注入.

您显示的相同模块可以编写如下,使用@Binds绑定实现到接口,并实际使用构造函数注入来创建适配器(这就是为什么我们不必为它编写任何方法!更少的代码到维护,减少错误,更易读的类)

如你所见,我不需要new自己使用--Dagger会为我创建物体.

public abstract class RecipeListModule {
  @RecipeListScope
  @Binds
  RecipeItemClickListener providesRecipeClickListener(RecipeItemClickListenerImp listener);
}
Run Code Online (Sandbox Code Playgroud)