medium - Daga 2 en Android: inyecta la misma dependencia en la actividad y el fragmento retenido
dagger android processor (1)
Teniendo en cuenta que su fragmento retenido dura más que su actividad, apostaría a que la forma correcta de hacerlo sería hacer que FragmentScope
contenga el ActivityScope
, y no al revés.
Lo que significa que tu FragmentComponent tendría
@FragmentScope
@Component(modules = {FragmentModule.class})
public interface FragmentComponent {
void inject(MyFragment presenter);
}
Y tu componente de Actividad tendría
@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();
}
Lo cual es posible si las clases de Fragment
Injected no dependen del módulo Activity como dependencias.
Esto se puede hacer con algo similar a
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();
}
}
Y
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;
}
}
EDITAR:
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();
}
}
Tengo objetos de las clases F1
y F2
que quiero inyectar en un Fragmento retenido. También tengo un objeto de clase A
que depende de la Actividad, y quiero que se lo inyecte en esa Actividad y en un Fragmento retenido adjunto al Administrador de Fragmentos de esa Actividad. Escribo el siguiente código Primero, el módulo para la dependencia de la actividad:
@Module
public class MainActivityModule {
private Activity mActivity;
public MainActivityModule(Activity activity) {
mActivity = activity;
}
@Provides
@ActivityScope
public A provideA() {
return new A(mActivity);
}
}
Luego, el componente correspondiente, que debe hacer que el objeto A
esté disponible para sus componentes dependientes:
@ActivityScope
@Component(modules = {MainActivityModule.class})
public interface MainActivityComponent {
void inject(MainActivity activity);
// make the A object available to dependent components
A getA();
}
También escribo el módulo relacionado con Fragmentos:
@Module
public class FragmentModule {
@Provides
@FragmentScope
public F1 provideF1() {
return new F1();
}
@Provides
@FragmentScope
public F2 provideF2() {
return new F2();
}
}
y su componente correspondiente:
@FragmentScope
@Component(modules = {FragmentModule.class}, dependencies = {MainActivityComponent.class})
public interface FragmentComponent {
void inject(MyFragment presenter);
}
Finalmente, inyecté la dependencia de A
en la Actividad, donde también debo llamar a los métodos específicos del ciclo de vida. La actividad también proporciona un método para obtener el componente para que el fragmento pueda usarlo al crear su propio componente:
// in MainActivity.onCreate
mActivityComponent = DaggerMainActivityComponent.builder()
.mainActivityModule(new MainActivityModule(this))
.build();
mActivityComponent.inject(this);
mA.onCreate();
e intento inyectar también las dependencias de A
, F1
, F2
en el Fragmento:
// in MyFragment.onCreate
FragmentComponent component = DaggerFragmentComponent.builder()
.fragmentModule(new FragmentModule())
.mainActivityComponent(((MainActivity) getActivity()).getComponent())
.build();
component.inject(this);
Sin embargo, dado que el Fragmento se conserva, cuando el Sistema destruye y recrea la Actividad reaccionando a un cambio de configuración (por ejemplo, una rotación del dispositivo), el Fragmento mantiene una referencia a la antigua instancia A
, mientras que la nueva Actividad recreó correctamente un nuevo A
instancia para ir con eso. Para evitar este problema, tengo que crear FragmentComponent
e inyectar dependencias en MyFragment.onActivityCreated
en lugar de MyFragment.onCreate
. Por otro lado, esto implica que las dependencias F1
y F2
se vuelven a crear cada vez que se destruye y recrea la actividad; pero son dependencias con ámbito de Fragmento, por lo que deben seguir el ciclo de vida del Fragmento en lugar de la Actividad.
Por lo tanto, mi pregunta es la siguiente: ¿es posible tener dependencias de ámbito diferente inyectadas en un fragmento retenido? Idealmente, las dependencias F1
y F2
deberían inyectarse en MyFragment.onCreate
, mientras que A
dependencia debería ser inyectada en MyFragment.onActivityCreated
. Intenté usar dos componentes diferentes, pero parece que no es posible realizar una inyección parcial. Actualmente, terminé agregando una reasignación explícita de la dependencia Fragmento A
en MyFragment.onActivityCreated
, pero eso no es realmente inyección, ya sabes. ¿Podría hacerse esto de una mejor manera?