My scenario is like this:
I have a one activity architecture of my app, and I have multiple fragments, which I navigate to using the NavController
object.
In my MainActivity
I create an object called Orchestrator
which I want to create just once, and use all over the app and fragments.
The Orchestrator
also holds the context
of the MainActivity
.
I currently pass the object between the app by createing an interface called IShared
look like this:
interface IShared {
Orchestrator getOrchestrator();
}
The MainActivity
implements this interface so it looks something like this:
public activity MainActivity extends AppCompatActivity implements IShared {
private Orchestrator orchestrator;
@Override
public onCreate() {
...
orchestrator = new Orchestrator(requireContext());
...
}
@override
public Orchestrator getOrchestrator() {
return orchestrator;
}
}
and I get the Orchestrator
object in the fragment through the onAttach
method:
private Orchestrator orchestrator;
@Override
public void onAttach(Context context) {
orchestrator = ((Orchestrator1) context).getOrchestrator()
}
but my problem is that the onAttach
method is deprecated, so I guess this is not the best practice of doing this.
What should I do then?
Should I use a ViewModel
that will hold the orchestrator
even though it is not a UI object? it is an object which holds a lot of data that I need to pass between fragments and some of the fragments also changes the data of it.
Use the recommended practices.
Various ideas.
Do not fight the Framework, and don't couple all your fragments with an interface like that. Either inject it directly in the fragments via DependencyInjection, or observe the object through a viewmodel (shared?) and or individual viewModels for each Fragment/Screen. Or Both, you can have a shared viewmodel (provided by the activity) in addition to individual view models for each fragment.
even though it is not a UI object?
If it's not a UI object, then why is it in the UI to begin with?
it's in the UI because some buttons in the UI, changes some of the fields inside the object. for example pressing the "connect" button in the UI, changes the "connected" boolean variable in the orchestrator to true. How can I create this object and add a context to if, if not from the MainActivity?
If pressing a button changes the state of the object, then the correct flow would be:
suspend
function as well (probably not to just update a boolean... but if you needed to do more than that, then probably yes).val newOrch = connectedUseCase.onConnected()
(for e.g. remember this is pseudo-code, there are multiple ways to do this), now emits the new Orchestrator
it just received to a flow
StateFlow
or SharedFlow
(or even LiveData
if you use that, I wouldn't since StateFlow is better most of the times).collect
the new State and react to it (say, for e.g. by changing a text from not connected
to connected
.Now you'd argue this is a lot of overhead for a "simple boolean change" and I'd somewhat agree that indeed it is, but such is the complexity of a modern, decoupled application.
The benefits are mid-long term, as you'd now no longer need to worry about this operation (the Usecase can be reused) and the Activity/Fragment no longer really know much (nor maintain) their "common Orchestrator".
All these concepts are better explained and expanded in the Guide to Android Architecture official website.
As for the final bit of your question:
How can I create this object and add a context to if, if not from the MainActivity?
If this object must exist from the beginning of your app, then your ViewModel#init() (for your activity) could call a UseCase or create it itself. You say it's not a UI object, but it has UI STATE in it, so someone must create it, set the initial state, and hold a reference to it. Either have an Orchestrator "Repository" or "factory" or "OrchestratorInitUseCase" that creates it for you.
You do not need (and should not) have a Context
Stored in any of these things. You only need the Context
to perform Android Framework operations (theme, navigation, etc.) and these can happen in the Fragment/Activity that triggers the action/event once the ViewModel they are observing emits the corresponding value(s). There's no need to send the context anywhere.