Принятый ответ имеет несколько существенных недостатков. Не рекомендуется использовать AsyncTask для работы в сети, если вы действительно не знаете, что делаете. Некоторые из недостатков включают в себя:
- AsyncTask, созданные как нестатические внутренние классы, имеют неявную ссылку на включающий объект Activity, его контекст и всю иерархию View, созданную этим действием. Эта ссылка предотвращает сбор мусора в Activity, пока не завершится фоновая работа AsyncTask. Если подключение пользователя медленное и / или загрузка велика, эти кратковременные утечки памяти могут стать проблемой - например, если ориентация меняется несколько раз (и вы не отменяете выполнение задач), или пользователь перемещается от деятельности.
- AsyncTask имеет различные характеристики выполнения в зависимости от платформы, на которой он выполняется: до уровня API 4 AsyncTask выполняется последовательно в одном фоновом потоке; от уровня API 4 до уровня API 10 AsyncTasks выполняется в пуле до 128 потоков; начиная с уровня API 11 и далее AsyncTask выполняется последовательно в одном фоновом потоке (если вы не используете перегруженный метод
executeOnExecutor
и не предоставляете альтернативного исполнителя). Код, который отлично работает при последовательном запуске на ICS, может прерваться при одновременном выполнении на Gingerbread, например, если у вас есть непреднамеренные зависимости порядка выполнения.
Если вы хотите избежать кратковременных утечек памяти, иметь четко определенные характеристики выполнения на всех платформах и иметь базу для построения действительно надежной обработки сети, вы можете рассмотреть следующие вопросы:
- Использование библиотеки, которая делает эту работу за вас - есть хорошее сравнение сетевых библиотек в этом вопросе или
- Вместо этого используйте
Service
или IntentService
, возможно, с PendingIntent
, чтобы вернуть результат методом onActivityResult
Действия.
IntentService подход
вниз-сторона:
- Больше кода и сложности, чем
AsyncTask
, но не так много, как вы думаете
- Будет помещать запросы в очередь и запускать их в одном фоновом потоке. Вы можете легко контролировать это, заменив
IntentService
эквивалентной Service
реализацией, возможно, такой как this .
- Хм, сейчас я не могу думать ни о каких других
Up-сторон:
- Предотвращает проблему кратковременной утечки памяти
- Если ваша деятельность возобновляется, когда сетевые операции находятся в полете, она все равно может получить результат загрузки с помощью
onActivityResult
метода
- Лучшая платформа, чем AsyncTask, для создания и повторного использования надежного сетевого кода. Пример: если вам нужно сделать важную загрузку, вы можете сделать это с
AsyncTask
в Activity
, но если пользователь переключается из приложения, чтобы принять телефонный звонок, система может убить приложение до завершения загрузки. менее вероятно убить приложение с активным Service
.
- Если вы используете свою собственную параллельную версию
IntentService
(например, ту, которую я упомянул выше), вы можете контролировать уровень параллелизма с помощью Executor
.
Краткое описание реализации
Вы можете легко реализовать IntentService
для загрузки в одном фоновом потоке.
Шаг 1: Создайте IntentService
для выполнения загрузки. Вы можете указать, что загружать с помощью Intent
extra, и передать его PendingIntent
, чтобы использовать для возврата результата в 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 re-use, 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);
}
}
}
Шаг 2. Зарегистрируйте службу в манифесте:
<service
android:name=".DownloadIntentService"
android:exported="false"/>
Шаг 3: вызвать службу из Activity, передав объект PendingResult, который Service будет использовать для возврата результата:
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);
Шаг 4: обработать результат в 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);
}
Доступен проект github, содержащий полностью работающий проект Android-Studio / gradle здесь .