使用Dagger2无法向ViewHolder注入PictureDownloader类的求助
核心原因很明确:Dagger 不会自动注入 RecyclerView.ViewHolder 实例。你在 HomeListViewHolder 中标注 @Inject 的 mPictureDownloader 字段,并没有被 Dagger 初始化,所以才会始终为 null。Dagger 默认只处理 Android 系统组件(Activity、Fragment、Service 等)的注入,ViewHolder 不在这个自动注入的范围内。
下面提供两种方案,从快速修复到规范注入,你可以根据需求选择:
方案一:快速修复——从 Application 直接获取实例
你的 MarvelApplication 已经成功注入了 PictureDownloader,并且提供了 getter 方法,我们可以直接在 ViewHolder 中通过 Application 获取这个单例实例:
public class HomeListViewHolder extends RecyclerView.ViewHolder { private PictureDownloader mPictureDownloader; @BindView(R.id.image_character) ImageView mImageView; private View mItemView; private HomeListListener mListener; public HomeListViewHolder(View itemView, final HomeListListener listener) { super(itemView); ButterKnife.bind(this, itemView); this.mItemView = itemView; this.mListener = listener; // 从 Application 获取已注入的 PictureDownloader 实例 mPictureDownloader = ((MarvelApplication) itemView.getContext().getApplicationContext()).getPictureDownloader(); } // ... 其余方法保持不变 }
这种方式不需要修改任何 Dagger 配置,能快速解决 null 问题,适合快速验证功能。
方案二:规范注入——通过依赖传递让 ViewHolder 获取实例
如果想遵循 Dagger 的依赖注入规范,我们可以通过「Activity → Adapter → ViewHolder」的路径传递 PictureDownloader 实例:
1. 在宿主 Activity 中注入 PictureDownloader
确保你的宿主 Activity(比如 HomeActivity)已经被 Dagger 注入,然后拿到 PictureDownloader 实例:
public class HomeActivity extends AppCompatActivity { @Inject PictureDownloader mPictureDownloader; @Override protected void onCreate(Bundle savedInstanceState) { AndroidInjection.inject(this); // 确保 Activity 被 Dagger 注入 super.onCreate(savedInstanceState); setContentView(R.layout.activity_home); RecyclerView recyclerView = findViewById(R.id.recycler_home); // 将 PictureDownloader 传递给 Adapter HomeListAdapter adapter = new HomeListAdapter(mPictureDownloader, this); recyclerView.setAdapter(adapter); } }
2. 在 Adapter 中接收并传递实例
修改 Adapter 的构造方法,接收 PictureDownloader,并在创建 ViewHolder 时传递给它:
public class HomeListAdapter extends RecyclerView.Adapter<HomeListViewHolder> { private PictureDownloader mPictureDownloader; private HomeListListener mListener; public HomeListAdapter(PictureDownloader pictureDownloader, HomeListListener listener) { mPictureDownloader = pictureDownloader; mListener = listener; } @NonNull @Override public HomeListViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_home, parent, false); // 将 PictureDownloader 传递给 ViewHolder return new HomeListViewHolder(itemView, mListener, mPictureDownloader); } // ... 其余方法保持不变 }
3. 修改 ViewHolder 构造方法接收实例
移除 ViewHolder 中的 @Inject 注解,改为通过构造方法接收 PictureDownloader:
public class HomeListViewHolder extends RecyclerView.ViewHolder { private PictureDownloader mPictureDownloader; @BindView(R.id.image_character) ImageView mImageView; private View mItemView; private HomeListListener mListener; public HomeListViewHolder(View itemView, HomeListListener listener, PictureDownloader pictureDownloader) { super(itemView); ButterKnife.bind(this, itemView); this.mItemView = itemView; this.mListener = listener; this.mPictureDownloader = pictureDownloader; } // ... 其余方法保持不变 }
你的现有配置(AppComponent、PictureDownloaderModule)是完全正确的,不需要新增额外的 Module。PictureDownloaderModule 已经正确提供了 CacheImageHandler 和 PictureDownloader 的单例实例,AppComponent 也正确关联了所有必要的 Module,只需通过上述方式让 ViewHolder 获取到实例即可。
内容的提问来源于stack exchange,提问作者Martin Rico Martinez

