Deep dive into android restoration - DroidCon Paris 2014

  • Published on
    10-Jun-2015

  • View
    286

  • Download
    0

DESCRIPTION

http://fr.droidcon.com/2014/agenda/ http://fr.droidcon.com/2014/agenda/detail?title=Deep+Dive+Into+State+Restoration Come learn about how Android saves state in general in order to be able to restore an application in the exact same state the process was prior being killed because of a low memory condition or a configuration change. In this talk we will mainly focus on the Parcelable and Parcel objects and how Android uses them to save/restore some important stateful information such as the complete UI state. Speaker : Cyril Mottier, Capitaine Train Cyril Mottier is Mobile Software Engineer at Capitaine Train and Android Google Developer Expert. Passionate about technology and design, Cyril is an avid lover of Android and a multi-skilled engineer. He is actively involved in the Android community and shares his passion writing blog posts, creating open source librairies and giving talks. His motto: Do less, but do it insanely great

Transcript

  • 1. Deep Dive Into AndroidState Restoration

2. TWITTER@cyrilmottier!WEBSITEcyrilmottier.com 3. The story of a newbieAndroid developer 4. Kevin has just developed his firstAndroid app 5. He discovers an annoying bug:Fields are cleared on rotate 6. 3 options 7. 3 optionsDont care 8. 3 optionsDont care Block orientation 9. 3 optionsDont care Block orientation Use configChangesHint: all options are 10. 1 !4 !5 !6 !7 11. Kevins satisfied 12. Still having issues on 13. Still having issues onlanguage changes 14. 1 15. ANGRY! Angry Kevin is 16. 1 !4 !5 !6 !7 17. The nightmare continuesStill having issues when moving the app to the background 18. God save the STATE 19. State restorationkey components 20. The containerParcel 21. The containerParcel The contentPrimitives typesPrimitives arraysParcelable 22. The contentPrimitives typesPrimitives arrays1 parcel.writeInt(1);!2 parcel.writeLong(2L);!3 parcel.writeFloat(3F);!4 parcel.writeString("Hi!");Parcelable 23. 1 parcel.writeIntArray(new int[]{1, 2, 3});!2 parcel.writeLongArray(new long[]{1L, 2L, 3L});!3 parcel.writeDoubleArray(new double[]{1, 2, 3});!4 parcel.writeStringArray(new String[]{!5 "Hi", "Droidcon", "guys!"!6 });The contentPrimitives typesPrimitives arraysParcelable 24. 1 public final class Suggestion implements Parcelable {!2 !3 public final String id;!4 public final String name;!5 public final int type;!6 !7 public Suggestion(String id, String name, int type) {!8 this.id = Objects.requireNonNull(id);!9 this.name = Objects.requireNonNull(name);!10 this.type = type;!11 }!12 !13 } 25. 1 @Override!2 public int describeContents() {!3 return 0;!4 }!5 !6 @Override!7 public void writeToParcel(Parcel dest, int flags) {!8 dest.writeString(id);!9 dest.writeString(name);!10 dest.writeInt(type);!11 }!1213 public static final Parcelable.Creator CREATOR = !14 new Parcelable.Creator() {!15 public Suggestion createFromParcel(Parcel in) {!16 return new Suggestion(in.readString(), // !17 in.readString(), //!18 in.readInt());!19 }!20 !21 public Suggestion[] newArray(int size) {!22 return new Suggestion[size];!23 }!24 }; 26. Parcelable.Creator!The base creator interface!Parcelable.ClassLoaderCreator!A creator with the ClassLoader passed on read.!ParcelableCompat &ParcelableCompatCreatorCallbacks!Compatibility stuff 27. BundleA key-value map & type-safe Parcelable 28. Parcel!internally uses reflection(required to get the CREATOR instance)i.e. beware Proguard 29. Activity levelstate restoration 30. onCreate(null) 31. onCreate(null) 32. : non-null BundleonCreate(null)onSaveInstanceState( ) 33. onCreate(null)onSaveInstanceState( )onCreate( ): non-null Bundle 34. onCreate(null)onSaveInstanceState( )onCreate( )onRestoreInstanceState( ): non-null Bundle 35. onCreate(null)onSaveInstanceState( )onCreate( )onRestoreInstanceState( ): non-null Bundle 36. onCreate(null)onSaveInstanceState( )onCreate( )onRestoreInstanceState( ): non-null Bundle 37. What to save?Non persistent or non reconstructible info 38. 1 public class SearchActivity extends Activity {!2 !3 private static final String STATE_OUTWARD = "state:outward";!4 !5 private DateComponents mOutward;!6 !7 @Override!8 protected void onCreate(Bundle inState) {!9 super.onCreate(inState);!10 !11 if (inState != null) {!12 DateComponents components = inState.getParcelable(STATE_OUTWARD);!13 if (components != null) {!14 setOutward(components);!15 }!16 }!17 }!18 !19 @Override!20 protected void onSaveInstanceState(Bundle outState) {!21 super.onSaveInstanceState(outState);!22 outState.putParcelable(STATE_OUTWARD, mOutward);!23 }!24 } 39. onSaveInstanceState savesWindow 40. onSaveInstanceState savesWindow Fragments 41. onSaveInstanceState savesWindow Fragments Dialogs 42. Always call theSUPER METHODS Android has no guards on save-related methods 43. android:stateNotNeededFor restart-on-crash apps only(i.e. launcher app) 44. Developer optionsDont keep activities 45. Developer optionsDont keep activities 46. View levelstate restoration 47. Android saves UI stateAUTOMAGICALLY 48. Android saves UI stateAUTOMAGICALLY (aka It just works!) 49. Android saves UI stateAUTOMAGICALLY (aka It just works!)except in some cases 50. Works out-of-the-box if Views1. Have an ID2. Are save enabled3. Come from the framework 51. It always begins with a call tosaveHierarchyState() 52. EditText!@id/textRelativeLayout!@id/containerTextViewCheckBox!@id/check_box 53. EditText!@id/textRelativeLayout!@id/containerTextViewCheckBox!@id/check_boxSparseArray 54. RelativeLayout!@id/containerTextViewCheckBox!@id/check_boxSparseArrayS1EditText!@id/text 55. SparseArray@id/containerRelativeLayout!@id/containerTextViewCheckBox!@id/check_boxS1EditText!@id/text 56. EditText!@id/textRelativeLayout!@id/containerTextViewCheckBox!@id/check_boxS2 SparseArray@id/container S1 57. SparseArray@id/container S1@id/textS2RelativeLayout!@id/containerEditText!@id/textTextViewCheckBox!@id/check_box 58. RelativeLayout!@id/containerTextViewCheckBox!@id/check_boxSparseArray@id/container S1@id/textS2S3EditText!@id/text 59. SparseArray@id/container@id/text@id/check_boxS1S2S3RelativeLayout!@id/containerEditText!@id/textTextViewCheckBox!@id/check_box 60. Controlling savesetSaveEnabled(boolean)setSaveFromParentEnabled(boolean) 61. It always ends with a call torestoreHierarchyState() 62. SparseArray@id/container S1@id/text S2@id/check_box S3RelativeLayout!@id/containerEditText!@id/textTextViewCheckBox!@id/check_box 63. SparseArray@id/container S1S2S3@id/text@id/check_boxRelativeLayout!@id/containerEditText!@id/textTextViewCheckBox!@id/check_box 64. RelativeLayout!@id/containerTextViewCheckBox!@id/check_boxSparseArray@id/container S1S2@id/check_box S3S1@id/textEditText!@id/text 65. SparseArray@id/container S1@id/text S2@id/check_box S3RelativeLayout!@id/containerEditText!@id/textTextViewCheckBox!@id/check_box 66. EditText!@id/textRelativeLayout!@id/containerTextViewCheckBox!@id/check_boxS2 SparseArray@id/container S1@id/textS2@id/check_box S3 67. SparseArray@id/container S1@id/text S2@id/check_box S3RelativeLayout!@id/containerEditText!@id/textTextViewCheckBox!@id/check_box 68. RelativeLayout!@id/containerTextViewCheckBox!@id/check_box@id/container S1@id/text S2@id/check_box S3S3SparseArrayEditText!@id/text 69. Ensure your Views IDs areunique & constant 70. 1 static class SavedState extends BaseSavedState {!2 int checked;!3 !4 SavedState(Parcelable superState) { super(superState); }!5 !6 private SavedState(Parcel in) {!7 super(in);!8 checked = in.readInt();!9 }!10 !11 @Override!12 public void writeToParcel(Parcel out, int flags) {!13 super.writeToParcel(out, flags);!14 out.writeInt(checked);!15 }!16 !17 public static final Parcelable.Creator CREATOR = !18 new Parcelable.Creator() {!19 public SavedState createFromParcel(Parcel in) {!20 return new SavedState(in);!21 }!22 public SavedState[] newArray(int size) {!23 return new SavedState[size];!24 }!25 };!26 } 71. 1 @Override!2 public Parcelable onSaveInstanceState() {!3 final Parcelable superState = super.onSaveInstanceState();!4 SavedState ss = new SavedState(superState);!5 ss.checked = isChecked() ? 1 : 0;!6 return ss;!7 }!8 !9 @Override!10 public void onRestoreInstanceState(Parcelable state) {!11 SavedState ss = (SavedState) state;!12 super.onRestoreInstanceState(ss.getSuperState());!13 setChecked(ss.checked == 1);!14 } 72. FrameLayout!@id/rootImageBox!@id/container1CheckBox!@id/check_boxImageView!@id/imageImageBox!@id/container2CheckBox!@id/check_boxImageView!@id/image 73. FrameLayout!@id/rootImageBox!@id/container1ImageView!@id/imageCheckBox!@id/check_boxImageBox!@id/container2ImageView!@id/imageCheckBox!@id/check_boxCustom views with children with same IDs 74. 1 static class SavedState extends BaseSavedState {!2 !3 SparseArray childrenStates;!4 !5 SavedState(Parcelable superState) { super(superState); }!6 !7 private SavedState(Parcel in, ClassLoader loader) {!8 super(in);!9 childrenStates = in.readSparseArray(loader);!10 }!11 !12 @Override!13 public void writeToParcel(Parcel out, int flags) {!14 super.writeToParcel(out, flags);!15 out.writeSparseArray(childrenStates);!16 }!17 !18 public static final Creator CREATOR = ParcelableCompat.!19 newCreator(new ParcelableCompatCreatorCallbacks() {!20 @Override!21 public SavedState createFromParcel(Parcel source, ClassLoader loader) {!22 return new SavedState(source, loader);!23 }!24 @Override!25 public SavedState[] newArray(int size) {!26 return new SavedState[size];!27 }!28 });!29 } 75. 1 @Override!2 public Parcelable onSaveInstanceState() {!3 final Parcelable superState = super.onSaveInstanceState();!4 SavedState ss = new SavedState(superState);!5 ss.childrenStates = new SparseArray();!6 for (int i = 0; i < getChildCount(); i++) {!7 getChildAt(i).saveHierarchyState(ss.childrenStates);!8 }!9 return ss;!10 }!11 !12 @Override!13 public void onRestoreInstanceState(Parcelable state) {!14 SavedState ss = (SavedState) state;!15 super.onRestoreInstanceState(ss.getSuperState());!16 for (int i = 0; i < getChildCount(); i++) {!17 getChildAt(i).restoreHierarchyState(ss.childrenStates);!18 }!19 } 76. That has solved nothing!Still need to block save/restore dispatch 77. 1 @Override!2 protected void dispatchSaveInstanceState(SparseArray container) {!3 dispatchFreezeSelfOnly(container);!4 }!5 !6 @Override!7 protected void dispatchRestoreInstanceState(SparseArray container) {!8 dispatchThawSelfOnly(container);!9 } 78. Fragment levelstate restoration 79. Very similar to Activitiesstate restoration lifecycle.(Fragments are tied to Activity after all) 80. Fragment blocks Activitysave mechanismwith frameworksetSaveFromParentEnabled(false)with support libraryNoSaveStateFrameLayout 81. 2 distinct statesFragment + Viewcommon caseView onlydetach, addToBackStack, etc. 82. Leveraging save/restoreCan to be used to create smooth transitions betweenyour Activities:!- Save the state SA of A- Start B with no animations passing SA- Apply SA to B- Transition between A and B was smooth 83. Summarizingin three rules 84. Always save the stateAn Android app must survive configurationchanges & low memory conditions. 85. Only save essential infoOnly save info that is non persistentor can not be reconstructed later. 86. Use correct levelsSave instance states at the appropriatecomponent level: Activity, Fragment or View. 87. Thank you!@cyrilmottiercyrilmottier.com 88. ResourcesDressed for Iceland Ccile BernardMoelwynion, Eryri, Cymru Marc PoppletonHappy, Confused, Wink, Sad, Angry Megan SheehanFloppy-Disk Alex Auda SamoraFontsSource Sans ProCourier