У принятого ответа есть несколько существенных недостатков. Не рекомендуется использовать AsyncTask для работы в сети, если вы действительно не знаете, что делаете. Вот некоторые из недостатков:
- AsyncTask, созданные как нестатические внутренние классы, имеют неявную ссылку на включающий объект Activity, его контекст и всю иерархию View, созданную этим действием. Эта ссылка предотвращает сборку мусора Activity до завершения фоновой работы AsyncTask. Если соединение пользователя медленное и / или загрузка велика, эти краткосрочные утечки памяти могут стать проблемой - например, если ориентация меняется несколько раз (и вы не отменяете выполняемые задачи) или пользователь уходит от Activity.
- AsyncTask имеет разные характеристики выполнения в зависимости от платформы, на которой он выполняется: до API уровня 4 AsyncTasks выполняются последовательно в одном фоновом потоке; от уровня API 4 до уровня API 10 AsyncTasks выполняются в пуле до 128 потоков; начиная с уровня API 11 и далее AsyncTask выполняется последовательно в одном фоновом потоке (если вы не используете перегруженный метод
executeOnExecutor
и не предоставляете альтернативный исполнитель). Код, который отлично работает при последовательном запуске в ICS, может сломаться при одновременном выполнении в Gingerbread, например, если у вас есть непреднамеренные зависимости порядка выполнения.
Если вы хотите избежать краткосрочных утечек памяти, иметь четко определенные характеристики выполнения на всех платформах и иметь основу для создания действительно надежной сетевой обработки, вы можете рассмотреть:
- Использование библиотеки, которая отлично справляется с этой задачей для вас - есть хорошее сравнение сетевых библиотек в этом вопросе или
- Использование вместо этого
Service
или IntentService
, возможно, с PendingIntent
для возврата результата через метод Activity onActivityResult
.
подход IntentService
Минусы:
- Больше кода и сложности, чем
AsyncTask
, но не так много, как вы думаете
- Будет помещать запросы в очередь и запускать их в одиночном фоновом потоке. Вы можете легко контролировать это, заменив
IntentService
эквивалентной реализацией Service
, например, этой.
- Эм, я не могу сейчас вспомнить ни о каких других
Плюсы:
- Устраняет проблему краткосрочной утечки памяти
- Если ваша деятельность перезапускается во время выполнения сетевых операций, она все равно может получить результат загрузки с помощью своего
onActivityResult
метода
- Лучшая платформа, чем AsyncTask, для создания и повторного использования надежного сетевого кода. Пример: если вам нужно выполнить важную загрузку, вы можете сделать это из
AsyncTask
в Activity
, но если пользователь переключает контекст из приложения, чтобы принять телефонный звонок, система может убить приложение до завершения загрузки. менее вероятно убить приложение с активным Service
.
- Если вы используете свою собственную параллельную версию
IntentService
(например, ту, которую я связал выше), вы можете контролировать уровень параллелизма с помощью Executor
.
Обзор реализации
Вы можете легко реализовать IntentService
для выполнения загрузок в одном фоновом потоке.
Шаг 1. Создайте IntentService
для выполнения загрузки. Вы можете указать ему, что загружать, с помощью Intent
дополнений и передать ему 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 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);
}
}
}
Шаг 2. Зарегистрируйте службу в манифесте:
<service
android:name=".DownloadIntentService"
android:exported="false"/>
Шаг 3: вызовите службу из Activity, передав объект PendingResult, который служба будет использовать для возврата результата:
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, доступен здесь.
Прочтите это сообщение в блоге в NetworkOnMainThreadException для получения дополнительной информации. Это объясняет, почему это происходит на Android 3.0 и выше.
Чтобы быть в курсе, сначала прочтите о сетевых запросах в Android, а затем я бы рекомендовал изучить «Залп».
Есть много альтернативных библиотек, которые решают эту проблему. Многие из них перечислены внизу этой страницы. Если есть еще, берем :)
Вам нужно запускать интернет-действия в потоке, отдельном от основного (UI) потока.
"Из-за ошибки в предыдущих версиях Android система не помечала запись в сокет TCP в основном потоке как нарушение строгого режима. Android 7.0 исправляет эту ошибку. Приложения, которые демонстрируют такое поведение, теперь выдают файл android.os. NetworkOnMainThreadException. " - Значит, до недавнего времени некоторые из нас этого не делали! developer.android.com/about/versions/nougat/…