I've made a search screen that has one tab for keywords, filters, and a search button, and three optional tabs for the different types of results (each containing a ListView
with an ArrayAdapter
). When starting the activity, the developer can optionally pass in the results as an extra Parcelable[]
if the search has already been performed. In the onCreate()
method I'm creating each of the three tabs for the Parcelable[]
passed through.
When I call a search from the button on the filter tab, I clear the tabs and recreate them with the new results, which works perfectly. The problem is that when you rotate the device, it appears that Android's automatic orientation switching support recreates the entire activity, calling onCreate()
. This means that my search results are reset to the Parcelable[]
passed through when starting the activity.
The only solution I've had so far is to call finish()
then startActivity()
to essentially restart the activity with the new results. I'm sure there must be a much simpler solution and that I've done something extremely noobish.
Is there a better way to do this?
Of cource there is. Just add configChanges attribute to your AndroidManifest.xml, like that:
<activity android:name=".MyActivity"
android:configChanges="orientation|keyboardHidden" />
Activity restart on rotation Android
How do I disable orientation change on Android?
http://developer.android.com/guide/topics/manifest/activity-element.html#config
What you describe is the default behavior. You have to detect and handle these events yourself by adding:
android:configChanges
to your manifest and then the changes that you want to handle. So for orientation, you would use:
android:configChanges="orientation"
and for the keyboard being opened or closed you would use:
android:configChanges="keyboardHidden"
If you want to handle both you can just separate them with the pipe command like:
android:configChanges="keyboardHidden|orientation"
This will trigger the onConfigurationChanged
method in whatever Activity
you call.
If you override the method you can pass in the new values.
Hope this helps.
One option is using android:configChanges="orientation"
to tell android that you want to handle the configuration changes yourself instead of having it recreate the Activity. However, this is discouraged:
When a configuration change occurs at runtime, the activity is shut
down and restarted by default, but declaring a configuration with this
attribute will prevent the activity from being restarted. Instead, the
activity remains running and its onConfigurationChanged() method is
called. Note: Using this attribute should be avoided and used only as
a last resort. Please read Handling Runtime Changes for more
information about how to properly handle a restart due to a
configuration change.
( Source)
There is a different way to retain an object during a configuration change: The ViewModel can be used to define a wrapper class for your data. Each instance of the Activity seems to have its own set of ViewModels, accessible through
MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
and if you use LiveData
within it, you can subscribe to data changes as outlined in the documentation. Here is their example just in case the link dies at some point:
Architecture Components provides ViewModel helper class for the UI
controller that is responsible for preparing data for the UI.
ViewModel objects are automatically retained during configuration
changes so that data they hold is immediately available to the next
activity or fragment instance. For example, if you need to display a
list of users in your app, make sure to assign responsibility to
acquire and keep the list of users to a ViewModel, instead of an
activity or fragment, as illustrated by the following sample code:
public class MyViewModel extends ViewModel {
private MutableLiveData<List<User>> users;
public LiveData<List<User>> getUsers() {
if (users == null) {
users = new MutableLiveData<List<User>>();
loadUsers();
}
return users;
}
private void loadUsers() {
// Do an asynchronous operation to fetch users.
} } You can then access the list from an activity as follows:
public class MyActivity extends AppCompatActivity {
public void onCreate(Bundle savedInstanceState) {
// Create a ViewModel the first time the system calls an activity's onCreate() method.
// Re-created activities receive the same MyViewModel instance created by the first activity.
MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
model.getUsers().observe(this, users -> {
// update UI
});
} }
If the activity is re-created, it receives the same MyViewModel
instance that was created by the first activity. When the
owner activity is finished, the framework calls the ViewModel
objects's onCleared() method so that it can clean up resources.