If you are programming an Android app and you’re allowing your users to select an image from gallery you will soon discover that as the screen rotates the image disappears!
Gosh! What’s happening?!
Let’s go a step back.
So far you should know that activities have a specific lifecycle. Each activity lives in different stages of its lifecycle during application usage.
The activity instance goes its journey through the following stages when it is created:
created (transient) –> started (transient) –> resumed.
When the users starts leaving the activity/application both temporarily or not, then the activity starts going down the other way:
paused (partially obscured) –> stopped (hidden) –> destroyed
So each time a users starts interacting with another activity of the same app or with another app, the current foreground activity goes to background and goes to the paused or stopped state.
If the user interaction with the app makes clear to Android that the activity is no more needed then the activity is destroyed.
If a phone call arrives or the application is put in the background, then the activity stays in the stopped state waiting to be resumed.
If the activity is not destroyed it keeps being in memory with all of its state retained and it will be fully resumed if needed.
See here for a full graph and explanation: Activity Lifecycle
What happens when it is destroyed?
One More Hidden Scenario – Screen Rotation
2) User interaction with the app leaves room to the possibility that the activity will be used again1) In the first case (e.g.: the users closes the application) the activity instance goes down all the states and is destroyed to free up memory.2) In the second case (e.g.; the user brings another app in foreground) the activity is stopped but retained in memory, ready to be resumed.If the activity lives in the stopped state long enough, there is the possibility that Android decides to destroy it in order to free up memory for foreground apps and activities.
But this is not the full story…read the following note:
Caution: Your activity will be destroyed and recreated each time the user rotates the screen.
source: Recreating an Activity
So what to do then?
The Wrong Answer
When destroying activities for its own purposes, Android preserves the activity status in a key-value pair blob dictionary (Bundle).
All the activity done by the user interacting with the Views in the layout is preserved in this activity state dictionary.
This means the text entered into an EditText for instance.
If you have other application specific state stored in private members of the Activity you need to add specific code to store it in the Bundle and to retrieve it from there when activity is recreated.
The events exposed to add our code are:
onSaveInstanceState and on RestoreInstanceState
You can add your persistence logic in the first one by adding key value pairs to the bundle.
You can then retrieve your values in the second event or in the onCreate event as well.
You can find detailed explanation and example here: Persist Activity State
This is the usual answer you will find on forums but it’s not applicable for Bitmaps.
The Right Answer
We tell it with the following line:
setRetainInstance(true);
The Fragment does not need to have an associated Layout.
Create the Fragment:
import android.graphics.Bitmap; import android.os.Bundle; import android.support.v4.app.Fragment; public class ImageRetainingFragment extends Fragment { private Bitmap selectedImage; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // retain this fragment setRetainInstance(true); } public void setImage(Bitmap selectedImage) { this.selectedImage = selectedImage; } public Bitmap getImage() { return this.selectedImage; } }
Add the Fragment to the Activity:
private static final String FRAGMENT_NAME = "imageFragment"; ... protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); .... initializeImageRetainingFragment(); tryLoadImage(); } private void initializeImageRetainingFragment() { // find the retained fragment on activity restarts FragmentManager fragmentManager = getSupportFragmentManager(); this.imageRetainingFragment = (ImageRetainingFragment) fragmentManager.findFragmentByTag(FRAGMENT_NAME); // create the fragment and bitmap the first time if (this.imageRetainingFragment == null) { this.imageRetainingFragment = new ImageRetainingFragment(); fragmentManager.beginTransaction() // Add a fragment to the activity state. .add(this.imageRetainingFragment, FRAGMENT_NAME) .commit(); } } private void tryLoadImage() { if (this.imageRetainingFragment == null) { return; } Bitmap selectedImage = this.imageRetainingFragment.getImage(); if (selectedImage == null) { return; } ImageView selectedImageView = (ImageView) findViewById(R.id.selectedImage); selectedImageView.setImageBitmap(selectedImage); }