{"id":6,"date":"2011-06-17T06:04:55","date_gmt":"2011-06-17T06:04:55","guid":{"rendered":"http:\/\/kratzindustries.com\/CodeRedBlog\/?p=6"},"modified":"2014-03-27T04:44:09","modified_gmt":"2014-03-27T04:44:09","slug":"flexible-way-to-save-android-activity-state","status":"publish","type":"post","link":"https:\/\/burnt-traces.com\/?p=6","title":{"rendered":"Flexible way to save Android Activity State"},"content":{"rendered":"<p>Today I would like to introduce a flexible and easy method for saving and restoring your Activity state in your Android App. Typically in Android, and Activity can be destroyed by the system for various reasons, either to save memory while the Activity is not visible, or to rebuild the layout when device orientation changes. Whatever the case, it&#8217;s your responsibility to save any variables when the Activity goes away and restore them when it comes back. This is accomplished by overriding the <a href=\"http:\/\/developer.android.com\/reference\/android\/app\/Activity.html#onSaveInstanceState(android.os.Bundle)\"><code>onSaveInstanceState<\/code><\/a> and <a href=\"http:\/\/developer.android.com\/reference\/android\/app\/Activity.html#onRestoreInstanceState(android.os.Bundle)\"><code>onRestoreInstanceState<\/code><\/a> methods of the Activity object. This is explained in more detail <a href=\"http:\/\/developer.android.com\/guide\/topics\/fundamentals\/activities.html#SavingActivityState\">here<\/a>.<\/p>\n<p>Now, what is usually suggested is to set and read info using the various get and put methods of the\u00a0<a href=\"http:\/\/developer.android.com\/reference\/android\/os\/Bundle.html\"><code>Bundle<\/code><\/a> object that is passed to the methods in order to save and restore state. Kinda like this:<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\n\t\tpublic static final String SAVE_KEY1 = &quot;SAVE_KEY1&quot;;\r\n\t\tpublic static final String SAVE_KEY2 = &quot;SAVE_KEY2&quot;;\r\n\t\tpublic static final String SAVE_KEY3 = &quot;SAVE_KEY3&quot;;\r\n\r\n\t\t@Override\r\n\t\tprotected void onSaveInstanceState(Bundle outState)\r\n\t\t{\r\n\t\t\toutState.putString(SAVE_KEY1, stringValue);\r\n\t\t\toutState.putInt(SAVE_KEY2, intValue);\r\n\t\t\toutState.putFloat(SAVE_KEY3, floatValue);\r\n\t\t}\r\n\r\n\t\t@Override\r\n\t\tprotected void onRestoreInstanceState(Bundle savedState)\r\n\t\t{\r\n\t\t\tstringValue = savedState.getString(SAVE_KEY1);\r\n\t\t\tintValue = savedState.getString(SAVE_KEY2);\r\n\t\t\tfloatValue = savedState.getString(SAVE_KEY3);\r\n\t\t}\r\n<\/pre>\n<p>Ok, so you&#8217;re thinking, what&#8217;s the big deal about that? It seems pretty easy. Well, it is pretty easy, and for most situations its good enough. However there are a couple things I don&#8217;t like about the approach. First, if you have a large number of fields to save and restore, this can seem tedious. Also, if your data is not flat (i.e., a\u00a0hierarchy of complex data structures), this simple method might not get the job done. Second, its just a code\u00a0maintenance\u00a0issue.\u00a0Every time you change the data in your activity, you have to update these two functions and re-test them. You run the risk of introducing bugs, like loading the wrong saved value into the wrong\u00a0field.<\/p>\n<p>The solution to these potential complications is simple. Place all your data for your Activity in a class, and then serialize and deserialize that class as a whole. That way, your <code>onSaveInstanceState<\/code> and <code>onRestoreInstanceState<\/code> implementations will be the same no matter the size or complexity of your state information, and if the information changes, you will not have to alter these methods. The only thing you need to do is ensure that your class in which you will save your Activity state is able to be serialized. This is done by marking it with the <a href=\"http:\/\/developer.android.com\/reference\/java\/io\/Serializable.html\"><code>Serializable<\/code><\/a> interface. As long the class is marked and any types within the class are serializable, you are good to go. However, if you do have some object that just can&#8217;t be serialized, you may declare it as <code>transient<\/code>. This prevents it from being serialized with all the other fields. Here is a short example of a serializable class:<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\n\t\tpublic class State implements Serializable\r\n\t\t{\r\n\t\t\tpublic enum ExampleEnum implements Serializable\r\n\t\t\t{\r\n\t\t\t\tPAUSED,\r\n\t\t\t\tRUNNING,\r\n\t\t\t\tOVER\r\n\t\t\t}\r\n\t\t\t\/\/The Transient keyword excludes the field from serialization\r\n\t\t\tpublic transient NonSerializableClass nsc;\r\n\t\t\tpublic int anIntegerValue;\r\n\t\t\tpublic int anotherIntegerValue;\r\n\t\t\tpublic ExampleEnum exampleEnum;\r\n\r\n\t\t\tpublic LinkedList listOfStrings;\r\n\t\t\tpublic LinkedList listOfInts;\r\n\t\t\tpublic LinkedList listOfFloats;\r\n\r\n\t\t}\r\n<\/pre>\n<p>This example just contains some simple values, a couple lists of simple values, and an enumeration. Note that the enumeration is also marked as <code>Serializable<\/code>.<\/p>\n<p>Ok, so the we have a serializable class that holds all the info we need. Now what? Well to actually perform the serialization, we can use the\u00a0<a href=\"http:\/\/developer.android.com\/reference\/java\/io\/ObjectOutputStream.html\"><code>ObjectOutputStream<\/code><\/a> class with the help of the\u00a0<a href=\"http:\/\/developer.android.com\/reference\/java\/io\/ByteArrayOutputStream.html\"><code>ByteArrayOutputStream<\/code><\/a> class to turn our class into a array of bytes that we can place in the Bundle. <code>ObjectOutputStream<\/code> does the work of serializing the object to the <code>ByteArrayOutputStream<\/code>, where the array of bytes is retrieved. \u00a0Then, to do the opposite, we use the\u00a0<a href=\"http:\/\/developer.android.com\/reference\/java\/io\/ObjectInputStream.html\"><code>ObjectInputStream<\/code><\/a> and\u00a0<a href=\"http:\/\/developer.android.com\/reference\/java\/io\/ByteArrayInputStream.html\"><code>ByteArrayInputStream<\/code><\/a> class to turn an array of bytes back into an object. Here is the code for that:<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\n\t\tpublic static final String SAVE_KEY = &quot;SAVE_KEY&quot;;\r\n\r\n\t\tpublic State state;\r\n\r\n\t\t@Override\r\n\t\tprotected void onSaveInstanceState(Bundle outState) throws IOException\r\n\t\t{\r\n            \/\/Serialize state object and write it to bundle\r\n        \tByteArrayOutputStream bos = new ByteArrayOutputStream();\r\n        \tObjectOutput out = new ObjectOutputStream(bos);\r\n        \tout.writeObject(state);\r\n        \tout.flush();\r\n        \tout.close();\r\n        \toutState.putByteArray(SAVE_KEY, bos.toByteArray());\r\n\r\n        }\r\n\r\n\t\t@Override\r\n\t\tprotected void onRestoreInstanceState(Bundle savedState) throws StreamCorruptedException, IOException, ClassNotFoundException\r\n\t\t{\r\n\t\t\tif(savedState != null)\r\n\t\t\t{\r\n\t\t\t\tif(savedState.containsKey(SAVE_KEY))\r\n\t\t\t\t{\r\n\t\t\t\t\tObjectInputStream objectIn = new ObjectInputStream(new ByteArrayInputStream(savedState.getByteArray(SAVE_KEY)));\r\n\t\t\t\t\tObject obj = objectIn.readObject();\r\n\t\t\t\t\tState = (State) obj;\r\n\t\t\t\t}else\r\n\t\t\t\t{\r\n\t\t\t\t\tstate = new State();\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tstate = new State();\r\n\t\t\t}\r\n   \t\t}\r\n<\/pre>\n<p>And thats that. Note that this code was pulled from a working example, but as always issues can be introduced in the simplification and presentation process. If you find a problem, let us know in the comments!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Today I would like to introduce a flexible and easy method for saving and restoring your Activity state in your Android App. Typically in Android, and Activity can be destroyed by the system for various reasons, either to save memory while the Activity is not visible, or to rebuild the layout when device orientation changes.&hellip;&nbsp;<a href=\"https:\/\/burnt-traces.com\/?p=6\" class=\"\" rel=\"bookmark\">Read More &raquo;<span class=\"screen-reader-text\">Flexible way to save Android Activity State<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"neve_meta_sidebar":"","neve_meta_container":"","neve_meta_enable_content_width":"","neve_meta_content_width":0,"neve_meta_title_alignment":"","neve_meta_author_avatar":"","neve_post_elements_order":"","neve_meta_disable_header":"","neve_meta_disable_footer":"","neve_meta_disable_title":"","footnotes":""},"categories":[2,4],"tags":[7,85,9,87,23],"class_list":["post-6","post","type-post","status-publish","format-standard","hentry","category-android","category-java","tag-activity","tag-android","tag-bundle","tag-java","tag-serializable"],"_links":{"self":[{"href":"https:\/\/burnt-traces.com\/index.php?rest_route=\/wp\/v2\/posts\/6","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/burnt-traces.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/burnt-traces.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/burnt-traces.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/burnt-traces.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=6"}],"version-history":[{"count":1,"href":"https:\/\/burnt-traces.com\/index.php?rest_route=\/wp\/v2\/posts\/6\/revisions"}],"predecessor-version":[{"id":332,"href":"https:\/\/burnt-traces.com\/index.php?rest_route=\/wp\/v2\/posts\/6\/revisions\/332"}],"wp:attachment":[{"href":"https:\/\/burnt-traces.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=6"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/burnt-traces.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=6"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/burnt-traces.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=6"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}