Replaced use of deprecated methods with retained fragments. Change-Id: I0f0a045da88f9817077070729ef0af283bea93e4 Review: http://quixote.mtv.corp.google.com:8007/guide/topics/resources/runtime-changes.html
291 lines
15 KiB
Plaintext
291 lines
15 KiB
Plaintext
page.title=Handling Runtime Changes
|
|
page.tags="activity","lifecycle"
|
|
@jd:body
|
|
|
|
<div id="qv-wrapper">
|
|
<div id="qv">
|
|
|
|
<h2>In this document</h2>
|
|
<ol>
|
|
<li><a href="#RetainingAnObject">Retaining an Object During a Configuration Change</a></li>
|
|
<li><a href="#HandlingTheChange">Handling the Configuration Change Yourself</a>
|
|
</ol>
|
|
|
|
<h2>See also</h2>
|
|
<ol>
|
|
<li><a href="providing-resources.html">Providing Resources</a></li>
|
|
<li><a href="accessing-resources.html">Accessing Resources</a></li>
|
|
<li><a href="http://android-developers.blogspot.com/2009/02/faster-screen-orientation-change.html">Faster
|
|
Screen Orientation Change</a></li>
|
|
</ol>
|
|
</div>
|
|
</div>
|
|
|
|
<p>Some device configurations can change during runtime
|
|
(such as screen orientation, keyboard availability, and language). When such a change occurs,
|
|
Android restarts the running
|
|
{@link android.app.Activity} ({@link android.app.Activity#onDestroy()} is called, followed by {@link
|
|
android.app.Activity#onCreate(Bundle) onCreate()}). The restart behavior is designed to help your
|
|
application adapt to new configurations by automatically reloading your application with
|
|
alternative resources that match the new device configuration.</p>
|
|
|
|
<p>To properly handle a restart, it is important that your activity restores its previous
|
|
state through the normal <a
|
|
href="{@docRoot}guide/components/activities.html#Lifecycle">Activity
|
|
lifecycle</a>, in which Android calls
|
|
{@link android.app.Activity#onSaveInstanceState(Bundle) onSaveInstanceState()} before it destroys
|
|
your activity so that you can save data about the application state. You can then restore the state
|
|
during {@link android.app.Activity#onCreate(Bundle) onCreate()} or {@link
|
|
android.app.Activity#onRestoreInstanceState(Bundle) onRestoreInstanceState()}.</p>
|
|
|
|
<p>To test that your application restarts itself with the application state intact, you should
|
|
invoke configuration changes (such as changing the screen orientation) while performing various
|
|
tasks in your application. Your application should be able to restart at any time without loss of
|
|
user data or state in order to handle events such as configuration changes or when the user receives
|
|
an incoming phone call and then returns to your application much later after your application
|
|
process may have been destroyed. To learn how you can restore your activity state, read about the <a
|
|
href="{@docRoot}guide/components/activities.html#Lifecycle">Activity lifecycle</a>.</p>
|
|
|
|
<p>However, you might encounter a situation in which restarting your application and
|
|
restoring significant amounts of data can be costly and create a poor user experience. In such a
|
|
situation, you have two other options:</p>
|
|
|
|
<ol type="a">
|
|
<li><a href="#RetainingAnObject">Retain an object during a configuration change</a>
|
|
<p>Allow your activity to restart when a configuration changes, but carry a stateful
|
|
object to the new instance of your activity.</p>
|
|
|
|
</li>
|
|
<li><a href="#HandlingTheChange">Handle the configuration change yourself</a>
|
|
<p>Prevent the system from restarting your activity during certain configuration
|
|
changes, but receive a callback when the configurations do change, so that you can manually update
|
|
your activity as necessary.</p>
|
|
</li>
|
|
</ol>
|
|
|
|
|
|
<h2 id="RetainingAnObject">Retaining an Object During a Configuration Change</h2>
|
|
|
|
<p>If restarting your activity requires that you recover large sets of data, re-establish a network
|
|
connection, or perform other intensive operations, then a full restart due to a configuration change
|
|
might be a slow user experience. Also, it might not be possible for you to completely restore your
|
|
activity state with the {@link android.os.Bundle} that the system saves for you with the {@link
|
|
android.app.Activity#onSaveInstanceState(Bundle) onSaveInstanceState()} callback—it is not
|
|
designed to carry large objects (such as bitmaps) and the data within it must be serialized then
|
|
deserialized, which can consume a lot of memory and make the configuration change slow. In such a
|
|
situation, you can alleviate the burden of reinitializing your activity by retaining a {@link
|
|
android.app.Fragment} when your activity is restarted due to a configuration change. This fragment
|
|
can contain references to stateful objects that you want to retain.</p>
|
|
|
|
<p>When the Android system shuts down your activity due to a configuration change, the fragments
|
|
of your activity that you have marked to retain are not destroyed. You can add such fragments to
|
|
your activity to preserve stateful objects.</p>
|
|
|
|
<p>To retain stateful objects in a fragment during a runtime configuration change:</p>
|
|
|
|
<ol>
|
|
<li>Extend the {@link android.app.Fragment} class and declare references to your stateful
|
|
objects.</li>
|
|
<li>Call {@link android.app.Fragment#setRetainInstance(boolean)} when the fragment is created.
|
|
</li>
|
|
<li>Add the fragment to your activity.</li>
|
|
<li>Use {@link android.app.FragmentManager} to retrieve the fragment when the activity is
|
|
restarted.</li>
|
|
</ol>
|
|
|
|
<p>For example, define your fragment as follows:</p>
|
|
|
|
<pre>
|
|
public class RetainedFragment extends Fragment {
|
|
|
|
// data object we want to retain
|
|
private MyDataObject data;
|
|
|
|
// this method is only called once for this fragment
|
|
@Override
|
|
public void onCreate(Bundle savedInstanceState) {
|
|
super.onCreate(savedInstanceState);
|
|
// retain this fragment
|
|
setRetainInstance(true);
|
|
}
|
|
|
|
public void setData(MyDataObject data) {
|
|
this.data = data;
|
|
}
|
|
|
|
public MyDataObject getData() {
|
|
return data;
|
|
}
|
|
}
|
|
</pre>
|
|
|
|
<p class="caution"><strong>Caution:</strong> While you can store any object, you
|
|
should never pass an object that is tied to the {@link android.app.Activity}, such as a {@link
|
|
android.graphics.drawable.Drawable}, an {@link android.widget.Adapter}, a {@link android.view.View}
|
|
or any other object that's associated with a {@link android.content.Context}. If you do, it will
|
|
leak all the views and resources of the original activity instance. (Leaking resources
|
|
means that your application maintains a hold on them and they cannot be garbage-collected, so
|
|
lots of memory can be lost.)</p>
|
|
|
|
<p>Then use {@link android.app.FragmentManager} to add the fragment to the activity.
|
|
You can obtain the data object from the fragment when the activity starts again during runtime
|
|
configuration changes. For example, define your activity as follows:</p>
|
|
|
|
<pre>
|
|
public class MyActivity extends Activity {
|
|
|
|
private RetainedFragment dataFragment;
|
|
|
|
@Override
|
|
public void onCreate(Bundle savedInstanceState) {
|
|
super.onCreate(savedInstanceState);
|
|
setContentView(R.layout.main);
|
|
|
|
// find the retained fragment on activity restarts
|
|
FragmentManager fm = getFragmentManager();
|
|
dataFragment = (DataFragment) fm.findFragmentByTag(“data”);
|
|
|
|
// create the fragment and data the first time
|
|
if (dataFragment == null) {
|
|
// add the fragment
|
|
dataFragment = new DataFragment();
|
|
fm.beginTransaction().add(dataFragment, “data”).commit();
|
|
// load the data from the web
|
|
dataFragment.setData(loadMyData());
|
|
}
|
|
|
|
// the data is available in dataFragment.getData()
|
|
...
|
|
}
|
|
|
|
@Override
|
|
public void onDestroy() {
|
|
super.onDestroy();
|
|
// store the data in the fragment
|
|
dataFragment.setData(collectMyLoadedData());
|
|
}
|
|
}
|
|
</pre>
|
|
|
|
<p>In this example, {@link android.app.Activity#onCreate(Bundle) onCreate()} adds a fragment
|
|
or restores a reference to it. {@link android.app.Activity#onCreate(Bundle) onCreate()} also
|
|
stores the stateful object inside the fragment instance.
|
|
{@link android.app.Activity#onDestroy() onDestroy()} updates the stateful object inside the
|
|
retained fragment instance.</p>
|
|
|
|
|
|
|
|
|
|
|
|
<h2 id="HandlingTheChange">Handling the Configuration Change Yourself</h2>
|
|
|
|
<p>If your application doesn't need to update resources during a specific configuration
|
|
change <em>and</em> you have a performance limitation that requires you to
|
|
avoid the activity restart, then you can declare that your activity handles the configuration change
|
|
itself, which prevents the system from restarting your activity.</p>
|
|
|
|
<p class="note"><strong>Note:</strong> Handling the configuration change yourself can make it much
|
|
more difficult to use alternative resources, because the system does not automatically apply them
|
|
for you. This technique should be considered a last resort when you must avoid restarts due to a
|
|
configuration change and is not recommended for most applications.</p>
|
|
|
|
<p>To declare that your activity handles a configuration change, edit the appropriate <a
|
|
href="{@docRoot}guide/topics/manifest/activity-element.html">{@code <activity>}</a> element in
|
|
your manifest file to include the <a
|
|
href="{@docRoot}guide/topics/manifest/activity-element.html#config">{@code
|
|
android:configChanges}</a> attribute with a value that represents the configuration you want to
|
|
handle. Possible values are listed in the documentation for the <a
|
|
href="{@docRoot}guide/topics/manifest/activity-element.html#config">{@code
|
|
android:configChanges}</a> attribute (the most commonly used values are {@code "orientation"} to
|
|
prevent restarts when the screen orientation changes and {@code "keyboardHidden"} to prevent
|
|
restarts when the keyboard availability changes). You can declare multiple configuration values in
|
|
the attribute by separating them with a pipe {@code |} character.</p>
|
|
|
|
<p>For example, the following manifest code declares an activity that handles both the
|
|
screen orientation change and keyboard availability change:</p>
|
|
|
|
<pre>
|
|
<activity android:name=".MyActivity"
|
|
android:configChanges="orientation|keyboardHidden"
|
|
android:label="@string/app_name">
|
|
</pre>
|
|
|
|
<p>Now, when one of these configurations change, {@code MyActivity} does not restart.
|
|
Instead, the {@code MyActivity} receives a call to {@link
|
|
android.app.Activity#onConfigurationChanged(Configuration) onConfigurationChanged()}. This method
|
|
is passed a {@link android.content.res.Configuration} object that specifies
|
|
the new device configuration. By reading fields in the {@link android.content.res.Configuration},
|
|
you can determine the new configuration and make appropriate changes by updating
|
|
the resources used in your interface. At the
|
|
time this method is called, your activity's {@link android.content.res.Resources} object is updated
|
|
to return resources based on the new configuration, so you can easily
|
|
reset elements of your UI without the system restarting your activity.</p>
|
|
|
|
<p class="caution"><strong>Caution:</strong> Beginning with Android 3.2 (API level 13), <strong>the
|
|
"screen size" also changes</strong> when the device switches between portrait and landscape
|
|
orientation. Thus, if you want to prevent runtime restarts due to orientation change when developing
|
|
for API level 13 or higher (as declared by the <a
|
|
href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code minSdkVersion}</a> and <a
|
|
href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code targetSdkVersion}</a>
|
|
attributes), you must include the {@code "screenSize"} value in addition to the {@code
|
|
"orientation"} value. That is, you must decalare {@code
|
|
android:configChanges="orientation|screenSize"}. However, if your application targets API level
|
|
12 or lower, then your activity always handles this configuration change itself (this configuration
|
|
change does not restart your activity, even when running on an Android 3.2 or higher device).</p>
|
|
|
|
<p>For example, the following {@link
|
|
android.app.Activity#onConfigurationChanged(Configuration) onConfigurationChanged()} implementation
|
|
checks the current device orientation:</p>
|
|
|
|
<pre>
|
|
@Override
|
|
public void onConfigurationChanged(Configuration newConfig) {
|
|
super.onConfigurationChanged(newConfig);
|
|
|
|
// Checks the orientation of the screen
|
|
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
|
|
Toast.makeText(this, "landscape", Toast.LENGTH_SHORT).show();
|
|
} else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
|
|
Toast.makeText(this, "portrait", Toast.LENGTH_SHORT).show();
|
|
}
|
|
}
|
|
</pre>
|
|
|
|
<p>The {@link android.content.res.Configuration} object represents all of the current
|
|
configurations, not just the ones that have changed. Most of the time, you won't care exactly how
|
|
the configuration has changed and can simply re-assign all your resources that provide alternatives
|
|
to the configuration that you're handling. For example, because the {@link
|
|
android.content.res.Resources} object is now updated, you can reset
|
|
any {@link android.widget.ImageView}s with {@link android.widget.ImageView#setImageResource(int)
|
|
setImageResource()}
|
|
and the appropriate resource for the new configuration is used (as described in <a
|
|
href="providing-resources.html#AlternateResources">Providing Resources</a>).</p>
|
|
|
|
<p>Notice that the values from the {@link
|
|
android.content.res.Configuration} fields are integers that are matched to specific constants
|
|
from the {@link android.content.res.Configuration} class. For documentation about which constants
|
|
to use with each field, refer to the appropriate field in the {@link
|
|
android.content.res.Configuration} reference.</p>
|
|
|
|
<p class="note"><strong>Remember:</strong> When you declare your activity to handle a configuration
|
|
change, you are responsible for resetting any elements for which you provide alternatives. If you
|
|
declare your activity to handle the orientation change and have images that should change
|
|
between landscape and portrait, you must re-assign each resource to each element during {@link
|
|
android.app.Activity#onConfigurationChanged(Configuration) onConfigurationChanged()}.</p>
|
|
|
|
<p>If you don't need to update your application based on these configuration
|
|
changes, you can instead <em>not</em> implement {@link
|
|
android.app.Activity#onConfigurationChanged(Configuration) onConfigurationChanged()}. In
|
|
which case, all of the resources used before the configuration change are still used
|
|
and you've only avoided the restart of your activity. However, your application should always be
|
|
able to shutdown and restart with its previous state intact, so you should not consider this
|
|
technique an escape from retaining your state during normal activity lifecycle. Not only because
|
|
there are other configuration changes that you cannot prevent from restarting your application, but
|
|
also because you should handle events such as when the user leaves your application and it gets
|
|
destroyed before the user returns to it.</p>
|
|
|
|
<p>For more about which configuration changes you can handle in your activity, see the <a
|
|
href="{@docRoot}guide/topics/manifest/activity-element.html#config">{@code
|
|
android:configChanges}</a> documentation and the {@link android.content.res.Configuration}
|
|
class.</p>
|