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

Here is a discussion on sof

What is Android BaseSavedState

Search for: What is Android BaseSavedState

Here are a couple of examples using onSaveInstanceState

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


onSaveInstanceState
saveHierarchyState
dispatchSaveInstanceState
setSaveEnabled
BaseSavedState

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

onSaveInstanceState Romain

Search for: onSaveInstanceState Romain

Here is another worthy thread from developers group


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

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.

Android pattern for using BaseSavedState

Search for: Android pattern for using BaseSavedState

This example uses that pattern


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];
            }
        };
    }

android Parcelable CREATOR pattern

Search for: android Parcelable CREATOR pattern

Bam! here is the link

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

dispatchSaveInstanceState

Search for: dispatchSaveInstanceState

Here is a formal document from Charles Harley

dispatchFreezeSelfOnly

Search for: dispatchFreezeSelfOnly


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);
            }
        }
    }

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

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);
            }
        }
    }

I finally get this knarly string!!!


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);
            }
        }
    }

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

simple enough


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


//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.

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

Counterpart of dispatchFreezeSelfOnly is dispatchThawSelfOnly