问题
I have objects of classes F1 and F2 that I want to inject in a retained Fragment. I also have an object of class A that depends on Activity, and I want it to be injected in that Activity and in a retained Fragment attached to that Activity's Fragment Manager. I write the following code. First, the module for the Activity dependency:
@Module
public class MainActivityModule {
private Activity mActivity;
public MainActivityModule(Activity activity) {
mActivity = activity;
}
@Provides
@ActivityScope
public A provideA() {
return new A(mActivity);
}
}
Then, the corresponding component, that must make the A object available to its dependent components:
@ActivityScope
@Component(modules = {MainActivityModule.class})
public interface MainActivityComponent {
void inject(MainActivity activity);
// make the A object available to dependent components
A getA();
}
I also write the Fragment-related module:
@Module
public class FragmentModule {
@Provides
@FragmentScope
public F1 provideF1() {
return new F1();
}
@Provides
@FragmentScope
public F2 provideF2() {
return new F2();
}
}
and its corresponding component:
@FragmentScope
@Component(modules = {FragmentModule.class}, dependencies = {MainActivityComponent.class})
public interface FragmentComponent {
void inject(MyFragment presenter);
}
Finally, I inject the dependency on A in the Activity, where I also need to call specific life cycle methods on it. The Activity also provides a method to get the component so that the Fragment is able to use it when building its own component:
// in MainActivity.onCreate
mActivityComponent = DaggerMainActivityComponent.builder()
.mainActivityModule(new MainActivityModule(this))
.build();
mActivityComponent.inject(this);
mA.onCreate();
and I try to inject the dependencies on A, F1, F2 in the Fragment, too:
// in MyFragment.onCreate
FragmentComponent component = DaggerFragmentComponent.builder()
.fragmentModule(new FragmentModule())
.mainActivityComponent(((MainActivity) getActivity()).getComponent())
.build();
component.inject(this);
However, since the Fragment is retained, when the Activity is destroyed and recreated by the system reacting to a configuration change (e.g. a device rotation), the Fragment maintains a reference to the old A instance, while the new Activity has correctly recreated a new A instance to go with it. To work around this problem, I have to create the FragmentComponent and inject dependencies in MyFragment.onActivityCreated rather than MyFragment.onCreate. On the other hand, this implies that F1 and F2 dependencies are recreated every time the activity is destroyed and recreated; but they are Fragment-scoped dependencies, so they should follow the Fragment life cycle instead of the Activity's.
Therefore, my question is as follows: is it possible to have differently-scoped dependencies injected in a retained Fragment? Ideally, F1 and F2 dependencies should be injected in MyFragment.onCreate, while A dependency should be injected in MyFragment.onActivityCreated. I tried using two different components, but it seems not to be possible to perform partial injection. Currently, I ended up adding an explicit reassignment of the Fragment A dependency in MyFragment.onActivityCreated, but that's not really injection, you know. Could this be done in a better way?
回答1:
Considering your retained fragment lives longer than your activity, I'd wager that the proper way to do this would be to make the FragmentScope contain the ActivityScope, and not vice versa.
Meaning your FragmentComponent would have
@FragmentScope
@Component(modules = {FragmentModule.class})
public interface FragmentComponent {
void inject(MyFragment presenter);
}
And your Activity component would have
@ActivityScope
@Component(dependencies = {FragmentComponent.class}, modules = {MainActivityModule.class})
public interface MainActivityComponent extends FragmentComponent { //provision methods
void inject(MainActivity activity);
// make the A object available to dependent components
A getA();
}
Which is possible if your Fragment injected classes don't rely on the Activity module as dependencies.
This can be done with something akin to
public class MainActivity extends AppCompatActivity {
private MainActivityComponent mainActivityComponent;
private MyFragment myFragment;
@Override
public void onCreate(Bundle saveInstanceState) {
super.onCreate(saveInstanceState);
setContentView(R.layout.activity_main);
if(saveInstanceState == null) { // first run
myFragment = new MyFragment(); //headless retained fragment
getSupportFragmentManager()
.beginTransaction()
.add(myFragment, MyFragment.class.getName()) //TAG
.commit();
} else {
myFragment = (MyFragment)(getSupportFragmentManager()
.findFragmentByTag(MyFragment.class.getName()));
}
}
@Override
public void onPostCreate() {
mainActivityComponent = DaggerMainActivityComponent.builder()
.fragmentComponent(myFragment.getComponent())
.build();
}
}
And
public class MyFragment extends Fragment {
public MyFragment() {
this.setRetainInstance(true);
}
private FragmentComponent fragmentComponent;
@Override
public void onCreate(Bundle saveInstanceState) {
super.onCreate(saveInstanceState);
this.fragmentComponent = DaggerFragmentComponent.create();
}
public FragmentComponent getFragmentComponent() {
return fragmentComponent;
}
}
EDIT:
public class MyFragment extends Fragment {
public MyFragment() {
this.setRetainInstance(true);
this.fragmentComponent = DaggerFragmentComponent.create();
}
private FragmentComponent fragmentComponent;
public FragmentComponent getFragmentComponent() {
return fragmentComponent;
}
}
public class MainActivity extends AppCompatActivity {
private MainActivityComponent mainActivityComponent;
private MyFragment myFragment;
@Inject
A mA;
@Override
public void onCreate(Bundle saveInstanceState) {
super.onCreate(saveInstanceState);
setContentView(R.layout.activity_main);
if(saveInstanceState == null) { // first run
myFragment = new MyFragment(); //headless retained fragment
getSupportFragmentManager()
.beginTransaction()
.add(myFragment, MyFragment.class.getName()) //TAG
.commit();
} else {
myFragment = (MyFragment)(getSupportFragmentManager().findFragmentByTag(MyFragment.class.getName()));
}
mainActivityComponent = DaggerMainActivityComponent.builder()
.fragmentComponent(myFragment.getComponent())
.build();
mainActivityComponent.inject(this);
mA.onCreate();
}
}
来源:https://stackoverflow.com/questions/32113953/dagger-2-on-android-inject-same-dependency-in-activity-and-retained-fragment