Skip to content

Instantly share code, notes, and snippets.

@OrenBochman
Last active September 10, 2018 08:45
Show Gist options
  • Save OrenBochman/60e6464c7a01ada3ab0a40e0e4a76f04 to your computer and use it in GitHub Desktop.
Save OrenBochman/60e6464c7a01ada3ab0a40e0e4a76f04 to your computer and use it in GitHub Desktop.
AsyncTask how-to in Android

AsyncTask

When to use this threading primitive?

  1. for short (say up to 3 seconds) background tasks that would otherwise block the UI thread
  2. thread abstraction
  3. easy implimentation
  4. easy ui communication

Caveats:

  1. not lifecycle aware
  2. multiple calls and internals are run sequentiall all on the same thread.
  3. do all work in the doInBackground() methods
  4. is not lifecycle aware
  5. easy to leak resources (say if we define it as an non static inner class)
  6. can cause an ANR if accessin UI of GC activity/app - unless we cancell in onDestroy() (see code item 4)
  7. folow the DRY principle
    • reusable AsyncTasks are tricky due to UI coupling. (see code item 3)
    • move code such as networking and parsing into static helper/utility classes (and unit test seperetly).

Resources

//AsyncTask with cancelation and progress update
public class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
private URL[] urls;
public DownloadFilesTask(URL... urls) {
this.urls = urls;
}
//set up the ui
protected void onPreExecute(Long result) {
// swap search button text: search -> cancell
// show progress bar + set it to zero
}
//do the work in a worker thread
protected Long doInBackground(URL... urls) {
this.urls=urls
int count = this.urls.length;
long totalSize = 0;
for (int i = 0; i < count; i++) { // handling multiple input
totalSize += Downloader.downloadFile(urls[i]);
publishProgress((int) ((i / (float) count) * 100));
if (isCancelled()) break; //cancelation support
}
return totalSize;
}
protected void onProgressUpdate(Integer... progress) {
setProgressPercent(progress[0]);
}
//update UI
protected void onPostExecute(Long result) {
//hide progress bar
//set cancel -> to search
//update the list/show dialog
showDialog("Downloaded " + result + " bytes");
}
protected void onCancelled(Long result) {
showDialog("Downloaded was cancelled after " + result + " bytes");
}
}
DownloadFilesTask task = new DownloadFilesTask();
task.execute(URL1, URL2, URL3);
or
DownloadFilesTask task = new DownloadFilesTask(URL1, URL2, URL3);
task.execute();
/**
* How to pass a reference to a static Asyncask in its constructor
*/
private static class MyAsyncTask extends AsyncTask{
TextView resultTV;
public MyAsyncTask(TextView resultTV) {
this.resultTV = resultTV;
}
@Override
protected void onPreExecute() {
resultTV.setBackgroundColor(Color.GRAY);
//…
}
public class MainActivity extends AppCompatActivity {
AsyncTask asyncTask;
protected void onCreate(...) {
//…
asyncTask = new MyAsyncTask();
asyncTask.execute();
}
//p.s. this is not always run
protected void onDestroy(...) {
//…
asyncTask.cancel();
asyncTask = null;
}
}
/**
* This demonstrates how to test AsyncTasks in android JUnit. Below I used
* an in line implementation of a asyncTask, but in real life you would want
* to replace that with some task in your application.
* @throws Throwable
*/
public void testSomeAsynTask () throws Throwable {
// create a signal to let us know when our task is done.
final CountDownLatch signal = new CountDownLatch(1);
/* Just create an in line implementation of an asynctask. Note this
* would normally not be done, and is just here for completeness.
* You would just use the task you want to unit test in your project.
*/
final AsyncTask<String, Void, String> myTask = new AsyncTask<String, Void, String>() {
@Override
protected String doInBackground(String... arg0) {
//Do something meaningful.
return "something happened!";
}
@Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
/* This is the key, normally you would use some type of listener
* to notify your activity that the async call was finished.
*
* In your test method you would subscribe to that and signal
* from there instead.
*/
signal.countDown();
}
};
// Execute the async task on the UI thread! THIS IS KEY!
runTestOnUiThread(new Runnable() {
@Override
public void run() {
myTask.execute("Do something");
}
});
/* The testing thread will wait here until the UI thread releases it
* above with the countDown() or 30 seconds passes and it times out.
*/
signal.await(30, TimeUnit.SECONDS);
// The task is done, and now you can assert some things!
assertTrue("Happiness", true);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment