Saving listeners correctly when changing orientation
Consider the following scenario:
- I have
Activity
with UI elements that fireDialogFragment
when clicked -
DialogFragment
has a listenerinterface
thatActivity
provides an implementation. Let's say for exampleActivity
is an image editor andDialogFragment
picks contrast - there will be a dialog boxOnContrastChangedListener
thatActivity
implements -
Activity
implemented thisinterface
to update views in my UI. Continuing with the image editor example,Activity
implementOnContrastChangedListener
to update its preview - something like this:contrastDialog.setOnContrastChangedListener(new OnContrastChangedListener { @Override public void OnContrastChanged(int newContrast) { getPreviewView().updateWithContrast(newContrast); } });
-
The orientation changes and everything is recreated and the listener is saved and restored correctly using the recommended techniques here (the listener is saved in
Fragment
and restored when the lifecycle restores the state).
The problem is that the listener interface is now broken . The function getPreviewView()
now returns null
, although when called elsewhere in Activity
it returns the correct value
Sorry for the poor terminology (my knowledge of compilation and bytecode is limited), but I can understand what happened. The frontend was compiled with a version getPreviewView()
that returned a preview view that was destroyed on orientation change and has since been released / garbage collected / now null
.
My question is, is there a way in Java to make the compilation of an interface pending value / function change - like a keyword volatile
in C (I expect not)? In that case, what's the best approach to get around this situation? I have considered the following:
-
CreateDialogFragment
(and itinterface
) in code that reruns when recreatedActivity
. It's okay for things likeOnClickListeners
forButtons
, as they are definitely created. But this oneDialogFragment
is only created when the button is pressed, so this approach means every dialog for the screen is created every timeActivity
- it seems wasteful as they might not even be executed -
Create all possibleinterfaces
forActivity
each time and store them in member variables, then use theseinterfaces
when the requestDialogFragments
should be created by the event. The same comments as above - seem wasteful, making everything possibleinterface
just in case it is running. - Keep some of the hacky open dialog member variables in action that define the re-creation
interfaces
. Hacky and creates a connection betweenActivity
andDialogFragment
, which is not a big practice.
As you can see, all options involve leisure, somewhat wasteful - is there a way to reuse an existing implementation interface
?
EDIT: Options 1 and 2 won't work because they need a reference to an existing one Dialog
. This can all be done, but it increasingly relies on the hack option along with the variables of the current "Dialogue", getting DialogFragment
help FragmentManager
when restarting the activity, applying it appropriately based on the variable "current dialog", recreating the listener. Is there a less messy way?
source to share
The method onAttach
onDetach
is good and I like to use it, sometimes when I know there will be more developers in the code, I will not even throw it blindly, but I do the validation 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-install the interface when the fragment is recreated. For example, in actiononCreate
if(savedInstanceState != null){
mDialogFrag = getSupportFragmentManager().findFragmentByTag(MyDialogFrag.TAG);
if(mDialogFrag != null)
mDialogFrag.setListener(... the interface ...);
}
I know this is also not the best separation of objects, but the fact of the matter is that the getPreviewView()
current activity NEEDS the current activity to work properly, so you MUST pass this reference again when everything is destroyed. n restored.
They are just different ways to do it.
source to share
If an activity will always have an interface implementation, is it possible to set a listener in the DialogFragment onAttach ()? This will ensure that when it is destroyed and recreated, it will have the most recent reference, for example:
public void onAttach(Activity activity) {
super.onAttach(activity);
contrastChangedListener = (OnContrastChangedListener)activity;
}
public void onDetach() {
super.onDetach();
contrastChangedListener = null;
}
source to share