android10

  • Increase font size
  • Default font size
  • Decrease font size

Android Application and AsyncTask basics

E-mail Print
( 6 Votes )
Share

Introduction

Programming on Android is pretty easy. At least it is at first, then the subtleties creep in -- like maintaining state, long running tasks, managing orientation changes, and so on. When you get to that stuff programming Android gets, well, not hard, but let's say more complicated.


AsyncTask Basics

The Android APIs are generally very nice, and there are ways to deal with just about any situation, it's just that they aren't always obvious. In this tutorial we will apply the knowledge we learnt in the article Creating and Using Databases in Android. Here we are going to fill out more details, including adding an android.app.Application object to stash expensive objects and maintain state, adding a few more data operations, and putting said data operations onto a background Thread using AsyncTask (plus demonstrating saving instance state when the screen orientation is changed, and restoring same).

The complete application, which is intended to be very simple, will look like the screen shot below:

android_application_asynctask_01

As you can see this example allows us to enter data, then press "Save," and that data is stored in a database. Also, this example shows the current data from the database, and allows us to clear/delete it.

Again, this is a simplified example that is intended to give you an idea of what the possibilities and related problems with long running tasks are, and an overview of how to use the Application object. For more complete information, as always, see the Android docs (specifically for AsyncTask management stuff, and you might also want to check out Droid-Fu which is an excellent little utility library that helps, and goes much farther than this example, with AsyncTask management).

Ok, so on with our example. The complete code for this project, in case you just want to jump in that way, you can download it at the end of this article.

The first notable code with this update is the Main Activity, as seen below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
public class Main extends Activity {
 
private static final String NAME = "NAME";
 
private EditText input;
private Button saveButton;
private Button deleteButton;
private TextView output;
 
private MyApplication application;
 
@Override
public void onCreate(final Bundle savedInstanceState) {
Log.d(MyApplication.APP_NAME, "onCreate");
super.onCreate(savedInstanceState);
 
this.setContentView(R.layout.main);
 
// get "Application" object for shared state or creating of expensive resources - like DataHelper
// (this is not recreated as often as each Activity)
this.application = (MyApplication) this.getApplication();
 
// inflate views
this.input = (EditText) this.findViewById(R.id.in_text);
this.saveButton = (Button) this.findViewById(R.id.save_button);
this.deleteButton = (Button) this.findViewById(R.id.del_button);
this.output = (TextView) this.findViewById(R.id.out_text);
 
// initially populate "output" view from database
new SelectDataTask().execute();
 
// save new data to database (when save button is clicked)
this.saveButton.setOnClickListener(new OnClickListener() {
public void onClick(final View v) {
new InsertDataTask().execute(Main.this.input.getText().toString());
Main.this.input.setText("");
}
});
 
// delete all data from database (when delete button is clicked)
this.deleteButton.setOnClickListener(new OnClickListener() {
public void onClick(final View v) {
new DeleteDataTask().execute();
}
});
}
 
@Override
public void onSaveInstanceState(final Bundle b) {
Log.d(MyApplication.APP_NAME, "onSaveInstanceState");
if ((this.input.getText().toString() != null) && (this.input.getText().toString().length() > 0)) {
b.putString(Main.NAME, this.input.getText().toString());
}
super.onSaveInstanceState(b);
}
 
@Override
public void onRestoreInstanceState(final Bundle b) {
Log.d(MyApplication.APP_NAME, "onRestoreInstanceState");
super.onRestoreInstanceState(b);
String name = b.getString(Main.NAME);
if (name != null) {
// use onSaveInstanceState/onRestoreInstance state to manage state when orientation is changed (and whenever restarted)
// put some text in input box, then rotate screen, text should remain
// COMMENT this out, and try again, text won't be there - you need to maintain this state - esp for orientation changes
// (you can rotate the screen in the emulator by pressing 9 on numeric keypad)
this.input.setText(name);
}
}
 
private class InsertDataTask extends AsyncTask<String, Void, Void> {
private final ProgressDialog dialog = new ProgressDialog(Main.this);
 
// can use UI thread here
protected void onPreExecute() {
this.dialog.setMessage("Inserting data...");
this.dialog.show();
}
 
// automatically done on worker thread (separate from UI thread)
protected Void doInBackground(final String... args) {
Main.this.application.getDataHelper().insert(args[0]);
return null;
}
 
// can use UI thread here
protected void onPostExecute(final Void unused) {
if (this.dialog.isShowing()) {
this.dialog.dismiss();
}
// reset the output view by retrieving the new data
// (note, this is a naive example, in the real world it might make sense
// to have a cache of the data and just append to what is already there, or such
// in order to cut down on expensive database operations)
new SelectDataTask().execute();
}
}
 
private class SelectDataTask extends AsyncTask<String, Void, String> {
private final ProgressDialog dialog = new ProgressDialog(Main.this);
 
// can use UI thread here
protected void onPreExecute() {
this.dialog.setMessage("Selecting data...");
this.dialog.show();
}
 
// automatically done on worker thread (separate from UI thread)
protected String doInBackground(final String... args) {
List<String> names = Main.this.application.getDataHelper().selectAll();
StringBuilder sb = new StringBuilder();
sb.append("Names in database:\n");
for (String name : names) {
sb.append(name + "\n");
}
return sb.toString();
}
 
// can use UI thread here
protected void onPostExecute(final String result) {
if (this.dialog.isShowing()) {
this.dialog.dismiss();
}
Main.this.output.setText(result);
}
}
 
private class DeleteDataTask extends AsyncTask<String, Void, Void> {
private final ProgressDialog dialog = new ProgressDialog(Main.this);
 
// can use UI thread here
protected void onPreExecute() {
this.dialog.setMessage("Deleting data...");
this.dialog.show();
}
 
// automatically done on worker thread (separate from UI thread)
protected Void doInBackground(final String... args) {
Main.this.application.getDataHelper().deleteAll();
return null;
}
 
// can use UI thread here
protected void onPostExecute(final Void unused) {
if (this.dialog.isShowing()) {
this.dialog.dismiss();
}
// reset the output view by retrieving the new data
// (note, this is a naive example, in the real world it might make sense
// to have a cache of the data and just append to what is already there, or such
// in order to cut down on expensive database operations)
new SelectDataTask().execute();
}
}
}

In this code we start with a typical onCreate method that inflates views and includes a Button click listener. But, you might also have noticed we are setting up a "MyApplication" object which is an instance of Application. The android.app.Application object is very useful, for many reasons. You can stick state there and share it between activities (you can do this with getters and setters, simple), and you can use it to instantiate objects that are needed by your app in more than one activity and are relatively expensive to create. The context of the Application object is "lives" as long as your application, and is different from the context of any single activity. For our simple example we only have one activity, but we are using the application object to demonstrate what the possibilities are. To use the application object you need to extend it, and you need to add android:name="[ApplicationClassName] to the "application" element of your manifest. Our "MyApplication" object for this example is shown below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class MyApplication extends Application {
 
public static final String APP_NAME = "AndroidExamples";
 
private DataHelper dataHelper;
 
@Override
public void onCreate() {
super.onCreate();
Log.d(APP_NAME, "APPLICATION onCreate");
this.dataHelper = new DataHelper(this);
}
 
@Override
public void onTerminate() {
Log.d(APP_NAME, "APPLICATION onTerminate");
super.onTerminate();
}
 
public DataHelper getDataHelper() {
return this.dataHelper;
}
 
public void setDataHelper(DataHelper dataHelper) {
this.dataHelper = dataHelper;
}
}
 

Real simple. Here we *could* add getters and setters and store state, but basically we are using the longer lifecycle of the application object to create our DataHelper object and make it available to activities. Application let's you do much more than this, but the basics are state and longer life-cycle (see the docs for more info). Even though application is very useful, and very simple, it's often overlooked.

The next significant part of our new Main activity (going back to the previous code snippet above) is that it no longer calls data operations directly. Instead, it uses several inner AsyncTask classes to move these operations OFF of the main UI thread and onto another thread where they can run asynchronously.

This is HUGE. Ok, our sample application doesn't have anything fancy/long running to do, so it's overkill for the purposes of the example here, but in real life these data operations might be a lot more complicated and time consuming. And, you can use AsyncTask for anything, network operations are another common case.

AsyncTask replaces a lot of manual Handler/Thread/wiring that was needed in the original Android SDKs - it's a convenience for you. AsyncTask has an execute method that performs your long running task on a thread SEPARATE from the main UI thread automatically, and has clear pre and post methods where you CAN interact with the main UI thread too. From the example code, where we have 3 AsyncTask instances for our different data operations, the pattern should be clear (another approach might have been to have ONE task here, and to use the execute method arguments to determine the operation -- that would also allow us to manage the task state itself better, but that is getting beyond the scope of this example). One caveat with this example is that we are not properly using onPause (remember I said it was oversimplified). You should always clean up in onPause, and if you use an AsyncTask that shows a ProgressDialog, you'll want to make sure to dismiss it there (if it is showing, else it will leak).

NOTE Overall, the way we are handling the ProgressDialog in the AsyncTasks here is naive. It's better to use an external ProgressDialog and make use of the onProgressUpdate and publishProgress methods. That gives you more control over the dialog, and makes it easier to prevent errors when activities are restarted (orientation changes, for which you still need to use onPause and cleanup).

After the new task code we also have now implemented the onSaveInstanceState and onRestoreInstanceState methods of Activity. These are for instance state only (some call it "transient" state -- that may seem an odd turn of phrase, but this is state only associated with current instance of the activity, not persistent state, for that we are using the database). These allow us to save simple UI state, such as the contents of a text view, or what checkbox is checked, in case the activity is restarted suddenly, in mid course. This wouldn't really be all that important except for the fact that any orientation change does *exactly this* (yes, it restarts your activity and the life-cycle happens again). For this simple example we have only one input field ("input") to worry about, but you can see how this could be applied to any number of elements. And, in fact, this is unnecessary for a TextView because the framework does it for us with it's own onSaveInstanceState method. The trick is that you need to be aware of it and make sure you handle it in cases where it's not automatic (such as your own custom views). If you want to see how this works with a plain TextView, override the onSaveInstanceState method and do nothing, then change the orientation, and any text in the view will be lost.

That does it for this time. Our new AndroidExamples application now has a slightly more useful user interface that allows us to enter and delete data, and displays it all. Additionally we are now using the Application object to create our DataHelper, and our helper performs all of the data management tasks we need (creating the database, and basic CRUD operations). Also, every time we invoke data related methods we are now doing this with AsyncTask instances, so that these operations are not blocking the UI thread. This is still an oversimplified example, of course, and we are really only scratching the surface, but I do hope it might provide some insight as to where you can start to clean up your Android apps and deal with the sticky spots.

Thanks Screaming-Penguin for this article!!!


Source code files
FileDescriptionSDK VersionFile sizeLast Modified
Download this file (AsyncTask.rar)AsyncTask.rarThe source code for this exampleAPI version 454 Kb18/11/10 12:02
Comments (8)
  • Narendar
    avatar

    Do we need to add @override for each methods like doInBackground(), onPreExecute() and so on in AsyncTask implemenation ?

  • penano  - hi
    avatar

    If you can compile without them, remove them... ;)

  • Shaista

    thanks

  • IamStalker  - Thanks nice article!!!

    Coool!

  • arun0107  - its Superab

    its really a very nice article and clearly understandable.

    thanx

  • prabin1987

    Thanks for very useful info

  • prabin1987  - THANKS

    Thanks for useful information

Only registered users can write comments!
 

ANDROID10 --- TIP!!!

android10 tipYou can edit your position easily: just click on your picture when you're logged in, then on the top you will find a menu, go there, edit your profile, go to your position tab and drag the icon in the map. That's all. Just easy!!!
contact android10!!!