Dirk Dougherty | 22558d0 | 2009-12-10 16:25:06 -0800 | [diff] [blame] | 1 | page.title=Faster Screen Orientation Change |
Scott Main | 796ce77 | 2011-02-16 10:04:45 -0800 | [diff] [blame] | 2 | parent.title=Articles |
| 3 | parent.link=../browser.html?tag=article |
Dirk Dougherty | 22558d0 | 2009-12-10 16:25:06 -0800 | [diff] [blame] | 4 | @jd:body |
| 5 | |
Scott Main | a225e3b | 2010-05-11 17:43:43 -0700 | [diff] [blame] | 6 | |
| 7 | <div id="qv-wrapper"> |
| 8 | <div id="qv"> |
| 9 | |
| 10 | <h2>See also</h2> |
| 11 | <ol> |
| 12 | <li><a href="{@docRoot}guide/topics/resources/runtime-changes.html">Handling Runtime |
| 13 | Changes</a></li> |
| 14 | </ol> |
| 15 | |
| 16 | </div> |
| 17 | </div> |
| 18 | |
Dirk Dougherty | 22558d0 | 2009-12-10 16:25:06 -0800 | [diff] [blame] | 19 | <p>Android is designed to run efficiently on a wide |
| 20 | array of devices, with very different hardware configurations. Some |
| 21 | devices, like the T-Mobile G1, can change their hardware configuration |
| 22 | at runtime. For instance, when you open the keyboard, the screen change |
| 23 | from the portrait orientation to the landscape orientation. |
| 24 | |
| 25 | <div class="sidebox-wrapper"> |
| 26 | <div class="sidebox"> |
| 27 | <h2>Using the alternate resources framework</h2> |
| 28 | |
| 29 | <p>The platform's support for loading orientation-specific |
| 30 | resources at run time is based on the alternate resources framework.</p> |
| 31 | |
| 32 | <p>Providing orientation-specific resources is an important part of |
| 33 | developing your app. If you are not familiar with resource directory qualifiers |
| 34 | or how the platform uses them, please read |
| 35 | <a href="{@docRoot}guide/topics/resources/resources-i18n.html#AlternateResources"> |
| 36 | Alternate Resources</a>. |
| 37 | </div> |
| 38 | </div> |
| 39 | |
| 40 | <p>To make |
| 41 | Android app development easier, the Android system automatically handles |
| 42 | configuration change events and restarts the current activity with the new |
| 43 | configuration. This is the default behavior that lets you declare |
| 44 | resources like layouts and drawables based on the orientation, screen |
| 45 | size, locale, etc. </p> |
| 46 | |
| 47 | <p>While this behavior is really powerful, since your application adapts |
| 48 | automatically to the device's configuration at runtime, it is sometimes |
| 49 | confusing for new Android developers, who wonder why their activity is |
| 50 | destroyed and recreated. </p> |
| 51 | |
| 52 | <p>Facing this "issue," some developers choose to handle configuration changes |
| 53 | themselves which is, in general, a short-term solution that will only complicate |
| 54 | their lives later. On the other hand, the system's automatic resource handling |
| 55 | is a very efficient and easy way to adapt an application's user interface to |
| 56 | various devices and devices configurations. It sometimes comes at a price, |
| 57 | though.</p> |
| 58 | |
| 59 | <p>When your application displays a lot of data, or data that is expensive to fetch, |
| 60 | the automatic destruction/creation of the activities can be lead to a |
Scott Main | 52bfc24 | 2012-02-09 10:09:14 -0800 | [diff] [blame] | 61 | painful user experience. Take the example of <a href="http://code.google.com/p/apps-for-android/source/browse/#git%2FPhotostream%2Fsrc%2Fcom%2Fgoogle%2Fandroid%2Fphotostream">Photostream</a>, |
Dirk Dougherty | 22558d0 | 2009-12-10 16:25:06 -0800 | [diff] [blame] | 62 | a simple Flickr browsing application. After you launch the application and choose a Flickr account, the |
| 63 | application downloads a set of 6 photos (on a T-Mobile G1) from the |
| 64 | Flickr servers and displays them on screen. To improve the user |
| 65 | experience, the application uses slightly different layouts and drawables in |
| 66 | portrait and landscape modes and this is what the result looks like:</p> |
| 67 | |
| 68 | <p><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_9l0GmPwgCzk/SZoGyJyg6-I/AAAAAAAAACU/ItuVwhegPb8/s1600-h/photostream_landscape.png"><img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 320px; height: 214px;" src="images/photostream_landscape.png" alt="" id="BLOGGER_PHOTO_ID_5303558969873198050" border="0"></a></p> |
| 69 | |
| 70 | <p><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_9l0GmPwgCzk/SZoGx4I-QlI/AAAAAAAAACM/-GkZR5MUKhY/s1600-h/photostream_portrait.png"><img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 214px; height: 320px;" src="images/photostream_portrait.png" alt="" id="BLOGGER_PHOTO_ID_5303558965135557202" border="0"></a></p> |
| 71 | |
| 72 | <p>Photostream lets Android take care of the configuration change when the |
| 73 | screen is rotated. However, can you imagine how painful it would be for the user |
| 74 | to see all the images being downloaded again? The obvious solution to this |
| 75 | problem is to temporarily cache the images. They could be cached on the SD card |
| 76 | (if there's one), in the Application object, in a static field, etc. None of |
| 77 | these techniques is adapted to the current situation: why should we bother |
| 78 | caching the images when the screen is not rotated? Fortunately for us, Android |
| 79 | offers a great API exactly for that purpose.</p> |
| 80 | |
| 81 | <p>The Activity class has a special method called |
| 82 | {@link android.app.Activity#onRetainNonConfigurationInstance()}. This method |
Scott Main | 52bfc24 | 2012-02-09 10:09:14 -0800 | [diff] [blame] | 83 | can be used to pass an arbitrary object to <em>your future self</em> and Android |
Dirk Dougherty | 22558d0 | 2009-12-10 16:25:06 -0800 | [diff] [blame] | 84 | is smart enough to call this method only when needed. In the case of Photostream, |
Scott Main | 52bfc24 | 2012-02-09 10:09:14 -0800 | [diff] [blame] | 85 | the application used this method |
Dirk Dougherty | 22558d0 | 2009-12-10 16:25:06 -0800 | [diff] [blame] | 86 | to pass the downloaded images to the future activity on orientation change. |
| 87 | The implementation can be summarized like so:</p> |
| 88 | |
| 89 | <pre class="prettyprint">@Override |
| 90 | public Object onRetainNonConfigurationInstance() { |
| 91 | final LoadedPhoto[] list = new LoadedPhoto[numberOfPhotos]; |
| 92 | keepPhotos(list); |
| 93 | return list; |
| 94 | } |
| 95 | </pre> |
| 96 | |
| 97 | <p>In the new activity, in <code>onCreate()</code>, all you have to do to |
| 98 | get your object back is to call {@link android.app.Activity#getLastNonConfigurationInstance()}. |
Scott Main | 52bfc24 | 2012-02-09 10:09:14 -0800 | [diff] [blame] | 99 | In Photostream, this method is invoked |
Dirk Dougherty | 22558d0 | 2009-12-10 16:25:06 -0800 | [diff] [blame] | 100 | and if the returned value is not null, the grid is loaded with the list of |
| 101 | photos from the previous activity:</p> |
| 102 | |
| 103 | <pre class="prettyprint">private void loadPhotos() { |
| 104 | final Object data = getLastNonConfigurationInstance(); |
| 105 | |
| 106 | // The activity is starting for the first time, load the photos from Flickr |
| 107 | if (data == null) { |
| 108 | mTask = new GetPhotoListTask().execute(mCurrentPage); |
| 109 | } else { |
| 110 | // The activity was destroyed/created automatically, populate the grid |
| 111 | // of photos with the images loaded by the previous activity |
| 112 | final LoadedPhoto[] photos = (LoadedPhoto[]) data; |
| 113 | for (LoadedPhoto photo : photos) { |
| 114 | addPhoto(photo); |
| 115 | } |
| 116 | } |
| 117 | } |
| 118 | </pre> |
| 119 | |
| 120 | <p>Be very careful with the object you pass through |
| 121 | <code>onRetainNonConfigurationChange()</code>, though. If the object you |
| 122 | pass is for some reason tied to the Activity/Context, <a |
| 123 | href="http://www.curious-creature.org/2008/12/18/avoid-memory-leaks-on-android/">you will leak</a> |
| 124 | all the views and resources of the activity. This means you should |
| 125 | never pass a View, a Drawable, an Adapter, etc. Photostream for |
| 126 | instance extracts the bitmaps from the drawables and pass the bitmaps |
| 127 | only, not the drawables. Finally, remember that |
| 128 | <code>onRetainNonConfigurationChange()</code> should be used only to retain |
| 129 | data that is expensive to load. Otherwise, keep it simple and let Android |
| 130 | do everything.</p> |
Scott Main | 52bfc24 | 2012-02-09 10:09:14 -0800 | [diff] [blame] | 131 | |
| 132 | <p>Also read the guide to <a href="{@docRoot}guide/topics/resources/runtime-changes.html">Handling Runtime |
| 133 | Changes</a>.</p> |