Как исправить android.ОС.NetworkOnMainThreadException?
Я получил ошибку при запуске моего проекта Android для RssReader.
код:
URL url = new URL(urlToRssFeed);
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser();
XMLReader xmlreader = parser.getXMLReader();
RssHandler theRSSHandler = new RssHandler();
xmlreader.setContentHandler(theRSSHandler);
InputSource is = new InputSource(url.openStream());
xmlreader.parse(is);
return theRSSHandler.getFeed();
и он показывает ниже Сообщение об ошибке:
android.os.NetworkOnMainThreadException
Как я могу исправить эту проблему?
30 ответов
это исключение возникает, когда приложение пытается выполнить сетевую операцию в своем основном потоке. Запустите код в AsyncTask
:
class RetrieveFeedTask extends AsyncTask<String, Void, RSSFeed> {
private Exception exception;
protected RSSFeed doInBackground(String... urls) {
try {
URL url = new URL(urls[0]);
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser();
XMLReader xmlreader = parser.getXMLReader();
RssHandler theRSSHandler = new RssHandler();
xmlreader.setContentHandler(theRSSHandler);
InputSource is = new InputSource(url.openStream());
xmlreader.parse(is);
return theRSSHandler.getFeed();
} catch (Exception e) {
this.exception = e;
return null;
} finally {
is.close();
}
}
protected void onPostExecute(RSSFeed feed) {
// TODO: check this.exception
// TODO: do something with the feed
}
}
Как выполнить задание:
на MainActivity.java
файл вы можете добавить эту строку в свой oncreate()
метод
new RetrieveFeedTask().execute(urlToRssFeed);
Не забудьте добавить это :
<uses-permission android:name="android.permission.INTERNET"/>
вы почти всегда должны запускать сетевые операции в потоке или как асинхронную задачу.
но это is можно удалить это ограничение и переопределить поведение по умолчанию, если вы готовы принять последствия.
добавить:
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
в классе
и
добавьте это разрешение в манифест android.xml-файл:
<uses-permission android:name="android.permission.INTERNET"/>
последствия:
ваш приложение будет (в областях пятнистого подключения к интернету) становится невосприимчивым и заблокировать, пользователь воспринимает медлительность и должен сделать силу убить, и вы рискуете менеджер деятельности убить приложение и сказать пользователю, что приложение остановилось.
Android имеет некоторые хорошие советы по хорошей практике программирования для разработки для отзывчивости: http://developer.android.com/reference/android/os/NetworkOnMainThreadException.html
Я решил эту проблему с помощью нового Thread
.
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
//Your code goes here
} catch (Exception e) {
e.printStackTrace();
}
}
});
thread.start();
вы не можете выполнить network I / O в потоке пользовательского интерфейса на сот. Технически, это is возможно на более ранних версиях Android, но это действительно плохая идея, поскольку это заставит ваше приложение перестать отвечать на запросы и может привести к тому, что ОС убьет ваше приложение за плохое поведение. Вам нужно будет запустить фоновый процесс или использовать AsyncTask для выполнения сетевой транзакции в фоновом потоке.
есть статья о безболезненно Threading на сайте разработчика Android, который является хорошим введением к этому, и он предоставит вам гораздо лучшую глубину ответа, чем может быть реалистично представлен здесь.
принято отвечать имеет ряд существенных вниз-стороны. Не рекомендуется использовать AsyncTask для создания сетей, если вы действительно знаете, что вы делаете. Некоторые из нижних сторон включают:
- AsyncTask, созданные как нестатические внутренние классы, имеют неявную ссылку на заключающий объект действия, его контекст и всю иерархию представлений, созданную этим действием. Эта ссылка предотвращает сбор мусора Activity до тех пор, пока Фоновая работа AsyncTask завершена. Если соединение пользователя медленное и / или загрузка большая, эти кратковременные утечки памяти могут стать проблемой-например, если ориентация изменяется несколько раз (и вы не отменяете выполняемые задачи), или пользователь переходит от действия.
- AsyncTask имеет разные характеристики выполнения в зависимости от платформы, на которой он выполняется: до уровня API 4 AsyncTask выполняются последовательно в одном фоновом потоке; из API Уровень 4 через уровень API 10 AsyncTask выполняется в пуле до 128 потоков; начиная с уровня API 11 и далее AsyncTask выполняется последовательно в одном фоновом потоке (если вы не используете перегруженный
executeOnExecutor
метод и поставка альтернативного исполнителя). Код, который отлично работает при последовательном запуске на ICS, может сломаться при одновременном выполнении на Gingerbread, скажем, если у вас есть непреднамеренные зависимости порядка выполнения.
если вы хотите, чтобы избежать кратковременных утечек памяти, хорошо определенные характеристики выполнения на всех платформах и имеют базу для создания действительно надежной сетевой обработки, вы можете рассмотреть:
- использование библиотеки, которая делает хорошую работу для вас - есть хорошее сравнение сетевых библиотек в этот вопрос или
- С помощью
Service
или СPendingIntent
чтобы вернуть результат через активностьonActivityResult
метод.
IntentService подходи!--44-->
вниз-стороны:
- больше кода и сложности, чем
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 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: вызовите службу из действия, передав объект 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 здесь.
- Не используйте strictMode (только в режиме отладки)
- не изменять версию SDK
- Не используйте отдельный поток
Используйте сервис или AsyncTask
см. также вопрос переполнения стека:
android.ОС.NetworkOnMainThreadException отправка электронной почты с Android
выполните сетевые действия в другом потоке
Например:
new Thread(new Runnable(){
@Override
public void run() {
// Do network action in this function
}
}).start();
и добавьте это в AndroidManifest.в XML
<uses-permission android:name="android.permission.INTERNET"/>
отключить строгий режим, используя следующий код:
if (android.os.Build.VERSION.SDK_INT > 9) {
StrictMode.ThreadPolicy policy =
new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
}
не рекомендуется используйте AsyncTask
интерфейс.
сетевые операции не могут выполняться в главном потоке. Вам нужно запустить все сетевые задачи в дочернем потоке или реализовать AsyncTask.
вот как вы запускаете задачу в дочернем потоке:
new Thread(new Runnable(){
@Override
public void run() {
try {
// Your implementation goes here
}
catch (Exception ex) {
ex.printStackTrace();
}
}
}).start();
поместите свой код внутрь:
new Thread(new Runnable(){
@Override
public void run() {
try {
// Your implementation
}
catch (Exception ex) {
ex.printStackTrace();
}
}
}).start();
или:
class DemoTask extends AsyncTask<Void, Void, Void> {
protected Void doInBackground(Void... arg0) {
//Your implementation
}
protected void onPostExecute(Void result) {
// TODO: do something with the feed
}
}
С помощью Android Аннотации - Это вариант. Это позволит вам запустить любой метод в фоновом потоке:
// normal method
private void normal() {
doSomething(); // do something in background
}
@Background
protected void doSomething()
// run your networking code here
}
обратите внимание, что, хотя он обеспечивает преимущества простоты и удобочитаемости, у него есть свои недостатки.
Это происходит в Android 3.0 и выше. С Android 3.0 и выше они ограничили использование сетевых операций (функций, которые получают доступ к интернету) от запуска в основном потоке/потоке пользовательского интерфейса (что порождает ваши методы создания и возобновления в активности).
Это поощрять использование отдельных потоков для сетевых операций. См.AsyncTask для получения более подробной информации о том, как правильно выполнять сетевые действия.
вы не должны выполнять какие-либо трудоемкие задачи в основном потоке (UI thread), например, любые сетевые операции, операции ввода-вывода файлов или операции базы данных SQLite. Таким образом, для такого рода операций необходимо создать рабочий поток, но проблема в том, что вы не можете напрямую выполнять операции, связанные с пользовательским интерфейсом из рабочего потока. Для этого, вы должны использовать Handler
и передать Message
.
чтобы упростить все эти вещи, Android предоставляет различные способы, как AsyncTask
, AsyncTaskLoader
, CursorLoader
или IntentService
. Так вы можете использовать любое из этих согласно вашим требованиям.
верх ответ spektom работает идеально.
если вы пишете AsyncTask
inline и не расширяется как класс, и, кроме того, если есть необходимость получить ответ из AsyncTask
можно использовать get()
метод, как показано ниже.
RSSFeed feed = new RetreiveFeedTask().execute(urlToRssFeed).get();
(на его примере.)
ошибка связана с выполнением длительных операций в основном потоке, вы можете легко исправить проблему, используя AsynTask или нить. Вы можете проверить эту библиотеку AsyncHTTPClient для лучшей управляемости.
AsyncHttpClient client = new AsyncHttpClient();
client.get("http://www.google.com", new AsyncHttpResponseHandler() {
@Override
public void onStart() {
// Called before a request is started
}
@Override
public void onSuccess(int statusCode, Header[] headers, byte[] response) {
// Called when response HTTP status is "200 OK"
}
@Override
public void onFailure(int statusCode, Header[] headers, byte[] errorResponse, Throwable e) {
// Called when response HTTP status is "4XX" (for example, 401, 403, 404)
}
@Override
public void onRetry(int retryNo) {
// Called when request is retried
}
});
это бросается только для приложений, ориентированных на сот SDK или выше. Приложения, ориентированные на более ранние версии SDK, могут создавать сети в своих основных потоках цикла событий.
для меня это было так:
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="10" />
устройства я проверял мое приложение была 4.1.2, которая является SDK версии 16!
убедитесь, что целевая версия совпадает с целевой библиотекой Android. Если вы не уверены, что ваша целевая библиотека, щелкните правой кнопкой мыши проект ->Построить Путь ->Android, и он должен быть отмечен.
кроме того, как уже упоминалось, включите правильные разрешения для доступа к Интернет:
<uses-permission android:name="android.permission.INTERNET"/>
просто, чтобы четко сформулировать что-то:
основной поток-это в основном поток пользовательского интерфейса.
таким образом, говоря, что вы не можете выполнять сетевые операции в основном потоке, вы не можете выполнять сетевые операции в потоке пользовательского интерфейса, что означает вы не можете выполнять сетевые операции в *runOnUiThread(new Runnable() { ... }*
блок внутри какой-то другой нити.
(У меня просто был длинный момент царапания головы, пытаясь понять, почему я получал эту ошибку где-то кроме моей основной темы. Вот почему; эта тема помогла; и, надеюсь, этот комментарий поможет кому-то еще.)
это исключение возникает из-за любой тяжелой задачи, выполняемой в основном потоке, если эта задача выполняется слишком много времени.
чтобы избежать этого, мы можем справиться с этим с помощью темы или исполнители
Executors.newSingleThreadExecutor().submit(new Runnable() {
@Override
public void run() {
// You can perform your task here.
}
});
**Use like this in Your Activity**
btnsub.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
//Initialize soap request + add parameters
SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME1);
//Use this to add parameters
request.addProperty("pincode",txtpincode.getText().toString());
request.addProperty("bg",bloodgroup.getSelectedItem().toString());
//Declare the version of the SOAP request
SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
envelope.setOutputSoapObject(request);
envelope.dotNet = true;
try {
HttpTransportSE androidHttpTransport = new HttpTransportSE(URL);
//this is the actual part that will call the webservice
androidHttpTransport.call(SOAP_ACTION1, envelope);
// Get the SoapResult from the envelope body.
SoapObject result = (SoapObject)envelope.getResponse();
Log.e("result data", "data"+result);
SoapObject root = (SoapObject) result.getProperty(0);
// SoapObject s_deals = (SoapObject) root.getProperty(0);
//SoapObject s_deals_1 = (SoapObject) s_deals.getProperty(0);
//
System.out.println("********Count : "+ root.getPropertyCount());
value=new ArrayList<Detailinfo>();
for (int i = 0; i < root.getPropertyCount(); i++)
{
SoapObject s_deals = (SoapObject) root.getProperty(i);
Detailinfo info=new Detailinfo();
info.setFirstName( s_deals.getProperty("Firstname").toString());
info.setLastName( s_deals.getProperty("Lastname").toString());
info.setDOB( s_deals.getProperty("DOB").toString());
info.setGender( s_deals.getProperty("Gender").toString());
info.setAddress( s_deals.getProperty("Address").toString());
info.setCity( s_deals.getProperty("City").toString());
info.setState( s_deals.getProperty("State").toString());
info.setPinecode( s_deals.getProperty("Pinecode").toString());
info.setMobile( s_deals.getProperty("Mobile").toString());
info.setEmail( s_deals.getProperty("Email").toString());
info.setBloodgroup( s_deals.getProperty("Bloodgroup").toString());
info.setAdddate( s_deals.getProperty("Adddate").toString());
info.setWaight(s_deals.getProperty("waight").toString());
value.add(info);
}
} catch (Exception e) {
e.printStackTrace();
}
Intent inten=new Intent(getApplicationContext(),ComposeMail.class);
//intent.putParcelableArrayListExtra("valuesList", value);
startActivity(inten);
}
}).start();
}
});
простыми словами,
НЕ ВЫПОЛНЯЙТЕ СЕТЕВУЮ РАБОТУ В ПОТОКЕ ПОЛЬЗОВАТЕЛЬСКОГО ИНТЕРФЕЙСА
например, если вы выполняете HTTP-запрос, это сетевое действие.
устранение:
- вы должны создать новый поток
- или использовать класс AsyncTask
путь:
положите все ваши работы внутрь
-
run()
метод новой нити -
или
doInBackground()
метод класса AsyncTask.
но:
когда вы получаете что-то от сетевого ответа и хотите показать его на своем представлении (например, отобразить ответное сообщение в TextView), вам нужно вернуться обратно в интерфейс нить.
если вы этого не сделаете, вы получите ViewRootImpl$CalledFromWrongThreadException
.
как?
- при использовании AsyncTask, обновить вид
onPostExecute()
метод -
или вызов
runOnUiThread()
метод и вид обновления внутриrun()
метод.
на этот вопрос уже есть много отличных ответов, но с тех пор, как эти ответы были опубликованы, появилось много отличных библиотек. Это предназначено как своего рода руководство для новичков.
я рассмотрю несколько вариантов использования для выполнения сетевых операций и a решение или два для каждого.
отдых через HTTP
обычно Json, может быть XML или что-то еще
полный доступ к API
давайте вы пишете приложение, которое позволяет пользователям отслеживать цены на акции, процентные ставки и currecy курсов. Вы найдете API Json, который выглядит примерно так:http://api.example.com/stocks //ResponseWrapper<String> object containing a list of Srings with ticker symbols
http://api.example.com/stocks/$symbol //Stock object
http://api.example.com/stocks/$symbol/prices //PriceHistory<Stock> object
http://api.example.com/currencies //ResponseWrapper<String> object containing a list of currency abbreviation
http://api.example.com/currencies/$currency //Currency object
http://api.example.com/currencies/$id1/values/$id2 //PriceHistory<Currency> object comparing the prices of the first currency (id1) to the second (id2)
дооснащение с площади
это отличный выбор для API с несколькими конечными точками и позволяет объявлять остальные конечные точки вместо того, чтобы кодировать их индивидуально, как с другими библиотеками, такими как ion или Volley. (вебсайт: http://square.github.io/retrofit/)
как вы используете его с API финансов?
построить.Gradle в
добавьте эти строки в buid уровня модуля.Gradle в:
implementation 'com.squareup.retrofit2:retrofit:2.3.0' //retrofit library, current as of September 21, 2017
implementation 'com.squareup.retrofit2:converter-gson:2.3.0' //gson serialization and deserialization support for retrofit, version must match retrofit version
FinancesApi.java
public interface FinancesApi {
@GET("stocks")
Call<ResponseWrapper<String>> listStocks();
@GET("stocks/{symbol}")
Call<Stock> getStock(@Path("symbol")String tickerSymbol);
@GET("stocks/{symbol}/prices")
Call<PriceHistory<Stock>> getPriceHistory(@Path("symbol")String tickerSymbol);
@GET("currencies")
Call<ResponseWrapper<String>> listCurrencies();
@GET("currencies/{symbol}")
Call<Currency> getCurrency(@Path("symbol")String currencySymbol);
@GET("currencies/{symbol}/values/{compare_symbol}")
Call<PriceHistory<Currency>> getComparativeHistory(@Path("symbol")String currency, @Path("compare_symbol")String currencyToPriceAgainst);
}
FinancesApiBuilder
public class FinancesApiBuilder {
public static FinancesApi build(String baseUrl){
return new Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(FinancesApi.class);
}
}
фрагмент фрагмента FinancesFragment
FinancesApi api = FinancesApiBuilder.build("http://api.example.com/"); //trailing '/' required for predictable behavior
api.getStock("INTC").enqueue(new Callback<Stock>(){
@Override
public void onResponse(Call<Stock> stockCall, Response<Stock> stockResponse){
Stock stock = stockCall.body();
//do something with the stock
}
@Override
public void onResponse(Call<Stock> stockCall, Throwable t){
//something bad happened
}
}
если ваш API требует ключа API или другой заголовок, такой как токен пользователя и т. д. чтобы быть отправленным, Retrofit делает это легко (см. Этот удивительный ответ для деталей:https://stackoverflow.com/a/42899766/1024412).
One off ReST API access
предположим, вы создаете приложение "Погода настроения", которое ищет местоположение GPS пользователей и проверяет текущую температуру в этой области и сообщает им настроение. Этот тип приложения не должен объявлять конечные точки API; он просто должен иметь доступ одна конечная точка API.
Иона
это отличная библиотека для такого типа доступа.
пожалуйста, прочитайте отличный ответ msysmilu (https://stackoverflow.com/a/28559884/1024412)
загрузка изображений через HTTP
волейбол
Volley также может использоваться для REST API, но из-за более сложной настройки я предпочитаю использовать Retrofit from Square, как указано выше (http://square.github.io/retrofit/)
предположим, вы создаете приложение для социальных сетей и хотите загрузить фотографии профиля друзей.
построить.Gradle в
добавьте эту строку в buid уровня модуля.Gradle в:
implementation 'com.android.volley:volley:1.0.0'
ImageFetch.java
Volley требует больше установки чем Retrofit. Вам нужно будет создать такой класс, чтобы настроить RequestQueue, ImageLoader и ImageCache, но это не так уж плохо:
public class ImageFetch {
private static ImageLoader imageLoader = null;
private static RequestQueue imageQueue = null;
public static ImageLoader getImageLoader(Context ctx){
if(imageLoader == null){
if(imageQueue == null){
imageQueue = Volley.newRequestQueue(ctx.getApplicationContext());
}
imageLoader = new ImageLoader(imageQueue, new ImageLoader.ImageCache() {
Map<String, Bitmap> cache = new HashMap<String, Bitmap>();
@Override
public Bitmap getBitmap(String url) {
return cache.get(url);
}
@Override
public void putBitmap(String url, Bitmap bitmap) {
cache.put(url, bitmap);
}
});
}
return imageLoader;
}
}
user_view_dialog.в XML
добавьте в XML-файл макета следующее, Чтобы добавить изображение:
<com.android.volley.toolbox.NetworkImageView
android:id="@+id/profile_picture"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
app:srcCompat="@android:drawable/spinner_background"/>
UserViewDialog.java
добавьте следующий код в метод onCreate (фрагмент, действие) или конструктор (диалоговое окно):
NetworkImageView profilePicture = view.findViewById(R.id.profile_picture);
profilePicture.setImageUrl("http://example.com/users/images/profile.jpg", ImageFetch.getImageLoader(getContext());
Пикассо
еще одна отличная библиотека с площади. Пожалуйста, посмотрите сайт для некоторых больших примеры: http://square.github.io/picasso/
хотя выше есть огромный пул решений, никто не упомянул com.koushikdutta.ion
: https://github.com/koush/ion
кроме асинхронные и очень просто использование:
Ion.with(context)
.load("http://example.com/thing.json")
.asJsonObject()
.setCallback(new FutureCallback<JsonObject>() {
@Override
public void onCompleted(Exception e, JsonObject result) {
// do stuff with the result or error
}
});
это работает. Только что сделал доктор Luiji ответ немного проще.
new Thread() {
@Override
public void run() {
try {
//Your code goes here
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
на Android, сетевые операции не могут выполняться в главном потоке. Для выполнения сетевых операций можно использовать Thread, AsyncTask (краткосрочные задачи), Service (долгосрочные задачи).
доступ к сетевым ресурсам из основного потока (UI) вызывает это исключение. Чтобы избежать этой проблемы, используйте отдельный поток или AsyncTask для доступа к сетевому ресурсу.
RxAndroid
еще одна лучшая альтернатива этой проблеме, и это избавляет нас от неприятностей создания потоков, а затем публикации результатов в потоке пользовательского интерфейса Android.
Нам просто нужно указать потоки, на которых должны выполняться задачи, и все обрабатывается внутри.
Observable<List<String>> musicShowsObservable = Observable.fromCallable(new Callable<List<String>>() {
@Override
public List<String> call() {
return mRestClient.getFavoriteMusicShows();
}
});
mMusicShowSubscription = musicShowsObservable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<List<String>>() {
@Override
public void onCompleted() { }
@Override
public void onError(Throwable e) { }
@Override
public void onNext(List<String> musicShows){
listMusicShows(musicShows);
}
});
при указании
(Schedulers.io())
,RxAndroid будет работатьgetFavoriteMusicShows()
в другом потоке.С помощью
AndroidSchedulers.mainThread()
мы хотим наблюдать это наблюдаемое в потоке UI, т. е. мы хотим, чтобы нашиonNext()
обратный вызов для вызова в потоке пользовательского интерфейса
новая Thread
и AsyncTask решения уже были объяснены.
AsyncTask
должно идеально использоваться для коротких операций. Нормальный Thread
не является предпочтительным для Android.
посмотреть на альтернативное решение, используя HandlerThread и проводник
HandlerThread
удобный класс для запуска нового потока с петлителем. Петлитель может быть используется для создания классов обработчиков. Обратите внимание, что
start()
еще должен быть вызван.
обработчик:
обработчик позволяет отправлять и обрабатывать сообщения и запускаемые объекты, связанные с MessageQueue потока. Каждый экземпляр обработчика связан с одним потоком и очередью сообщений этого потока. Когда вы создаете новый обработчик, он привязывается к потоку / очереди сообщений потока, который его создает - с этого момента он будет доставляйте сообщения и запускаемые файлы в эту очередь сообщений и выполняйте их по мере выхода из очереди сообщений.
устранение:
создать
HandlerThread
вызов
start()
onHandlerThread
создать
Handler
на получениеLooper
СHanlerThread
добавьте код, связанный с сетевой операцией, в
Runnable
объектотправить
Runnable
заданиеHandler
пример фрагмента кода, который адрес NetworkOnMainThreadException
HandlerThread handlerThread = new HandlerThread("URLConnection");
handlerThread.start();
handler mainHandler = new Handler(handlerThread.getLooper());
Runnable myRunnable = new Runnable() {
@Override
public void run() {
try {
Log.d("Ravi", "Before IO call");
URL page = new URL("http://www.google.com");
StringBuffer text = new StringBuffer();
HttpURLConnection conn = (HttpURLConnection) page.openConnection();
conn.connect();
InputStreamReader in = new InputStreamReader((InputStream) conn.getContent());
BufferedReader buff = new BufferedReader(in);
String line;
while ( (line = buff.readLine()) != null) {
text.append(line + "\n");
}
Log.d("Ravi", "After IO call");
Log.d("Ravi",text.toString());
}catch( Exception err){
err.printStackTrace();
}
}
};
mainHandler.post(myRunnable);
Плюсы использования этого подхода:
- создание нового
Thread/AsyncTask
для каждой сетевой операции является дорогостоящим. TheThread/AsyncTask
будет уничтожен и воссоздан для следующих сетевых операций. Но сHandler
иHandlerThread
подход, вы можете отправить много сетевых операций (как выполняемые задачи) в одинHandlerThread
С помощьюHandler
.
вам не разрешено выполнять сетевые операции в UI-потоке на Android. Вам придется использовать класс AsyncTask для выполнения сетевых операций, таких как отправка запроса API, загрузка изображения с URL-адреса и т. д. и используя методы обратного вызова AsyncTask, вы можете получить результат в onPostExecute menthod, и вы будете в потоке пользовательского интерфейса, и вы можете заполнить пользовательский интерфейс данными из веб-службы или что-то в этом роде.
Пример: Предположим, вы хотите загрузить изображение с URL-адрес: https://www.samplewebsite.com/sampleimage.jpg
решение с использованием AsyncTask: соответственно.
public class MyDownloader extends AsyncTask<String,Void,Bitmap>
{
@Override
protected void onPreExecute() {
// Show progress dialog
super.onPreExecute();
}
@Override
protected void onPostExecute(Bitmap bitmap) {
//Populate Ui
super.onPostExecute(bitmap);
}
@Override
protected Bitmap doInBackground(String... params) {
// Open URL connection read bitmaps and return form here
return result;
}
@Override
protected void onProgressUpdate(Void... values) {
// Show progress update
super.onProgressUpdate(values);
}
}
}
Примечание: не забудьте добавить разрешение интернета в файл манифеста Android. Это сработает как заклинание. :)
есть еще один очень удобный способ решения этой проблемы-использовать возможности параллелизма rxJava. Вы можете выполнить любую задачу в фоновом режиме и опубликовать результаты в основной поток очень удобным способом, поэтому эти результаты будут переданы в цепочку обработки.
первый проверенный ответ совет использовать AsynTask. Да, это решение, но оно устарело сегодня, потому что появились новые инструменты.
String getUrl() {
return "SomeUrl";
}
private Object makeCallParseResponse(String url) {
return null;
//
}
private void processResponse(Object o) {
}
метод getUrl предоставляет URL-адрес и он будет выполняться в основном потоке.
makeCallParseResponse(..) - делает фактическую работу
processResponse(..)- обработает результат на основном потоке.
код для асинхронного выполнения будет выглядеть так:
rx.Observable.defer(new Func0<rx.Observable<String>>() {
@Override
public rx.Observable<String> call() {
return rx.Observable.just(getUrl());
}
})
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.map(new Func1<String, Object>() {
@Override
public Object call(final String s) {
return makeCallParseResponse(s);
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Object>() {
@Override
public void call(Object o) {
processResponse(o);
}
},
new Action1<Throwable>() {
@Override
public void call(Throwable throwable) {
// Process error here, it will be posted on
// the main thread
}
});
по сравнению с AsyncTask, этот метод позволяет переключать планировщики произвольное количество раз (скажем, получать данные на одном планировщике и обрабатывать эти данные на другом (скажем, планировщик.вычисление.))( Вы также можете определить свой собственный планировщики.
чтобы использовать эту библиотеку, включите в нее следующие строки.файл gradle:
compile 'io.reactivex:rxjava:1.1.5'
compile 'io.reactivex:rxandroid:1.2.0'
последняя зависимость включает поддержку .mainThread() планировщик.