The accepted answer has some significant downsides. It is not advisable to use AsyncTask for networking unless you really know what you are doing. Some of the down-sides include:
- AsyncTask's created as non-static inner classes have an implicit reference to the enclosing Activity object, its context, and the entire View hierarchy created by that activity. This reference prevents the Activity from being garbage collected until the AsyncTask's background work completes. If the user's connection is slow, and/or the download is large, these short-term memory leaks can become a problem - for example, if the orientation changes several times (and you don't cancel the executing tasks), or the user navigates away from the Activity.
- AsyncTask has different execution characteristics depending on the platform it executes on: prior to API level 4 AsyncTasks execute serially on a single background thread; from API level 4 through API level 10, AsyncTasks execute on a pool of up to 128 threads; from API level 11 onwards AsyncTask executes serially on a single background thread (unless you use the overloaded
executeOnExecutor
method and supply an alternative executor). Code that works fine when running serially on ICS may break when executed concurrently on Gingerbread, say if you have inadvertent order-of-execution dependencies.
If you want to avoid short-term memory leaks, have well-defined execution characteristics across all platforms, and have a base to build really robust network handling, you might want to consider:
- Using a library that does a nice job of this for you - there's a nice comparison of networking libs in this question, or
- Using a
Service
or IntentService
instead, perhaps with a PendingIntent
to return the result via the Activity's onActivityResult
method.
IntentService approach
Downsides:
- More code and complexity than
AsyncTask
, though not as much as you might think - Will queue requests and run them on a single background thread. You can easily control this by replacing
IntentService
with an equivalent Service
implementation, perhaps like this one. - Um, I can't think of any others right now actually
Upsides:
- Avoids the short-term memory leak problem
- If your activity restarts while network operations are in-flight it can still receive the result of the download via its
onActivityResult
method - A better platform than AsyncTask to build and reuse robust networking code. Example: if you need to do an important upload, you could do it from
AsyncTask
in an Activity
, but if the user context-switches out of the app to take a phone call, the system may kill the app before the upload completes. It is less likely to kill an application with an active Service
. - If you use your own concurrent version of
IntentService
(like the one I linked above) you can control the level of concurrency via the Executor
.
Implementation summary
You can implement an IntentService
to perform downloads on a single background thread quite easily.
Step 1: Create an IntentService
to perform the download. You can tell it what to download via Intent
extras, and pass it a PendingIntent
to use to return the result to the Activity
:
import android.app.IntentService; import android.app.PendingIntent; import android.content.Intent; import android.util.Log; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; public class DownloadIntentService extends IntentService { private static final String TAG = DownloadIntentService.class.getSimpleName(); public static final String PENDING_RESULT_EXTRA = "pending_result"; public static final String URL_EXTRA = "url"; public static final String RSS_RESULT_EXTRA = "url"; public static final int RESULT_CODE = 0; public static final int INVALID_URL_CODE = 1; public static final int ERROR_CODE = 2; private IllustrativeRSSParser parser; public DownloadIntentService() { super(TAG); // make one and reuse, in the case where more than one intent is queued parser = new IllustrativeRSSParser(); } @Override protected void onHandleIntent(Intent intent) { PendingIntent reply = intent.getParcelableExtra(PENDING_RESULT_EXTRA); InputStream in = null; try { try { URL url = new URL(intent.getStringExtra(URL_EXTRA)); IllustrativeRSS rss = parser.parse(in = url.openStream()); Intent result = new Intent(); result.putExtra(RSS_RESULT_EXTRA, rss); reply.send(this, RESULT_CODE, result); } catch (MalformedURLException exc) { reply.send(INVALID_URL_CODE); } catch (Exception exc) { // could do better by treating the different sax/xml exceptions individually reply.send(ERROR_CODE); } } catch (PendingIntent.CanceledException exc) { Log.i(TAG, "reply cancelled", exc); } } }
Step 2: Register the service in the manifest:
<service android:name=".DownloadIntentService" android:exported="false"/>
Step 3: Invoke the service from the Activity, passing a PendingResult object which the Service will use to return the result:
PendingIntent pendingResult = createPendingResult( RSS_DOWNLOAD_REQUEST_CODE, new Intent(), 0); Intent intent = new Intent(getApplicationContext(), DownloadIntentService.class); intent.putExtra(DownloadIntentService.URL_EXTRA, URL); intent.putExtra(DownloadIntentService.PENDING_RESULT_EXTRA, pendingResult); startService(intent);
Step 4: Handle the result in onActivityResult:
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == RSS_DOWNLOAD_REQUEST_CODE) { switch (resultCode) { case DownloadIntentService.INVALID_URL_CODE: handleInvalidURL(); break; case DownloadIntentService.ERROR_CODE: handleError(data); break; case DownloadIntentService.RESULT_CODE: handleRSS(data); break; } handleRSS(data); } super.onActivityResult(requestCode, resultCode, data); }
A GitHub project containing a complete working Android Studio/Gradle project is available here.