Android,Dagger 2 在片段中注入 recyclerview
Android, Dagger 2 inject recyclerview inside fragment
正如我之前读到的,dagger 会发出所有构造函数并自行提供它们,因此我们的业务中几乎不应该有任何构造函数,现在想象 [=17= 中有一个 RecyclerView
] 并且 Activity
正在注入片段,我如何为片段注入 LayoutManager
和 Adapter
以及 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
,您正在访问 onResume
和 onCreateView
中的注入变量。这些是片段生命周期的一部分,一旦将其添加到 activity(假设 ActivityUtils.addFragmentToActivity
实际上 adds/replaces 布局中的片段),它将 运行。
如您所见,这意味着在您有机会注入成员之前,您已经在访问它们。所以你应该在添加之前注入片段。
正如我之前读到的,dagger 会发出所有构造函数并自行提供它们,因此我们的业务中几乎不应该有任何构造函数,现在想象 [=17= 中有一个 RecyclerView
] 并且 Activity
正在注入片段,我如何为片段注入 LayoutManager
和 Adapter
以及 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
,您正在访问 onResume
和 onCreateView
中的注入变量。这些是片段生命周期的一部分,一旦将其添加到 activity(假设 ActivityUtils.addFragmentToActivity
实际上 adds/replaces 布局中的片段),它将 运行。
如您所见,这意味着在您有机会注入成员之前,您已经在访问它们。所以你应该在添加之前注入片段。