Android component dependency detection
This is not another article about Dagger and its features. There will not be a word about other DI frameworks.

The purpose of this publication is to demonstrate an approach to obtaining dependencies in fragments, dialogs, and activity.
In one of the inherited projects, I came across the following implementation of the dialogue:
“Since when should a component search for a listener,” I thought at that moment.
Let's make it so that the fragment does not know who exactly implements the listener interface.
Many can immediately offer, for example, this option:
and the activation code in which we will embed this dialog:
This solution has one significant drawback. When changing the configuration (for example, flipping the screen) we get the following chain: the dialogue will save its state in
As a rule, the listener interface is implemented by such Android components as
If our “detective” does not have to change his state in the process of interacting with the component, then you can not redefine the method
Let's look at the implementation:
In this case, the code of our activity will take the form:
If we need to implement the display of the dialogue from the fragment, we get the following code:
As a result, we get that the calling component itself helps the dialogue find the listener. At the same time, the fragment of the concept has no idea who it is (the listener). For example, if for some reason we don’t want to contact
I sort of sorted out the dialogs. We are going further.
Often it is necessary to organize interaction of the type
As an example, we will analyze the case of receiving a presenter in a fragment. I think each of us has come across the following:
And everything seems to be fine, we hid the creation of dependencies, but their similar receipt is now scattered throughout the project. In my opinion, at least the one who calls should either provide these dependencies or help find them.
Again, we apply our technique with the "detective":
Thus, we have made our fragment more universal; accordingly, we can easily transfer it from project to project without changing the component code unnecessarily.
In a similar way, you can organize the receipt of dependencies in
A small example with the implementation of such an approach lies here .
I hope that the described approach will be useful to you in the implementation of projects and will benefit.
Thanks for attention!

The purpose of this publication is to demonstrate an approach to obtaining dependencies in fragments, dialogs, and activity.
Setting a listener for dialogue
In one of the inherited projects, I came across the following implementation of the dialogue:
public class ExampleDialogFragment extends DialogFragment {
private Listener listener;
public interface Listener {
void onMessageEntered(String msg);
}
@Override
public void onAttach (Context context) {
super.onAttach(context);
if(context instanceOf Listener) {
listener = (Listener) context;
} else {
listener = (Listener) getParentFragment();
}
}
}
“Since when should a component search for a listener,” I thought at that moment.
Let's make it so that the fragment does not know who exactly implements the listener interface.
Many can immediately offer, for example, this option:
public class ExampleDialogFragment extends DialogFragment {
private Listener listener;
public interface Listener {
void onMessageEntered(String msg);
}
public static DialogFragment newInstance(Listener listener) {
ExampleDialogFragment dialogFragment = new ExampleDialogFragment();
dialogFragment.listener = listener;
return dialogFragment;
}
}
and the activation code in which we will embed this dialog:
public class ExampleActivity extends AppCompatActivity {
void showDialog() {
DialogFragment dialogFragment = ExampleDialogFragment
.newInstance(new DialogFragment.Listener() {
@Override
void onMessageEntered(String msg) {
// TODO
}
});
dialogFragment.show(getFragmentManager(), "dialog");
}
}
This solution has one significant drawback. When changing the configuration (for example, flipping the screen) we get the following chain: the dialogue will save its state in
Bundle
and be destroyed -> the activity will be deleted -> a new instance of the activity will be created -> the dialogue will be created again based on the saved Bundle
state. As a result, we will lose the link to the listener in the dialogue, since it obviously has not been saved and restored. setListener()
Of course, we can manually call activity in one of the callbacks of the life cycle, but there is another option. Since we cannot store an anonymous class in Bundle
, as well as instances of ordinary classes, we need to observe the following conditions:- The listener must implement an interface
Serializable
orParcelable
- Pass listener through fragment arguments when creating it
setArguments(Bundle args)
- The listener must be stored in the method
onSaveInstanceState(Bundle outState)
- The listener must be restored in the method
Dialog onCreateDialog(Bundle savedInstanceState)
As a rule, the listener interface is implemented by such Android components as
Activity
or Fragment
. Such components are not intended to be stored in Bundle
, so we need to find a different approach to the solution. Let’s try to convey not the listener himself, but the “ Provider” who is able to find him. In this case, no one bothers us to make it serializable and save it to Bundle
. If our “detective” does not have to change his state in the process of interacting with the component, then you can not redefine the method
onSaveInstanceState(Bundle outState)
and Dialog onCreateDialog(Bundle savedInstanceState)
restore the dependency from the arguments when calling the method . Let's look at the implementation:
public class ExampleDialogFragment extends DialogFragment {
private static final String LISTENER_PROVIDER = "listener_provider";
private Listener listener;
public interface ListenerProvider extends Serializable {
Listener from(DialogFragment dialogFragment);
}
public interface Listener {
void onMessageEntered(String msg);
}
public static DialogFragment newInstance(ListenerProvider provider) {
ExampleDialogFragment dialogFragment = new ExampleDialogFragment();
Bundle args = new Bundle();
args.putSerializable(LISTENER_PROVIDER, provider);
dialogFragment.setArguments(args);
return dialogFragment;
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
Bundle args = getArguments();
if(args == null || !args.containsKey(LISTENER_PROVIDER)) {
throw new IllegalStateException("Listener provider is missing");
}
ListenerProvider listenerProvider =
(ListenerProvider) args.getSerializable(LISTENER_PROVIDER);
Listener listener = listenerProvider.from(this);
...
}
}
In this case, the code of our activity will take the form:
public class ExampleActivity extends AppCompatActivity
implements ExampleDialogFragment.Listener {
@Override
public void onMessageEntered(String msg) {
// TODO
}
void showDialog() {
DialogFragment dialogFragment = ExampleDialogFragment
.newInstance(new ListenerProvider());
dialogFragment.show(getFragmentManager(), "dialog");
}
private static class ListenerProvider
implements ExampleDialogFragment.ListenerProvider {
private static final long serialVersionUID = -5986444973089471288L;
@Override
public ExampleDialogFragment.Listener from(DialogFragment dialogFragment) {
return (ExampleDialogFragment.Listener) dialogFragment.getActivity();
}
}
}
If we need to implement the display of the dialogue from the fragment, we get the following code:
public class ExampleFragment extends Fragment
implements ExampleDialogFragment.Listener {
@Override
public void onMessageEntered(String msg) {
// TODO
}
void showDialog() {
DialogFragment dialogFragment = ExampleDialogFragment.newInstance(new ListenerProvider());
dialogFragment.show(getFragmentManager(), "dialog");
}
private static class ListenerProvider implements ExampleDialogFragment.ListenerProvider {
private static final long serialVersionUID = -5986444973089471288L;
@Override
public ExampleDialogFragment.Listener from(DialogFragment dialogFragment) {
return (ExampleDialogFragment.Listener) dialogFragment.getParentFragment();
}
}
}
As a result, we get that the calling component itself helps the dialogue find the listener. At the same time, the fragment of the concept has no idea who it is (the listener). For example, if for some reason we don’t want to contact
Activity
directly, then no one bothers us to simply throw the event, and then catch it and process it in the right place (the dialogue code does not even need to be changed):public class ExampleFragment extends Fragment {
void onMessageEvent(Message message) {
// TODO
}
void showDialog() {
DialogFragment dialogFragment = ExampleDialogFragment.newInstance(new ListenerProvider());
dialogFragment.show(getFragmentManager(), "dialog");
}
private static class Message {
public final String content;
private Message(String content) {
this.content = content;
}
}
private static class ListenerProvider implements ExampleDialogFragment.ListenerProvider {
private static final long serialVersionUID = -5986444973089471288L;
@Override
public ExampleDialogFragment.Listener from(DialogFragment dialogFragment) {
return new ExampleDialogFragment.Listener() {
@Override
public void onMessageEntered(String msg) {
EventBus.getDefault().post(new Message(msg));
}
};
}
}
}
I sort of sorted out the dialogs. We are going further.
Search for dependencies in fragments
Often it is necessary to organize interaction of the type
Activity <-> Fragment
or Fragment <-> Fragment
within the framework of one activity. The general principle remains the same as described above: through the interface (for example, Listener) and the “detective”, communication between the components is organized. In this article, we will consider one-way interaction. As an example, we will analyze the case of receiving a presenter in a fragment. I think each of us has come across the following:
public interface Presenter {
...
}
public class ExampleFragment extends Fragment {
private Presenter presenter;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
presenter = App.get().getExampleFragmentComponent().getPresenter();
}
}
And everything seems to be fine, we hid the creation of dependencies, but their similar receipt is now scattered throughout the project. In my opinion, at least the one who calls should either provide these dependencies or help find them.
Again, we apply our technique with the "detective":
public class ExampleFragment extends Fragment {
private static final String DI_PROVIDER = "di_provider";
private Presenter presenter;
public interface DependencyProvider implements Serializable {
Presenter getPresenterOf(Fragment fragment);
}
public static Fragment newInstance(DependencyProvider dependencyProvider) {
Fragment fragment = new ExampleFragment();
Bundle args = new Bundle();
args.putSerializable(DI_PROVIDER, dependencyProvider);
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle args = getArguments();
if(args == null || !args.containsKey(DI_PROVIDER)) {
throw new IllegalStateException("DI provider is missing");
}
DependencyProvider diProvider =
(DependencyProvider) args.getSerializable(DI_PROVIDER);
presenter = diProvider.getPresenterOf(this);
}
}
public class ExampleActivity extends AppCompatActivity {
void showFragment() {
FragmentTransaction ft = getFragmentManager().beginTransaction();
Fragment fragment = ExampleFragment
.newInstance(new DiProvider());
ft.add(R.id.container, fragment);
ft.commit();
}
private static class DiProvider
implements ExampleFragment.DependencyProvider {
private static final long serialVersionUID = -5986444973089471288L;
@Override
public Presenter get(Fragment fragment) {
return App.get().getExampleFragmentComponent().getPresenter();
}
}
}
Thus, we have made our fragment more universal; accordingly, we can easily transfer it from project to project without changing the component code unnecessarily.
In a similar way, you can organize the receipt of dependencies in
Activity
. A small example with the implementation of such an approach lies here .
I hope that the described approach will be useful to you in the implementation of projects and will benefit.
Thanks for attention!