Android,Dagger 2在片段内注入recyclerview

时间:2016-10-27 07:32:44

标签: android dagger-2

正如我之前读过的那样,dagger会发出所有构造函数并自己提供它们,因此我们的业务中几乎不应该有任何构造函数,现在想象RecyclerView内部有一个Fragment并且Activity正在注入片段,如何为片段注入LayoutManagerAdapter以及Presenter(不将其注入活动内并将参数传递给片段)

我正在处理的示例是here,但他根据Activity模式将View本身用作MVP,但我正在尝试使用{{1}相反。

This是他的Fragment(此处注册了回收者视图和演示者)。

我的代码:

UserComponent,它是AppComponent

的子组件
Activity

RepoListModule:

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

     UserEntity getUserEntity();
}

RepoListComponent:

@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);
  }
}

RepoListActivity:

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

RepoListFragment:

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);
  }
}

1 个答案:

答案 0 :(得分:2)

我不确定我是否完全理解这些词:" ...所以我们的业务中几乎不应该有任何构造函数......" 。通常我会经过 - 每次看到new运算符时,根据经验表明你对实例化的类有严格的依赖关系,你应该尝试删除它。也就是说,这就是我如何处理你的任务(肯定有更多解决方案,这只是一种方法)。

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

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);
}

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

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

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中注入的变量的ActivityUtils.addFragmentToActivity。这些是片段生命周期的一部分,它将在活动中添加(假设enum model_class { ... }; struct SModelData { string m_strName; model_class m_class; vector<int> m_parts; int m_iSeats; SModelData(...) : ... {} }; const multimap<int, SModelData*> models_data = { { 400, new SModelData("Landstalker", STUFF, ***, 4) }, ... }; 实际添加/替换布局中的片段)。

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