Open doridori opened 8 years ago
Bundle
is an android.os
construct - ideally want to keep core stack code away from android.*
for ease of testing. Simpler approach to a pure java generic serializable object? Maybe just a custom Serializable
will do (or a custom collection of auto typed objects) Also the objects passed into onSave and onRestore could be an arbritrary Serializable also.
Can use below for PilotFrame constructors
//==================================================================//
// Constructor
//==================================================================//
/**
* No arg constructor used to catch when subclasses forget to call super
*/
protected PilotFrame()
{
throw new RuntimeException("Subclasses need to call super(Bundle)");
}
/**
* Subclasses must call super(args) while providing their own single `Args` constructor
*
* @param bundle can be null
*/
protected PilotFrame(Bundle bundle)
{
mBundle = bundle;
}
this would require subclasses to be
Subclass(Args args)
{
super(args)
}
//could have a static convience method so bundle/args setup hidden from callers
public static Subclass create(String str, Serializable arg2)
{
Args args = new args();
args.addString(KEY1, str)
args.addSerializable(KEY2, arg2)
return new Subclass(args)
}
While verbose much cleaner than the alternative AND inside android convention
Also, the stack should have a pluggable persister - which can perform stack operations before the stack is persisted. This could for example take use of a custom @DontPersist
annotation which would clear the stack from this point upwards. Useful for sensitive data that needs to be wiped (i.e. session data) and not persisted.
Need to remove Serializable
from PilotFrame
. Bad practise (effective java)
Ideally this would be pluggable as consumers may want to define their own serialization process (i.e. use GSON etc) and choose how it plugs into the host Activitys saved state bundle. This however needs thinking about - what class would then be passed to the PilotFrame
superclass? Maybe one for a v2 there
Args
ends up being quite verbose in practise without any advantage over plane ol Serializable
. Most of the time I am passing a single object and end up with
public static final String ARG_DATA = "ARG_DATA";
public static Args buildArgs(String enteredData)
{
Args args = new Args();
args.put(ARG_DATA, enteredData);
return args;
}
and then pulling it out of args in the constructor.
Thinking about this is offers no advantage over PilotFrame.super(...) just taking a Serializable
object - as Args
is generics based one still needs to rely on runtime checking when the auto-cast occurs. Either approach is not compile time checked but one of is course more verbose.
Another option is to have PilotFrame specify a generic input type PilotFrame<T extends Serializable>
which types the super(T)
constructor and a T getInputData()
method. However this may muddy the api and will be messy when no arg is required. In this case we could use a Void
like approach with a serializable object...class NoArgs implements Serializable
. Thinking about it generics wont work for runtime checking as we pass the args to PilotStack.push()
so any type errors would still be deferred till runtime.
Args
can still be used if passing multiple data (or just any composite object instead)
TLDR
Serializable
Unlike traditional god-activity / god-fragment approaches we do not really need to worry about persisting state on config change or activity tear-down when in the background, which cuts out a high % of state saving. We do however need to consider the case where process-death occurs and we want the application to be in a similar state to where we left it on return. This issue is regarding the manner in which to save the stack.
As I see it we have 2 options
In turn will briefly talk about these.
Serialize the stack (or parcel)
Advantages
Disadvantages
Serializable
ortransient
Use a primitive representation of the stack
This is much like the approach that Activitys and Fragments already use i.e. take a
Parcelable
Bundle
that represents the inputs (and potentially any loaded data) used to start a frame and persist this. This makes it easy to reload the application to a very similar state to what it was when the process was killed.Advantages
Bundle
and frame class name. Do not have to mark everything up asSerializable
ortransient
Disadvantages
Notes
pushFrameForResult(CallbackToFrameClass, requestCode)
kind of approach...to make this simpler could follow a pattern of having a calling frame implementing a result interface which is then pulled from the stack when a result is ready (looking at the stack item below the one passing the result and checking if it conforms to the correct interface).This would still work after dead-process recreation and would avoid the result codes and request code stuff from current AOSP Activity solutions.onSaveInstanceState
Conclusion
Primitive stack representation makes more sense due to the pros and cons above.
Implementation
Callbacks
Going will a manual inspect stack and pull frame with callback interface approach.
Primitive bundle
PilotFrame
subclasses need to be able to have aBundle
delivered to them. Either a construtor arg called viasuper()
or an initBundle() method. May use a no-arg constructor in PilotFrame that throws an error as the bundle constructor should explitly be called - this will protect against subclasses forgetting to call super.onSave
andonRestore
methods by the stack on process death / recreation