Popular Posts

Thursday, April 07, 2011

Android: Using startActivityForResult()

Sometimes in Android, we want to be able to start an activity, but then return back to the activity that called it, and send a message (in the form of an integer in this case) along with it. This is what startActivityForResult() allows us to do.

Before I explain how to use it, let me explain the situation that I was trying to achieve. I'll start with the diagram below.


Now, the idea behind this is: an Activity Main starts as soon as the program starts. It doesn't have any layout associated with it. Then, based on a few conditions, it launches one of two Activities with layouts (in this case "Login" or "Details" Activity, but the names are completely irrelevant.) Of course, you want to be able to get to from one screen to the other (e.g. you login, or logout.)

Of course, I could have started with the login screen straight away, and if a condition states that I'm already logged in, launch the details screen straight away without doing anything, but personally I think this way is more versatile. At the moment the other way would work, but this I think gives more control over how your application behaves (though that could indeed be a bad thing.)

So, what did I have in my Main.java? Let's have a look:

private void launchUserInterface()
{
SharedPreferences settings = getSharedPreferences(PREFERENCE_FILENAME, MODE_PRIVATE);
Intent i;
if (settings.getBoolean("LOGGEDIN", false) == false)
       i = new Intent(this,Login.class);      
else
       i = new Intent(this, Display.class);      
startActivityForResult(i, 0);
}

If you're not sure what's going on with the preferences, Google around for a tutorial on preferences. Basically, PREFERENCE_FILENAME is a constant I have defined, and MODE_PRIVATE is a constant that was defined for use with SharedPreferences.

This is pretty straight forward: if the user is logged in, it will proceed to launch Display.class, if not, it will display Login.class to prompt them to login. The above class is called in the onCreate function of Main.

Now, you are hopefully familiar with the way Intents work (if not, you probably should be reading this tutorial,) the interesting thing comes with startActivityForResult(). It's not really that much different to the standard startActivity(), but it allows for the activity thats being launched to set an integer result. So, first you pass your intent, and an integer that identifies the call (not covered in this tutorial, so set to 0).

In your activity (either Login or Display,) you will at some stage want to go back to the main activity. To do this, just do the following:
public void onClick(View arg0) {
setResult(1);
finish();
}

Or something to that effect. You can set your integer result, and then call finish(). finish(), if called within an activity, should automatically kill that activity and return back to the previous one.

In your Main.class, you want something like this to catch the result:

@Override
    protected void onActivityResult(int pRequestCode, int resultCode, Intent data)
{
if (resultCode == -1)
finish();
else
launchUserInterface();     
    }

So, as soon as an activity that was called from Main.class with startActivityForResult() is finished(), this function will be called and you can do whatever you please. For example, every time that an activity finishes, I called launchUserInterface() again, which will re-launch the user interface based on whether you're logged in or not. The idea being, when someone logs in, the activity stores it in the preferences and closes the activity. Main then runs launchUserInterface() which will launch the Display activity instead, since you're now logged in.

Now, there is a downside to using this method: the more control over it you have, the more you need to take into consideration. The problem here is, when you press the "Back" button, you're finishing the Activity. So, if you're in the Login or Display screen and press the back button, you're pretty much doing the same thing as calling finish(). This is bad, because when it get's back to main, it will run the onActivityResult() and re-launch the activity. So pressing back will close and re-launch the activity.

In the above code, you will notice that there's a condition that if resultCode == -1, then it will finish() the current activity rather than launch the user interface again. This is the one place I used the resultCode.

In both Login and Display, I did something like the following:

@Override
public void onBackPressed()
{
// Return to main and close activity.
setResult(-1);
finish();
return ;
}

This takes control of what happens when the back button is pressed. If you override onBackPressed() and didn't put anything in it, then pressing the back button when that activity is present will do nothing. So, what I have done here is fairly straight forward: setResult to -1, so when it gets back to onActivityResult(), it will then proceed to finish() the main activity, instead of re-launching the interface. This is the expected result in this scenario, as it will take you back to the home screen (in most cases.)

So, there's a quick introduction to using onActivityForResult(). There is so much more that can be done with it, for example, using the contact picker. Read more in the Android Developer site to further your understanding of onActivityForResult().

Update: Using this method can create problems later on. I came across it, but did manage to fix it. Check out this Stack Overflow question (and the accepted solution) for what you should do in addition to this.

1 comments:

  1. Excellent tutorial!

    I was roaming around to find a better and better solution for my login-autologin check-register-mainactivity...

    This is one of the best :-) I will use your way.

    Thanks,

    ReplyDelete