Android,Dagger 2 在片段中注入 recyclerview

Android, Dagger 2 inject recyclerview inside fragment

正如我之前读到的,dagger 会发出所有构造函数并自行提供它们,因此我们的业务中几乎不应该有任何构造函数,现在想象 [=17= 中有一个 RecyclerView ] 并且 Activity 正在注入片段,我如何为片段注入 LayoutManagerAdapter 以及 Presenter (不将它们注入 activity 并通过片段的参数)

我正在处理的示例是 here,但他根据 MVP 模式将 Activity 本身用作 View,但我正在尝试使用 Fragment 代替。

This是他的Activity(这里注入了recycler view和presenter)

我的代码:

UserComponent 是 AppComponent 的子组件

@UserScope
@Subcomponent(modules = UserModule.class)
public interface UserComponent {
     RepoListComponent plus(RepoListModule repoListModule);

     UserEntity getUserEntity();
}

RepoListModule:

@Module
public class RepoListModule {
private RepoListContract.View view;

public RepoListModule(RepoListContract.View view) {
    this.view = view;
}

@Provides
RepoListContract.View provideRepoListContractView(){
    return view;
}

@Provides
LinearLayoutManager provideLayoutManager(Context context) {
    return new LinearLayoutManager(context);
}

@Provides
RepoListAdapter provideAdapter(RepoListContract.View view) {
    return new RepoListAdapter(view);
  }
}

RepoListComponent:

@Subcomponent(modules = RepoListModule.class)
public interface RepoListComponent {
    void inject(RepoListContract.View view);
}

RepoListActivity:

public class RepoListActivity extends BaseActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_repo_list);
    RepoListFragment fragment = (RepoListFragment) getSupportFragmentManager()
            .findFragmentById(R.id.fragment_container);
    if (fragment == null) {
        fragment = new RepoListFragment();
        ActivityUtils.addFragmentToActivity(getSupportFragmentManager(),
                fragment, R.id.fragment_container);
    }
    GApplication.get(getApplicationContext())
            .getUserComponent().plus(new RepoListModule(fragment))
            .inject(fragment);
  }
}

RepoListFragment:

public class RepoListFragment extends Fragment implements RepoListContract.View {
@BindView(R.id.rv_repo) RecyclerView rvRepo;

@Inject RepoListContract.Presenter presenter;
@Inject LinearLayoutManager layoutManager;
@Inject RepoListAdapter adapter;

public static RepoListFragment newInstance() {
    return new RepoListFragment();
}

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
                         @Nullable Bundle savedInstanceState) {
    View v = inflater.inflate(R.layout.fragment_repo_list, container, false);
    ButterKnife.bind(this, v);
    initRvRepo();
    return v;
}
private void initRvRepo() {
    rvRepo.setLayoutManager(layoutManager); // null
    rvRepo.setAdapter(adapter); //null
}

@Override
public void onResume() {
    super.onResume();
    presenter.subscribe(); //NullPointerException
  }
}

我不确定我是否完全理解这些词:“...所以我们的业务中几乎不应该有任何构造函数...”。通常我会这样做——根据经验,每次你看到 new 运算符时,这表明你对正在实例化的 class 有很强的依赖性,你应该尝试删除它。也就是说,这就是我处理您的任务的方式(肯定有更多的解决方案,这只是一种方法)。

让我们从最简单的事情开始。让我们创建您的片段:

public RepositoriesListFragment extends Fragment implements RepositoriesListView {
   @Inject RecyclerView.LayoutManager layoutManager;
   @Inject RecyclerView.Adapter adapter;
   @Inject RepositoriesListPresenter presenter;

   public static RepositoriesListFragment newInstance() {
       return new RepositoriesListFragment();
   }
   // ...
}

现在这可能是依赖项的模块:

@Module
public class RepositoriesListModule {
   private final RepositoriesListView view;

   public RepositoriesListModule(RepositoriesListView view) {
      this.view = view;
   }

   @Provides
   public RepositoriesListView providesView() {
      return view;
   }

   @Provides
   public RecyclerView.LayoutManager providesLayoutManager(Context context) {
     return new LinearLayoutManager(context);
   }

   @Provides
   public RecyclerView.Adapter providesAdapter(SomeAdapterImpl adapter) {
     return adapter;
   }

   @Provides
   public RepositoriesListPresenter providesPresenter(SomePresenterImpl presenter) {
      return presenter;
   }
}

首先要注意的是它需要构造函数中的视图。这是因为通常演示者也需要视图。因此 SomePresenterImpl 会在其构造函数中期望此视图。

其次,该模块假定 Context 也在某处提供。很可能是通过组件所依赖的另一个模块。

组件如下:

@Component(modules = { RepositoriesListModule.class, ... })
public interface RepositoriesListComponent {
    void inject(RepositoriesListFragment fragment);
}

(如前所述,此组件可能需要其他模块,甚至依赖于其他组件)。

您需要做的最后一件事是注入片段。正如您所说,有一个 activity 创建和注入片段。所以这可能看起来像:

public class MainActivity extends Activity {
   public void onCreate(Bundle savedInstanceState) {
      // ...
      RepositoriesListFragment fragment = RepositoriesListFragment.newInstance();

      DaggerRepositoriesListComponent.builder()
         .repositoriesListModule(new RepositoriesListModule(fragment))
         .inject(fragment);
      // ...
   }

就像我说的,这不是唯一的方法。这有一个问题,您多次创建组件。如果您确定组件的范围,则需要正确处理它的创建。您不能每次都创建它,否则会使范围变得无用。具有挑战性的部分是,每次您要注入视图时,模块都需要正确的视图,您需要确保模块引用该视图。

希望这对您有所帮助。

编辑:

查看您的代码后,我认为问题与您在注入前添加片段有关,因此基本上这样做可能会解决问题:

public class RepoListActivity extends BaseActivity {

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_repo_list);
    RepoListFragment fragment = (RepoListFragment) getSupportFragmentManager()
        .findFragmentById(R.id.fragment_container);
    if (fragment == null) {
       fragment = new RepoListFragment();
       GApplication.get(getApplicationContext())
        .getUserComponent().plus(new RepoListModule(fragment))
        .inject(fragment);
       ActivityUtils.addFragmentToActivity(
            getSupportFragmentManager(),
            fragment, R.id.fragment_container);
    }
  }
}

为什么会这样?查看您的 RepoListFragment,您正在访问 onResumeonCreateView 中的注入变量。这些是片段生命周期的一部分,一旦将其添加到 activity(假设 ActivityUtils.addFragmentToActivity 实际上 adds/replaces 布局中的片段),它将 运行。

如您所见,这意味着在您有机会注入成员之前,您已经在访问它们。所以你应该在添加之前注入片段。