December 22, 2014

Rotational Forces, Part Three

First, we showed you how rotation events can be handled just like any other lifecycle occurence, such as when your application is evicted from RAM. Then, we showed you how you can use onRetainNonConfigurationInstance() as an alternative, so you can hang onto more data than can fit into a Bundle, such as an open socket.

Today, we’ll cover yet another alternative for handling rotation events in Android: telling Android to mind its own business, allowing you to handle everything involved with the rotation yourself. Once again, this is an adapted excerpt from Version 1.4 of The Busy Coder’s Guide to Android Development.

Even onRetainNonConfigurationInstance() may be too intrusive to your application. Suppose, for example, you are creating a real-time game, such as a first-person shooter. The “hiccup” your users experience as your activity is destroyed and recreated might be enough to get them shot, which they may not appreciate. While this would be less of an issue on the T-Mobile G1, since a rotation requires sliding open the keyboard and therefore is unlikely to be done mid-game, other devices might rotate based solely upon the device’s position as determined by accelerometers.

The third possibility for handling rotations, therefore, is to tell Android that you will handle them completely yourself and that you do not want assistance from the framework. To do this:

  1. Put an android:configChanges entry in your file, listing the configuration changes you want to handle yourself versus allowing Android to handle for you
  2. Implement onConfigurationChanged() in your Activity, which will be called when one of the configuration changes you listed in android:configChanges occurs

Now, for any configuration change you want, you can bypass the whole activity-destruction process and simply get a callback letting you know of the change.

To see this in action, let us modify the sample application from the previous blog post. The Java code is significantly different, because we are no longer concerned with saving our state, but rather with updating our UI to deal with the layout.

But first, we need to make a small change to our manifest:

[sourcecode lang=’xml’]

apk/res/android"
package="com.commonsware.android.rotation.three"
android:versionCode="1"
android:versionName="1.0.0">

android:label="@string/app_name"
android:configChanges="keyboardHidden|orientation">







[/sourcecode]

Here, we state that we will handle keyboardHidden and orientation configuration changes ourselves. This covers us for any cause of the “rotation” – whether it is a sliding keyboard or a physical rotation. Note that this is set on the activity, not the application – if you have several activities, you will need to decide for each which of the tactics outlined in this chapter you wish to use.

The Java code for this project is shown below:

[sourcecode lang=’java’]
public class RotationThreeDemo extends Activity {
static final int PICK_REQUEST=1337;
Button viewButton=null;
Uri contact=null;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

setupViews();
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode==PICK_REQUEST) {
if (resultCode==RESULT_OK) {
contact=data.getData();
viewButton.setEnabled(true);
}
}
}

public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);

setupViews();
}

private void setupViews() {
setContentView(R.layout.main);

Button btn=(Button)findViewById(R.id.pick);

btn.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
Intent i=new Intent(Intent.ACTION_PICK, Uri.parse(“content://contacts/people”));

startActivityForResult(i, PICK_REQUEST);
}
});

viewButton=(Button)findViewById(R.id.view);

viewButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
startActivity(new Intent(Intent.ACTION_VIEW, contact));
}
});

viewButton.setEnabled(contact!=null);
}
}
[/sourcecode]

The onCreate() implementation delegates most of its logic to a setupViews() method, which loads the layout and sets up the buttons. The reason this logic was broken out into its own method is because it is also called from onConfigurationChanged().

Now when the device orientation changes, our activity is not destroyed. Instead, onConfigurationChanged() is called, and we handle the rotation ourselves. This gives us the utmost in flexibility.



  • http://androidBlogger.blogspot.com Emmanuel

    Hi,

    I was surprised by your post, as I haven’t heard about this technique before.

    What I don’t understand now, is why google hasn’t make this method the default method !
    It seems so more natural to have a function to deal with the device rotation, and let the user recreate the display from this point ( but while keeping your activity alive ), than deleting everything, and starting again from scratch, with only a variable bag to reconstitute the previous state !

    Emmanuel

    • nitin gupta

      i hav done all as u describe but when screen rientation changes it will cost memory again and again on orientation changes and app close due to low memory …….
      plz give me a suitable resolution ASAP..

  • Dianne Hackborn

    We strongly recommend against using this approach except for in special cases as described here. There are good reasons for sticking with the standard approach:

    – It leverages the standard activity lifecycle, so if you write your application correctly you don’t need to do anything special, it will just work.

    – As a corollary to the first, using this for your orientation changes allows you to debug and validate some of the more tricky parts of the activity lifecycle, which can otherwise be a lot harder to test and makes it a lot more obvious as your are developing when there are bugs here.

    – Actually dealing with a configuration change yourself correctly as a general case is -really- hard. Keep in mind that when a configuration change happens, the value of -any- resource may change: from layouts to strings. Going through and reloading all of these resources is a very tricky thing to do, and actually involves pretty much re-initializing most of your activity, so it’s easier to just let the system restart the activity for you.

    For the last point, you may think “oh well I know what resources I am using and so I know none of these strings and layouts will change when the orientation changes.” But what if in the future you or someone else localizes your app to German, and they find they they need to abbreviate a string or tweak a layout for a particular orientation? If you are handling the orientation changes yourself because you know what resources are changing, you probably weren’t expecting this, so now someone has to go and modify the app’s code, possibly in significant ways, in order to localize it.

    So, sure, if you don’t care about any of this stuff, go ahead and handle those changes yourself. But there are a lot of good reasons for having this the default behavior, which specific applications can turn off if they really really want to.

  • http://sankasaurus.blogspot.com/ Peter Sankauskas

    One case where this method works well is when your onCreate() method calls showDialog() to display a loading dialog while an HTTP request is sent. If the rotation changes while this dialog box is displayed, you get the nasty “leaked window” error:

    02-12 23:05:59.237: ERROR/WindowManager(282): Activity com.fun.game.WebCall has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView@433ba040 that was originally added here

    This error happened whether I dismissed the dialog in onStop() or not.

    Thanks for the excellent write up!

  • Steve Hall

    Hi,
    I have a ListView on the main screen displayed in Portrait and want to display a dialog from there (without stopping and restarting) in Landscape so I can use the whole context (open files etc) from the main screen.

    Is there a way I can do this??
    Possible without having the main screen repopulated?

  • Joe

    In my case, I have a WebView that handles rotation, as well as an ImageView with alternate drawable resources (landscape and portrait).

    I want the WebView to do its own thing. I don't want it to restart along with the activity. That would be bad, user-experience wise. :(

    So far I've been unsuccessful in using onRetainNonConfigurationInstance and getLastNonConfigurationInstance. I'm passing the WebView through but it seems to have lost its state (it shows up empty) … although the ImageView does change to the landscape version.

    However, I have been equally unsuccessful watching for config changes (keyboardHidden|orientation), then handling onConfigurationChanged. In this case, the _WebView_ "does the right thing" all on its own … but now the ImageView never updates to the landscape version because I've tripped it up by using android:configChanges!

    API clues welcome/appreciated.

  • Joe

    (Also, I've got all three of your books, Mike! Highly recommended to anyone new to Android development.)

    I think the gotcha just has to do with handling WebViews during rotations gracefully … then I've got this problem solved.

  • Joe

    Mark, I meant Mark! :)

  • Tallbruva

    One of the things to NEVER do is use onRetainNonConfigurationInstance with views as it will leak everything associated with the view when the configuration changes (http://developer.android.com/intl/zh-CN/resources

    I had a similar situation where I called a tabview with 4 tabs, all webviews. And of course, I had to deal with the destruction and recreation of the 5 views. As Dianne Hackborn said, it's easier to let the OS handle things. So what I successfully did was to allow the view to load normally (on the webviews I have a ProgressDialog that loads in the main activity and a separate thread that loads the page).

    After the view does its lifting, this code sits and waits for an event change. It works beautifully and using the debugger, I can see garbage collection happening as expected (a.k.a. no leaking):

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
    // We do nothing here. We're only handling this to keep orientation
    // or keyboard hiding from causing the WebView activity to restart.
    }

    Don't forget to update your activity for the class holding the view in the AndroidManifest with the config changes you expect. Because I'm testing on a G1 I've added the following:

    android:screenOrientation="unspecified" android:configChanges="keyboard|keyboardHidden|orientation|screenLayout"

  • http://twitter.com/jdandrea Joe

    Thanks Tallbruva! This looks _almost_ (but not quite) like what I ended up doing.

    I'm using the override (with the same comment!). That much is identical. However …

    I'm excluding :screenOrientation (vs. forcing it to portrait). I could make that explicit if it's considered a better/best practice, or if leaving it out is simply a no-no.

    I've also set :configChanges to "keyboardHidden|orientation" (probably from the same place I found that override) … which means I've left out "keyboard|screenLayout" in the process. Again, easily adjusted.

    Q: Does adding screenLayout to the change list take care of the ImageView updating to use the landscape resource?

    Reference: http://www.androidguys.com/2008/11/11/rotational-

  • http://www.jfseostudio.com get online sales

    thank for the update

  • Pingback: My Homepage

  • Pingback: edulliset kengät

  • Pingback: perde

  • Pingback: online marketing spain

  • Pingback: Auto Traffic Conspiracy Scam

  • Pingback: Project First Sale Bonus

  • Pingback: κατασκευη e-shop τιμες

  • Pingback: туроператор по израилю

  • Pingback: http://www.youtube.com/watch?v=j80Q9LBKS80

  • Pingback: cheap auto insurance

  • Pingback: ppi calculator

  • Pingback: Related Site

  • Pingback: auto insurance comapnies

  • Pingback: cheap auto insurance quotes

  • Pingback: Top 2013 Jigoshop WordPress Themes

  • Pingback: antimasque adversant acetimeter

  • Pingback: buy car insurance online

  • Pingback: administration benignly control