Scott Main | f940a1f | 2010-02-09 18:48:27 -0800 | [diff] [blame^] | 1 | page.title=Handling Runtime Changes |
| 2 | parent.title=Application Resources |
| 3 | parent.link=index.html |
| 4 | @jd:body |
| 5 | |
| 6 | <div id="qv-wrapper"> |
| 7 | <div id="qv"> |
| 8 | |
| 9 | <h2>In this document</h2> |
| 10 | <ol> |
| 11 | <li><a href="#CarryingAnObject">Carrying an Object During a Configuration Change</a></li> |
| 12 | <li><a href="#HandlingTheChange">Handling the Configuration Change Yourself</a> |
| 13 | </ol> |
| 14 | |
| 15 | <h2>See also</h2> |
| 16 | <ol> |
| 17 | <li><a href="providing-resources.html">Providing Resources</a></li> |
| 18 | <li><a href="accessing-resources.html">Accessing Resources</a></li> |
| 19 | <li><a href="{@docRoot}resources/articles/faster-screen-orientation-change.html">Faster Screen |
| 20 | Orientation Change</a></li> |
| 21 | </ol> |
| 22 | </div> |
| 23 | </div> |
| 24 | |
| 25 | <p>Some device configurations can change during runtime |
| 26 | (such as screen orientation, keyboard availability, and language). When such a change occurs, |
| 27 | Android's default behavior is to restart the running |
| 28 | Activity ({@link android.app.Activity#onDestroy()} is called, followed by {@link |
| 29 | android.app.Activity#onCreate(Bundle) onCreate()}). In doing so, the system re-queries your |
| 30 | application resources for alternatives that might apply to the new configuration.</p> |
| 31 | |
| 32 | <p>It is important that your Activity safely handles restarts and restores its previous |
| 33 | state through the normal <a href="{@docRoot}guide/topics/fundamentals.html#lcycles">Activity |
| 34 | lifecycle</a>. In fact, it's a useful field test to invoke configuration changes (such as changing |
| 35 | the screen orientation) during various states of your application to be sure that it properly |
| 36 | restarts itself with the application state intact. So it's in the best interest of your application |
| 37 | to allow the system to restart your application during any configuration change—this behavior |
| 38 | is in place to help you by automatically handling configuration changes and adapting your |
| 39 | application as necessary.</p> |
| 40 | |
| 41 | <p>However, you might encounter a situation in which restarting your application and |
| 42 | restoring significant amounts of data can be costly, create a slow user experience, and |
| 43 | using {@link android.app.Activity#onSaveInstanceState(Bundle) onSaveInstanceState()} does not |
| 44 | suffice. In such a situation, you have two options:</p> |
| 45 | |
| 46 | <ol type="a"> |
| 47 | <li><a href="#CarryingAnObject">Carrying an Object During a Configuration Change</a> |
| 48 | <p>Allow your |
| 49 | application to restart so that the appropriate configuration changes can take effect, but also |
| 50 | implement {@link android.app.Activity#onRetainNonConfigurationInstance()} paired with {@link |
| 51 | android.app.Activity#getLastNonConfigurationInstance()} to carry an {@link java.lang.Object} over |
| 52 | to the new instance of your Activity.</p> |
| 53 | <p>This is the recommended technique if you're facing performance issues during the |
| 54 | configuration restart. It allows your Activity to properly restart and reload resources for |
| 55 | the new configuration and also allows you to carry your arbitrary data that may be expensive to |
| 56 | collect again.</p> |
| 57 | </li> |
| 58 | <li><a href="#HandlingTheChange">Handling the Configuration Change Yourself</a> |
| 59 | <p>Declare that your |
| 60 | application will handle certain configuration changes and prevent the system from restarting your |
| 61 | application when such a change occurs. For example, you can declare in your manifest that your |
| 62 | Activity will handle configuration changes to the screen orientation. When the orientation |
| 63 | changes, your Activity will not be restarted and your Activity will receive a call to {@link |
| 64 | android.app.Activity#onConfigurationChanged(Configuration) onConfigurationChanged()} so that you can |
| 65 | perform necessary changes based on the new configuration.</p> |
| 66 | <p>This technique should be considered a last resort and temporary solution, because not all |
| 67 | runtime configuration changes can be handled this way—your application will eventually |
| 68 | encounter a runtime configuration in which you cannot prevent the Activity from being restarted, |
| 69 | whereas the first option will handle all configuration changes.</p> |
| 70 | </li> |
| 71 | </ol> |
| 72 | |
| 73 | <p class="note"><strong>Note:</strong> Your application should always be able to successfully |
| 74 | restart at any time without any loss of user data or state in order to handle other events such as |
| 75 | when the user receives an incoming phone call and then returns to your application (read about the |
| 76 | <a href="{@docRoot}guide/topics/fundamentals.html#lcycles">Activity lifecycle</a>). The following |
| 77 | techniques for handling runtime configuration changes should only be necessary to optimize |
| 78 | performance during specific configuration changes.</p> |
| 79 | |
| 80 | |
| 81 | <h2 id="CarryingAnObject">Carrying an Object During a Configuration Change</h2> |
| 82 | |
| 83 | <p>If your application has acquired significant amounts of data during its life, which would be |
| 84 | costly to recover due to a restart of the Activity, you can use {@link |
| 85 | android.app.Activity#onRetainNonConfigurationInstance()} paired with {@link |
| 86 | android.app.Activity#getLastNonConfigurationInstance()} to pass an {@link java.lang.Object} |
| 87 | to the new Activity instance. The {@link android.app.Activity#onRetainNonConfigurationInstance()} |
| 88 | method is called between {@link android.app.Activity#onStop()} and {@link |
| 89 | android.app.Activity#onDestroy()} when your Activity is being shut down due to a configuration |
| 90 | change. In your implementation of this method, you can return any {@link java.lang.Object} that you |
| 91 | need to efficiently restore your state after the configuration change. When your Activity is |
| 92 | created again, you can call {@link |
| 93 | android.app.Activity#getLastNonConfigurationInstance()} to retrieve the {@link |
| 94 | java.lang.Object}.</p> |
| 95 | |
| 96 | <p>A scenario in which this can be valuable is if your application loads a lot of data from the |
| 97 | web. If the user changes the orientation of the device and the Activity restarts, your application |
| 98 | will need to re-fetch the data, which could be slow. What you can do is implement |
| 99 | {@link android.app.Activity#onRetainNonConfigurationInstance()} to return an object carrying your |
| 100 | data and then retrieve the data when your Activity restarts with {@link |
| 101 | android.app.Activity#getLastNonConfigurationInstance()}. For example:</p> |
| 102 | |
| 103 | <pre> |
| 104 | @Override |
| 105 | public Object onRetainNonConfigurationInstance() { |
| 106 | final MyDataObject data = collectMyLoadedData(); |
| 107 | return data; |
| 108 | } |
| 109 | </pre> |
| 110 | |
| 111 | <p class="caution"><strong>Caution:</strong> While you can return any object, you |
| 112 | should never pass an object that is tied to the {@link android.app.Activity}, such as a {@link |
| 113 | android.graphics.drawable.Drawable}, an {@link android.widget.Adapter}, a {@link android.view.View} |
| 114 | or any other object that's associated with a {@link android.content.Context}. If you do, it will |
| 115 | leak all the Views and resources of the original Activity instance. (To leak the resources |
| 116 | means that your application maintains a hold on them and they cannot be garbage-collected, so |
| 117 | lots of memory can be lost.)</p> |
| 118 | |
| 119 | <p>Then get the {@code data} after the restart:</p> |
| 120 | |
| 121 | <pre> |
| 122 | @Override |
| 123 | public void onCreate(Bundle savedInstanceState) { |
| 124 | super.onCreate(savedInstanceState); |
| 125 | setContentView(R.layout.main); |
| 126 | |
| 127 | final MyDataObject data = (MyDataObject) getLastNonConfigurationInstance(); |
| 128 | if (data == null) { |
| 129 | data = loadMyData(); |
| 130 | } |
| 131 | ... |
| 132 | } |
| 133 | </pre> |
| 134 | |
| 135 | <p>In this case, {@link android.app.Activity#getLastNonConfigurationInstance()} is called to get |
| 136 | the data saved during the configuration change, and if it is null (which will happen if the |
| 137 | Activity is started in any case other than a configuration change) then the data is loaded |
| 138 | from the original source.</p> |
| 139 | |
| 140 | |
| 141 | |
| 142 | |
| 143 | |
| 144 | <h2 id="HandlingTheChange">Handling the Configuration Change Yourself</h2> |
| 145 | |
| 146 | <p>If your application doesn't need to update resources during a specific configuration |
| 147 | change <em>and</em> you have a performance limitation that requires you to |
| 148 | avoid the Activity restart, then you can declare that your Activity handles the configuration change |
| 149 | itself, which will prevent the system from restarting your Activity.</p> |
| 150 | |
| 151 | <p class="note"><strong>Note:</strong> Handling the configuration change yourself can make it much |
| 152 | more difficult to use alternative resources, because the system will not automatically apply them |
| 153 | for you.</p> |
| 154 | |
| 155 | <p>To declare that your Activity handles a configuration change, edit the appropriate <a |
| 156 | href="{@docRoot}guide/topics/manifest/activity-element.html">{@code <activity>}</a> element |
| 157 | in your manifest file to include the <a |
| 158 | href="{@docRoot}guide/topics/manifest/activity-element.html#config">{@code |
| 159 | android:configChanges}</a> attribute with a string value that represents the configuration that you |
| 160 | want to handle. Possible values are listed in the documentation for |
| 161 | the <a href="{@docRoot}guide/topics/manifest/activity-element.html#config">{@code |
| 162 | android:configChanges}</a> attribute (the most commonly used values are {@code orientation} to |
| 163 | handle when the screen orientation changes and {@code keyboardHidden} to handle when the |
| 164 | keyboard availability changes). You can declare multiple configuration values in the attribute |
| 165 | by separating them with a pipe character ("|").</p> |
| 166 | |
| 167 | <p>For example, the following manifest snippet declares an Activity that handles both the |
| 168 | screen orientation change and keyboard availability change:</p> |
| 169 | |
| 170 | <pre> |
| 171 | <activity android:name=".MyActivity" |
| 172 | android:configChanges="orientation|keyboardHidden" |
| 173 | android:label="@string/app_name"> |
| 174 | </pre> |
| 175 | |
| 176 | <p>Now when one of these configurations change, {@code MyActivity} is not restarted. |
| 177 | Instead, the Activity receives a call to {@link |
| 178 | android.app.Activity#onConfigurationChanged(Configuration) onConfigurationChanged()}. This method |
| 179 | is passed a {@link android.content.res.Configuration} object that specifies |
| 180 | the new device configuration. By reading fields in the {@link android.content.res.Configuration}, |
| 181 | you can determine the new configuration and make appropriate changes by updating |
| 182 | the resources used in your interface. At the |
| 183 | time this method is called, your Activity's {@link android.content.res.Resources} object is updated |
| 184 | to return resources based on the new configuration, so you can easily |
| 185 | reset elements of your UI without the system restarting your Activity.</p> |
| 186 | |
| 187 | <p>For example, the following {@link |
| 188 | android.app.Activity#onConfigurationChanged(Configuration) onConfigurationChanged()} implementation |
| 189 | checks the availability of a hardware keyboard and the current device orientation:</p> |
| 190 | |
| 191 | <pre> |
| 192 | @Override |
| 193 | public void onConfigurationChanged(Configuration newConfig) { |
| 194 | super.onConfigurationChanged(newConfig); |
| 195 | |
| 196 | // Checks the orientation of the screen |
| 197 | if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) { |
| 198 | Toast.makeText(this, "landscape", Toast.LENGTH_SHORT).show(); |
| 199 | } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){ |
| 200 | Toast.makeText(this, "portrait", Toast.LENGTH_SHORT).show(); |
| 201 | } |
| 202 | // Checks whether a hardware keyboard is available |
| 203 | if (newConfig.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO) { |
| 204 | Toast.makeText(this, "keyboard visible", Toast.LENGTH_SHORT).show(); |
| 205 | } else if (newConfig.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES) { |
| 206 | Toast.makeText(this, "keyboard hidden", Toast.LENGTH_SHORT).show(); |
| 207 | } |
| 208 | } |
| 209 | </pre> |
| 210 | |
| 211 | <p>The {@link android.content.res.Configuration} object represents all of the current |
| 212 | configurations, not just the ones that have changed. Most of the time, you won't care exactly how |
| 213 | the configuration has changed and can simply re-assign all your resources that provide alternatives |
| 214 | to the configuration that you're handling. For example, because the {@link |
| 215 | android.content.res.Resources} object is now updated, you can reset |
| 216 | any {@link android.widget.ImageView}s with {@link android.widget.ImageView#setImageResource(int)} |
| 217 | and the appropriate resource for the new configuration is used (as described in <a |
| 218 | href="providing-resources.html#AlternateResources">Providing Resources</a>).</p> |
| 219 | |
| 220 | <p>Notice that the values from the {@link |
| 221 | android.content.res.Configuration} fields are integers that are matched to specific constants |
| 222 | from the {@link android.content.res.Configuration} class. For documentation about which constants |
| 223 | to use with each field, refer to the appropriate field in the {@link |
| 224 | android.content.res.Configuration} reference.</p> |
| 225 | |
| 226 | <p class="note"><strong>Remember:</strong> When you declare your Activity to handle a configuration |
| 227 | change, you are responsible for resetting any elements for which you provide alternatives. If you |
| 228 | declare your Activity to handle the orientation change and have images that should change |
| 229 | between landscape and portrait, you must re-assign each resource to each element during {@link |
| 230 | android.app.Activity#onConfigurationChanged(Configuration) onConfigurationChanged()}.</p> |
| 231 | |
| 232 | <p>If you don't need to update your application based on these configuration |
| 233 | changes, you can instead <em>not</em> implement {@link |
| 234 | android.app.Activity#onConfigurationChanged(Configuration) onConfigurationChanged()}. In |
| 235 | which case, all of the resources used before the configuration change are still used |
| 236 | and you've only avoided the restart of your Activity. However, your application should always be |
| 237 | able to shutdown and restart with its previous state intact. Not only because |
| 238 | there are other configuration changes that you cannot prevent from restarting your application but |
| 239 | also in order to handle events such as when the user receives an incoming phone call and then |
| 240 | returns to your application.</p> |
| 241 | |
| 242 | <p>For more about which configuration changes you can handle in your Activity, see the <a |
| 243 | href="{@docRoot}guide/topics/manifest/activity-element.html#config">{@code |
| 244 | android:configChanges}</a> documentation and the {@link android.content.res.Configuration} |
| 245 | class.</p> |