Consider the following scenario:
Activity
with UI elements that launches a DialogFragment
when clickedDialogFragment
has a listener interface
that the Activity
provides an implementation of. Say for example, the Activity
is an image editor and the DialogFragment
selects a contrast - the dialog would have a OnContrastChangedListener
that the Activity
implementsThe Activity
has implemented this interface
to update views in its UI. Continuing the image editor example, the Activity
implements the OnContrastChangedListener
to update its preview view - something like this:
contrastDialog.setOnContrastChangedListener(new OnContrastChangedListener {
@Override
public void OnContrastChanged(int newContrast) {
getPreviewView().updateWithContrast(newContrast);
}
});
The orientation is changed, and everything is recreated and the listener saved and restored correctly using the methods recommended here (listener is saved in a Fragment
and restored when the lifecycle is restoring state).
The problem is the listener interface now does not work. The function getPreviewView()
is now returning null
even though when called anywhere else in the Activity
it returns the correct value
Excuse the poor terminology (my knowledge in compiling and bytecode is limited), but I can grasp what has happened. The interface has been compiled with the getPreviewView()
version that returned the preview view that was destroyed on the orientation change, and this has since been released / garbage collected / is now null
.
My question is, is there a way in Java to make the interface compile expecting the values / functions to change - much like the volatile
keyword in C (I am expecting there isn't)? In that case, what's the best approach for getting around this type of situation? I have considered the following:
DialogFragment
(and its interface
) in the code that is rerun when the Activity
is recreated. This is fine for things like OnClickListeners
for Buttons
as they are definitely created. But this DialogFragment
is only created when a button is pressed, so this approach means every dialog for the screen is created each time the Activity
is - this seems wasteful given they may not even be run interfaces
for the Activity
every time and save them in member variables, then use these interfaces
when the DialogFragments
are requested to be created by the event. Same comments as above - seems wasteful creating every possible interface
just in case it is run. interfaces
. Hacky and creates a tie between the Activity
and the DialogFragment
which isn't great practice.As you can see, all options involve recreation that is wasteful to some extent - is there a way to reuse the existing interface
implementation?
EDIT: Options 1 and 2 won't work because they need a link to the existing Dialog
. This is all doable but it is leaning more and more towards the hacked together option of having 'current Dialog' variables, getting the DialogFragment
with FragmentManager
when the activity is restarted, casting it appropriately based on the 'current Dialog' variable, recreating the listener. Is there a less messy way?
the onAttach
onDetach
method is good and I like using it, sometimes, when I know there will be more developers in the code I don't even cast it blindly, but I do a check like this:
if(activity instanceof MyInterface){
interface = (MyInterface) activity;
} else{
thrown new RuntimeException("Dear colleague, this fragment was meant to have the activity implementing MyInterface or else a bunch of other stuff won't work, please go back to your code and add the interface");
}
but as a different resort, you can also re-set the interface when the fragment is recreated. For example, on the activity onCreate
if(savedInstanceState != null){
mDialogFrag = getSupportFragmentManager().findFragmentByTag(MyDialogFrag.TAG);
if(mDialogFrag != null)
mDialogFrag.setListener(... the interface ...);
}
I know that's also not the best separation of objects, but the fact is that getPreviewView()
NEEDS the current activity to proper operate, so you NEED to pass this reference again when everybody gets destroyed n rebuilt.
Those are just different ways of doing it.