When Androids Dream
In Android, anything you do that takes longer than the proverbial two shakes of a lamb's tail* should be shunted off to an aync task, away from the UI
thread.
* Electric sheep, electronic lambs, what's
the diff?
This tip shows how to do that in step-by-step fashion; so, with aplomb but sans further ado, here they are:
0) First, add a class within the Activity that will use the async task like so:
private class SomeNameTask extends AsyncTask<String, String, String> {
}
So what you have is a class within a class, such as:
public class SongWriterActivity extends ActionBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_random_songwriter);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.random_songwriter, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
private class GetRandomSongwriterTask extends AsyncTask<String, String, String> {
}
}
As you can see, the class we just added ("GetRandomSongwriterTask
" above) extends AsyncTask
, and is embedded within the Activity
class (which extends "ActionBarActivity
").
As to naming your AsyncTask-extending method, appending "Task" is not necessary, but it reminds you/makes it obvious to any who may peruse
your code, just what sort of class this is at a glance.
Flesh It Out
1) In Droidio (Android Studio - you are using Droidio,
aren't you (all the cool kids (and geezers) do - Eclipse has been eclipsed)), right-click AsyncTask and select "Generate... > Implement Methods..."
You will see a dialog like this:
Mash the "OK" button; this will add an empty doInBackground()
method, which will automatically allow you to write async code (code that runs in the
background/separate from the UI thread). Your class will now look like this:
private class GetRandomSongwriterTask extends AsyncTask<String, String, String> {
@Override
protected String doInBackground(String... strings) {
return null;
}
}
Prepare to Show the Result
2) If you want to do something with the return value of the background operation, add an onPostExecute()
method, like so:
@Override
protected void onPostExecute(String result) {
}
Your embedded AsyncTask
class will now look like this:
private class GetRandomSongwriterTask extends AsyncTask<String, String, String> {
@Override
protected String doInBackground(String... strings) {
return null;
}
@Override
protected void onPostExecute(String result) {
}
}
Send Me the Compiler That You Dream On
This example will show grabbing a random bit of data and displaying it. The onPostExecute()
rather amazingly allows you access to the UI thread.
Before we get into the actual code for the two methods in that class, though, let's add some necessary "glue" code so that your class can be
instantiated from the "outside world" (the UI).
3) Add a variable of the class at the top of your activity (prior to the onCreate()
method, or any other method), like so:
private GetRandomSongwriterTask _GetRandomSongwriterTask;
In context:
public class SongWriterActivity extends ActionBarActivity {
private GetRandomSongwriterTask _GetRandomSongwriterTask;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_random_songwriter);
. . .
Note: If you need to step back and first create an Activity/Layout pair, see my previous tip on doing that here.
4) Now add a button to the related layout (xml) file. To do that, first select your .xml file (named something like activity_random_songwriter.xml, and located beneath \src\main\java\res\layout)
5) Now select the Design tab, which will show you your device. Drag a Button from the Widgets section of the palette, placing it where you want on the device. Once you have dragged it on to the device surface, select the Text tab below, and you can change the text of the button, either directly or (the preferred method) by adding a value to strings.xml (found within the values folder) and then referencing it like so in your .xml file:
android:text="@string/get_random_songwriter"
This assumes you have an entry in strings.xml like this:
<string name="get_random_songwriter">Get a random Songwriter</string>
Also, give the button a differentiating id, such as "
getRandomSongwriterButton", like so:
android:id="@+id/getRandomSongwriterButton"
It may look something like this on the design surface (if you had previously added an unrelated TextView widget with "Invoice #" as its text, that is):
6) Now go back to your Activity file, and get a reference to that button and code up an onClick handler for it within the onCreate()
method:
Button getRandSwBtn = (Button) findViewById(R.id.getRandomSongwriterButton);
getRandSwBtn.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
}
});
In context:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_random_songwriter);
Button getRandSwBtn = (Button) findViewById(R.id.getRandomSongwriterButton);
getRandSwBtn.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
}
});
}
Now we'll flesh out the code in the button's onClick
event.
7) Instantiate the AsyncTask
class like so:
_GetRandomSongwriterTask = new GetRandomSongwriterTask();
_GetRandomSongwriterTask.execute();
In context:
Button getRandSwBtn = (Button) findViewById(R.id.getRandomSongwriterButton);
getRandSwBtn.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
_GetRandomSongwriterTask = new GetRandomSongwriterTask();
_GetRandomSongwriterTask.execute();
}
});
We've kind of gotten ahead of ourselves here, though, because we're calling the GetRandomSongwriterTask's doInBackground()
method (albeit indirectly, simply by instantiating the class and calling execute()
) without that method actually doing anything.
We will remedy that now.
8) Add the following code to GetRandomSongwriterTask's doInBackground()
method so that it looks like this:
@Override
protected String doInBackground(String... strings) {
String songSlinger = "";
List<String> songwriterNames = new ArrayList<String>();
songwriterNames.add("J.S. Bach");
songwriterNames.add("Jackson Browne");
songwriterNames.add("Bob Dylan");
songwriterNames.add("Woody Guthrie");
songwriterNames.add("Merle Haggard");
songwriterNames.add("Justin Hayward");
songwriterNames.add("Chris Hillman");
songwriterNames.add("Louis Jordan");
songwriterNames.add("John Mellencamp");
songwriterNames.add("Bruce Springsteen");
songwriterNames.add("Merle Travis");
songwriterNames.add("Dwight Yoakam");
Random generator = new Random();
int i = generator.nextInt(11);
songSlinger = songwriterNames.get(i);
return songSlinger;
}
But how will we know which songwriter was (randomly) chosen? We could write it out using LogCat, but we want it to be part of the UI, so this is where the optional onPostExecute()
method of the AsyncTask comes in handy.
9) First, though, drag a label to hold the returned value onto the design surface; give it a name such as SongWriterLabel
:
android:id="@+id/textViewSongwriter"
10) Now, add code like this to GetRandomSongwriterTask's onPostExecute()
method:
TextView txtVuSongwriter = (TextView) findViewById(R.id.textViewSongwriter);
txtVuSongwriter.setText(result);
And, voila! - here's what it looks like after the button is mashed (it would be "tapped" if this was a real device instead of an emulator) - the name of the masterful and charismatic leader of the Tympani 5 is displayed:
Of course, that was just "the luck of the draw" - it could have been any of those other cats just as easily (YMMV).
Dream a Little Dream of Your Own
The probability that you will want to generate a random songwriter name in your async task is exceedingly miniscule, approaching negative infinity.
This was just shown as a simple example of how code can be added to an async task and its result can be displayed. Now replace it with something more
useful (unless your users are very easily amused, and want to see the same names over and over again).