Managing View State in Android: Techniques and Approaches

satya - Wed Oct 24 2012 08:51:04 GMT-0400 (Eastern Daylight Time)

Managing View State in Android: Techniques and Approaches

Managing View State in Android: Techniques and Approaches

Search for: Managing View State in Android: Techniques and Approaches

satya - Wed Oct 24 2012 08:52:34 GMT-0400 (Eastern Daylight Time)

Here is a discussion on sof

Here is a discussion on sof

satya - Wed Oct 24 2012 08:54:37 GMT-0400 (Eastern Daylight Time)

What is Android BaseSavedState

What is Android BaseSavedState

Search for: What is Android BaseSavedState

satya - Wed Oct 24 2012 08:58:22 GMT-0400 (Eastern Daylight Time)

Here are a couple of examples using onSaveInstanceState

Here are a couple of examples using onSaveInstanceState

satya - Wed Oct 24 2012 08:59:07 GMT-0400 (Eastern Daylight Time)

The nagging issue is the view has to have an ID

And if the view is reused or forget to put the ID then state is not saved...

satya - Wed Oct 24 2012 09:00:32 GMT-0400 (Eastern Daylight Time)

Key APIs seem to be


onSaveInstanceState
saveHierarchyState
dispatchSaveInstanceState
setSaveEnabled
BaseSavedState

satya - Wed Oct 24 2012 09:00:50 GMT-0400 (Eastern Daylight Time)

Another approach is to have the activity or fragment explicitly invoke a save and restore

Another approach is to have the activity or fragment explicitly invoke a save and restore

satya - Wed Oct 24 2012 09:01:52 GMT-0400 (Eastern Daylight Time)

onSaveInstanceState Romain

onSaveInstanceState Romain

Search for: onSaveInstanceState Romain

satya - Wed Oct 24 2012 09:08:53 GMT-0400 (Eastern Daylight Time)

Here is another worthy thread from developers group

Here is another worthy thread from developers group

satya - Wed Oct 24 2012 09:10:22 GMT-0400 (Eastern Daylight Time)

A quick approach if you are using the native view's state capabilities


override the onsaveinstancestate
Create your own bundle
use the return from the super and place it in your bundle
Do the reverse in onrestore...
Make sure to take into account null from the super class

satya - Wed Oct 24 2012 09:11:34 GMT-0400 (Eastern Daylight Time)

Here are some notes from Dianne on subclassing textview in 2008

A static variable is almost certainly not going to do what you want -- one of the big reasons for all of this is to be able to restore state if the process is killed, and if you are putting stuff in a static variable then that will all go away with the process when it is killed.

Unfortunately it does look like there is an issue in TextView with this, which I will need to think about how to fix. There are a lot of optimizations going on to avoid creating, saving, and restoring state objects unless they are needed... unfortunately this causes trouble for people deriving from TextView. Technically what you are supposed to do is take the state object returned by the super class, write it in your own state object, and hand it back to the super class when restoring state. The optimization to return null when there is no state to save is breaking that.

One simple thing you could do is call TextView.setFreezesText() when you are initializing your subclass. This tells the text view it should store its entire text in the state, which will force it to always save its state.

satya - Wed Oct 24 2012 09:14:45 GMT-0400 (Eastern Daylight Time)

Android pattern for using BaseSavedState

Android pattern for using BaseSavedState

Search for: Android pattern for using BaseSavedState

satya - Wed Oct 24 2012 09:18:02 GMT-0400 (Eastern Daylight Time)

This example uses that pattern

This example uses that pattern

satya - Wed Oct 24 2012 09:20:31 GMT-0400 (Eastern Daylight Time)

Here is the View's inner class BaseSavedState


public static class BaseSavedState extends AbsSavedState {
        /**
         * Constructor used when reading from a parcel. Reads the state of the superclass.
         *
         * @param source
         */
        public BaseSavedState(Parcel source) {
            super(source);
        }

        /**
         * Constructor called by derived classes when creating their SavedState objects
         *
         * @param superState The state of the superclass of this view
         */
        public BaseSavedState(Parcelable superState) {
            super(superState);
        }

        public static final Parcelable.Creator<BaseSavedState> CREATOR =
                new Parcelable.Creator<BaseSavedState>() {
            public BaseSavedState createFromParcel(Parcel in) {
                return new BaseSavedState(in);
            }

            public BaseSavedState[] newArray(int size) {
                return new BaseSavedState[size];
            }
        };
    }

satya - Wed Oct 24 2012 09:29:25 GMT-0400 (Eastern Daylight Time)

android Parcelable CREATOR pattern

android Parcelable CREATOR pattern

Search for: android Parcelable CREATOR pattern

satya - Wed Oct 24 2012 09:29:37 GMT-0400 (Eastern Daylight Time)

Bam! here is the link

Bam! here is the link

satya - Wed Oct 24 2012 09:33:28 GMT-0400 (Eastern Daylight Time)

managing view state

managing view state

Search Google for: managing view state

Search Android Developers Group for: managing view state

Search Android Beginers Group for: managing view state

Search Google Code for: managing view state

Search Android Issues Database for: managing view state

satya - Wed Oct 24 2012 09:46:33 GMT-0400 (Eastern Daylight Time)

dispatchSaveInstanceState

dispatchSaveInstanceState

Search for: dispatchSaveInstanceState

satya - Wed Oct 24 2012 09:48:18 GMT-0400 (Eastern Daylight Time)

Here is a formal document from Charles Harley

Here is a formal document from Charles Harley

satya - Wed Oct 24 2012 09:54:42 GMT-0400 (Eastern Daylight Time)

dispatchFreezeSelfOnly

dispatchFreezeSelfOnly

Search for: dispatchFreezeSelfOnly

satya - Wed Oct 24 2012 10:07:18 GMT-0400 (Eastern Daylight Time)

Here is one of the methods of ViewGrouop


protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
        super.dispatchSaveInstanceState(container);
        final int count = mChildrenCount;
        final View[] children = mChildren;
        for (int i = 0; i < count; i++) {
            View c = children[i];
            if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) {
                c.dispatchSaveInstanceState(container);
            }
        }
    }

satya - Wed Oct 24 2012 10:08:14 GMT-0400 (Eastern Daylight Time)

The knarly part is this


protected void dispatchFreezeSelfOnly(SparseArray<Parcelable> container) {
        super.dispatchSaveInstanceState(container);
    }

satya - Wed Oct 24 2012 10:09:16 GMT-0400 (Eastern Daylight Time)

Here is the super class method


protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
        if (mID != NO_ID && (mViewFlags & SAVE_DISABLED_MASK) == 0) {
            mPrivateFlags &= ~SAVE_STATE_CALLED;
            Parcelable state = onSaveInstanceState();
            if ((mPrivateFlags & SAVE_STATE_CALLED) == 0) {
                throw new IllegalStateException(
                        "Derived class did not call super.onSaveInstanceState()");
            }
            if (state != null) {
                // Log.i("View", "Freezing #" + Integer.toHexString(mID)
                // + ": " + state);
                container.put(mID, state);
            }
        }
    }

satya - Wed Oct 24 2012 10:09:49 GMT-0400 (Eastern Daylight Time)

I finally get this knarly string!!!

I finally get this knarly string!!!

satya - Wed Oct 24 2012 13:14:50 GMT-0400 (Eastern Daylight Time)

Here is how textview implements the SavedState


public static class SavedState extends BaseSavedState {
        int selStart;
        int selEnd;
        CharSequence text;
        boolean frozenWithFocus;
        CharSequence error;

        SavedState(Parcelable superState) {
            super(superState);
        }

        @Override
        public void writeToParcel(Parcel out, int flags) {
            super.writeToParcel(out, flags);
            out.writeInt(selStart);
            out.writeInt(selEnd);
            out.writeInt(frozenWithFocus ? 1 : 0);
            TextUtils.writeToParcel(text, out, flags);

            if (error == null) {
                out.writeInt(0);
            } else {
                out.writeInt(1);
                TextUtils.writeToParcel(error, out, flags);
            }
        }

        @Override
        public String toString() {
            String str = "TextView.SavedState{"
                    + Integer.toHexString(System.identityHashCode(this))
                    + " start=" + selStart + " end=" + selEnd;
            if (text != null) {
                str += " text=" + text;
            }
            return str + "}";
        }

        @SuppressWarnings("hiding")
        public static final Parcelable.Creator<SavedState> CREATOR
                = new Parcelable.Creator<SavedState>() {
            public SavedState createFromParcel(Parcel in) {
                return new SavedState(in);
            }

            public SavedState[] newArray(int size) {
                return new SavedState[size];
            }
        };

        private SavedState(Parcel in) {
            super(in);
            selStart = in.readInt();
            selEnd = in.readInt();
            frozenWithFocus = (in.readInt() != 0);
            text = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);

            if (in.readInt() != 0) {
                error = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
            }
        }
    }

satya - Wed Oct 24 2012 13:17:50 GMT-0400 (Eastern Daylight Time)

Here is how it is used in the onSavedInstance


Parcelable superStae = super.onS...();
ss = new SavedState(superState);
ss.set..();
ss.set..();
return ss;

simple enough

satya - Wed Oct 24 2012 13:19:59 GMT-0400 (Eastern Daylight Time)

What happens first in restore


if (!(state instanceof SavedState)) {
            super.onRestoreInstanceState(state);
            return;
        }

if you are not what I sent you let my super deal with it and return. Only recognize if it is me

satya - Wed Oct 24 2012 13:22:09 GMT-0400 (Eastern Daylight Time)

Then do this...


//cast it
SavedState ss = (SavedState)state;

//Call the super first
super.onRestoreInstanceState(ss.getSuperState());
ss.get..();
ss.get..();

//reinitialize your controls as if the view has just been
//created, if needed. Probably you do need.

- Wed Oct 24 2012 13:38:04 GMT-0400 (Eastern Daylight Time)

Here is my specific implementation


public class CircleView extends View
{
     //state
     //Remember this as device flipts
     int defRadius;
.....
    /*
     * ***************************************************************
     * Save and restore work
     * ***************************************************************
     */
    @Override
    protected void onRestoreInstanceState(Parcelable p)
    {
       this.onRestoreInstanceStateStandard(p);
       this.initCircleView();
    }
    @Override
    protected Parcelable onSaveInstanceState()
    {
       return this.onSaveInstanceStateStandard();
    }
    /*
     * ***************************************************************
     * Use a simpler approach
     * ***************************************************************
     */
    private void onRestoreInstanceStateSimple(Parcelable p)
    {
       if (!(p instanceof Bundle))
       {
          throw new RuntimeException("unexpected bundle");
       }
       Bundle b = (Bundle)p;
       defRadius = b.getInt("defRadius");
       Parcelable sp = b.getParcelable("super");
       
       //No need to call parent. It is just a base view
       super.onRestoreInstanceState(sp);
    }
    private Parcelable onSaveInstanceStateSimple()
    {
       //Don't call the base class as it will return a null
       Parcelable p = super.onSaveInstanceState();
       Bundle b = new Bundle();
       b.putInt("defRadius",defRadius);
       b.putParcelable("super",p);
       return b;
    }
    
    /*
     * ***************************************************************
     * Use a standard approach
     * ***************************************************************
     */
    private void onRestoreInstanceStateStandard(Parcelable state)
    {
       if (!(state instanceof SavedState)) {
            super.onRestoreInstanceState(state);
            return;
        }    
       //it is our state
       SavedState ss = (SavedState)state;
        super.onRestoreInstanceState(ss.getSuperState());
        
        defRadius = ss.defRadius;
    }
    private Parcelable onSaveInstanceStateStandard()
    {
       Parcelable superState = super.onSaveInstanceState();
       SavedState ss = new SavedState(superState);
       ss.defRadius = this.defRadius;
       return ss;       
    }
    /*
     * ***************************************************************
     * Saved State inner static class
     * ***************************************************************
     */
    public static class SavedState extends BaseSavedState {
        int defRadius;

        SavedState(Parcelable superState) {
            super(superState);
        }
        @Override
        public void writeToParcel(Parcel out, int flags) {
            super.writeToParcel(out, flags);
            out.writeInt(defRadius);
        }

        @Override
        public String toString() {
           return "CircleView defRadius:" + defRadius;
        }

        @SuppressWarnings("hiding")
        public static final Parcelable.Creator<SavedState> CREATOR
                = new Parcelable.Creator<SavedState>() {
            public SavedState createFromParcel(Parcel in) {
                return new SavedState(in);
            }
            public SavedState[] newArray(int size) {
                return new SavedState[size];
            }
        };

        //Read back the values
        private SavedState(Parcel in) {
            super(in);
            defRadius = in.readInt();
        }
    }//eof-state-class    
}//eof-main-view class

satya - Wed Oct 31 2012 13:26:49 GMT-0400 (Eastern Daylight Time)

Counterpart of dispatchFreezeSelfOnly is dispatchThawSelfOnly

Counterpart of dispatchFreezeSelfOnly is dispatchThawSelfOnly