Showing posts with label how to handle screen orientation changes. Show all posts
Showing posts with label how to handle screen orientation changes. Show all posts

Monday, March 28, 2011

How to handle Screen Orientation changes in Android - Part - 2

Adapting the GUI when screen orientation changes is extremely important for a good user experience experience.  We saw in my previous post, how Android framework does an excellent job at adapting the GUI when screen orientation changes.

Android *destroys* the current activity and *re-creates* the same activity all over again.  

We also saw that in certain situations this architectural choice could lead to bad user experience.

For example

In the above example think about the user experience when, he/she changes the screen orientation while viewing SomeActivity
  • User has waited 5 seconds for the activity to load completely
  • He/She changes the screen orientation
  • Current activity is destroyed
  • New activity is re-created
  • User has to wait for 5 more seconds for the activity to be loaded completely with the new screen orientation.
While the default behavior of destroying and recreating the activity is powerful, its sometimes confusing for new Android developers, who wonder why their activity is destroyed and recreated.

Moreover, if we do not handle this correctly, user experience goes for a toss.  He has to wait 5 extra seconds for no apparent reason.

Because of this, some developers decide to handle the configuration changes (like screen orientation changes) themselves.  This is done by adding the "configChanges" attribute to the Activity declaration in AndroidManifest.xml

The "configChanges" attribute tells Android Platform that, some of the config changes will be handled by the activity by themselves. Platform does not need to do any special handling for these.

The above code tells Android platform that Screen orientation changes will be handled by the activity on its own. In this case Android platform will not destroy the current activity and re-create it.

In general, this is a short-term solution that will only complicate developers lives later.

The Android Platforms automatic resource handling is a very efficient and easy way to adapt an application's user interface to various devices and devices configurations. Hence, its *not advisable* to use the above config value for handling screen orientation!

So what is the alternate?

How do they do it?

Android platform provides two elegant ways to handle this situation.  Lets look at them

Method #1:

The Activity class has a special method called onRetainNonConfigurationInstance()

This method can be used to pass any arbitrary object to your *future self*.  

What does that sentence mean?  

As the name suggests, we can retain an instance of a non configuration object using this method.  The return type of onRetainNonConfigurationInstance method is Object.  The value that you return from this method gets passed to the new activity instance that will be created because of the screen orientation change.

Android Platform is smart enough to call this method only when needed.  For example, in our case we would return an instance of SomeExpensiveObject class that was created earlier.  This way the new instance of SomeActivity will have access to an instance of SomeExpensiveObject automatically and it does not need to create the instance again.

Code is better than 1000 words, lets look at how this is done.

Question arises, how do we get hold of SomeExpensiveObject instance in onCreate method of the new instance of SomeActivity?

To get access to the instance of non configuration object that we saved earlier, we have to invoke the  getLastNonConfigurationInstance() method.  In our example, to get the instance of SomeExpensiveObject class which we retained earlier, we have to invoke the getLastNonConfigurationInstance method. Lets look at how the updated code would look like.

Have a look at the updated code. We first try to get the instance of SomeExpensiveObject using the getLastNonConfigurationInstance() method. If that instance is null then only we create a new instance of SomeExpensiveObject class.


Think of the user experience when, he/she changes the screen orientation while viewing the SomeActivity with updated code.
  • User has waited 5 seconds for the activity to load
  • He/She changes the screen orientation
  • Android platform calls onRetainNonConfigurationInstance()
  • Current activity is destroyed
  • New activity is recreated
  • We try to get the instance of SomeExpensiveObject using the getLastNonConfigurationInstance()
  • Since we will find the instance of SomeExpensiveObject we do not create another instance and hence user does not need to wait for 5 more seconds!
  • End result, user is happy.  Everyone loves to have happy users!


Disclaimer: 

Be very careful with the object you pass through onRetainNonConfigurationChange(), though. 

If the object you pass is for some reason tied to the Activity or Context, you will leak all the views and resources of the activity. This means you should never pass a View, a Drawable, an Adapter, etc.  Finally, remember that onRetainNonConfigurationChange() should be used only to retain data that is expensive to load. Otherwise, keep it simple and let Android do everything.

Method #2:

Android provides another elegant way of achieving this.  To achieve this, we have to override a method called onSaveInstanceState().  Android platform allows the users to save any instance state.  Instance state can be saved in the Bundle.  Bundle is passed as argument to the onSaveInstanceState method.

Android calls onSaveInstanceState before pausing/destroying the activity.

This method is called before an activity may be killed so that when it comes back some time in the future it can restore its state.  This gives us an opportunity to save any instance state we want in the Bundle.

In our case we are putting the SomeExpensiveObject instance in the Bundle.  For this to work we have to mark SomeExpensiveObject as Serializable.  We can also put primitives in the Bundle.

When Android recreates the activity we can get back the saved instance state as follows

As we can see, we can load the saved instance state from the Bundle passed as argument to the onCreate method. We can also load the saved instance state in "onRestoreInstanceState" method. But I will leave that for the readers to figure out.

We are now creating the instance of SomeExpensiveObject only if we are not able to get the instance from the saved state.

Think of the user experience when, he/she changes the screen orientation while viewing the SomeActivity with updated code.
  • User has waited 5 seconds for the activity to load
  • He/She changes the screen orientation
  • Android platform calls onSaveInstanceState()
  • Current activity is destroyed
  • New activity is recreated
  • We try to get the instance of SomeExpensiveObject using the Bundle argument passed to the onCreate method
  • Since we will find the instance of SomeExpensiveObject, we do not create another instance and hence user does not need to wait for 5 more seconds!
  • End result.  Users are happy!
Both these methods are the recommended ways of saving the instance state and getting it back.  Which one to use, is a choice left to the developers to decide.

Android is really a very powerful mobile development platform, but as Spider-Man's uncle Ben once said

With great power comes great responsibilities!

Use the Android platform in the correct way and you will enjoy your time with it!

Saturday, March 26, 2011

How to handle Screen Orientation changes in Android - Part - 1

In one of my Android projects, we had a requirement that, when user changes the orientation of his phone from portrait to landscape or visa-a-versa, the GUI should adapt itself and render appropriately.

Android, is an awesome mobile development platform.  I have worked on iOS, Brew, Symbian and Android platforms but I can tell you one thing, the kind of productivity you get while developing apps for Android platform is higher than any other mobile platform.

Android does a lot of things automatically for you.  One of them is screen orientation change.  Android platform detects that the screen orientation has changed and adapts the GUI accordingly.  How does Android platform achieve this?

What happens when Android detects that the screen orientation has changed?

Android *destroys* the current activity and *recreates* the same activity all over again.  

Yes, you read it right!  And I will say it again, 

It *destroys* the current activity and *recreates* the same activity all over again.

Why does it do that?

Normally, in the portrait mode the screen has more hight than the width.  In landscape mode the scene is reversed.  The screen has more width than the height.  This means that if we go by the default behavior, user might see lot of blank spaces on the right side in the landscape mode.

Screen in Portrait mode
Screen in landscape mode

Have a look at the images of an activity in Portrait and Landscape mode.  In Portrait mode the screen looks decent, but in landscape mode the screen looks ugly. There is too much gap between the logo image on the left corner and "some screen" button on the right hand corner.  Moreover, the text box with the hint "Enter Zip or City, State" looks is way to long in the landscape mode.

Android has provided us a way to get around this problem.  We as developers can provide different layouts for landscape and portrait modes.

The layouts that should be used for landscape mode should be placed in a directory called layout-land


Landscape layout XML

Lets say we updated the layout for the landscape mode so that the icon, button, text box and the Search button appear all in one line.  The updated screen would look like

Updated screen in landscape layout
As you can see in the updated screenshot, the layout in the landscape mode looks much better now.  

Using different layouts for landscape and portrait mode enables us to effectively use the space.

Because of this reason, when screen orientation changes, Android destroys the current activity and recreates the activity all over again by choosing the correct layouts and configuration.  

This behavior lets you declare resources like layout's and drawable's based on the orientation, screen size, locale, etc.

This architectural choice, helps Android adapt the GUI easily and effectively when screen orientation changes. 

Think over this approach for a moment, do you think there are any problems with this approach?

What are the problems with this approach?
What if your activity performs some expensive operation when activity is created?
For example

As show in the above code when SomeActivity is created we creating an instance of SomeExpensiveObject class.  To indicate that creating the instance of this class is expensive, I have added a sleep of 5 seconds in the constructor of SomeExpensiveObject class.

Think about the user experience, when user changes screen orientation while viewing SomeActivity.
  • User has waited 5 seconds for the activity to load completely
  • He/She changes the screen orientation
  • Current activity is destroyed
  • New activity is re-created
  • User has to wait for 5 more seconds for the activity to be loaded completely with the new screen orientation.
How do we solve this problem?

In the next post I will show how effectively we can handle this problem.  Android provides not just one but two elegant solutions to the above problem.  All that and more in the next post.  Stay tuned! 
Have some Fun!