У меня возникла ошибка при запуске моего проекта 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
Как я могу исправить эту проблему?
Это исключение возникает, когда приложение пытается выполнить сетевую операцию в своем основном потоке. Запустите свой код в 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);
Не забудьте добавить это в файл AndroidManifest.xml
:
<uses-permission android:name="android.permission.INTERNET"/>
Вы должны почти всегда запускать сетевые операции в потоке или как асинхронную задачу.
Но это ограничение можно удалить, и вы переопределите поведение по умолчанию, если вы готовы принять последствия.
Добавить
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
В вашем классе
и
Добавьте это разрешение в файл android manifest.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();
Принятый ответ имеет некоторые существенные сторонники. Не рекомендуется использовать AsyncTask для взаимодействия, если вы действительно не знаете, что делаете. Некоторые из сторон включают:
executeOnExecutor
и не предоставляете альтернативный исполнитель). Код, который отлично работает при серийном запуске ICS, может прерываться при одновременном выполнении на Gingerbread, скажем, если у вас есть непреднамеренные зависимости порядка выполнения.Если вы хотите избежать кратковременных утечек памяти, имеете четко определенные характеристики выполнения на всех платформах и имеете базу для создания действительно надежной сетевой обработки, вы можете подумать:
Service
или IntentService
, возможно, с помощью PendingIntent
, чтобы вернуть результат с помощью метода Activity onActivityResult
.Вниз стороны:
AsyncTask
, хотя и не так сильно, как вы думаетеIntentService
на эквивалентную реализацию Service
, возможно, как этот.Up-сторон:
onActivityResult
AsyncTask
в Activity
, но если пользовательский контекст отключит приложение для совершения телефонного звонка, система может убить приложение раньше загрузка завершена. С меньшей вероятностью можно убить приложение с активным Service
.IntentService
(например, связанную выше), вы можете управлять уровнем concurrency с помощью Executor
.Вы можете реализовать IntentService
для выполнения загрузок на одном фоновом потоке довольно легко.
Шаг 1: Создайте IntentService
для выполнения загрузки. Вы можете сказать, что загрузить с помощью Intent
extra's, и передать ему 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, который Служба будет использовать для возврата результата:
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, доступен здесь.
Вы не можете выполнять сетевое I/O в потоке пользовательского интерфейса на Honeycomb. Технически это возможно в более ранних версиях Android, но это действительно плохая идея, так как это заставит ваше приложение перестать отвечать на запросы и может привести к тому, что ОС будет убивать ваше приложение за плохое поведение. Вам нужно будет запустить фоновый процесс или использовать AsyncTask для выполнения сетевой транзакции в фоновом потоке.
На сайте разработчика Android есть статья о Painless Threading, которая будет хорошим знакомством с этим, и она предоставит вам много лучшая глубина ответа, чем это можно реально представить здесь.
Использовать службу или AsyncTask
См. также Stack Вопрос о переполнении:
android.os.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 и выше, они ограничили использование сетевых операций (функций, которые обращаются к Интернету) из потока в потоке основного потока/пользовательского интерфейса (что возникает из ваших методов create и on resume в действии).
Это поощрение использования отдельных потоков для сетевых операций. Подробнее см. AsyncTask о том, как правильно выполнять сетевые действия.
Ошибка связана с выполнением длительных операций в основном потоке. Вы можете легко устранить проблему, используя AsynTask или Thread. Вы можете проверить эту библиотеку 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
}
});
Вам не следует выполнять какую-либо трудоемкую задачу в основном потоке (потоке пользовательского интерфейса), как в любой сетевой операции, операции ввода-вывода или SQLite базы данных. Поэтому для такого рода операций вы должны создать рабочий поток, но проблема в том, что вы не можете напрямую выполнять какую-либо операцию, связанную с пользовательским интерфейсом, из рабочего потока. Для этого вам нужно использовать Handler
и передать Message
.
Чтобы упростить все эти вещи, Android предлагает различные способы, такие как AsyncTask
, AsyncTaskLoader
, CursorLoader
или IntentService
. Поэтому вы можете использовать любой из них в соответствии с вашими требованиями.
Верхний ответ spektom работает идеально.
Если вы пишете AsyncTask
inline и не расширяетесь как класс, и, кроме того, если есть необходимость получить ответ из AsyncTask
, можно использовать метод get()
как ниже.
RSSFeed feed = new RetreiveFeedTask().execute(urlToRssFeed).get();
(Из его примера.)
get()
- плохая идея ... это снова делает AsyncTask синхронизированным
Это делается только для приложений, ориентированных на Honeycomb SDK или выше. Приложения, ориентированные на более ранние версии SDK, могут создавать сети в своих потоках цикла основного события.
Для меня это было так:
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="10" />
Устройство, на которое я тестировал свое приложение, было 4.1.2, которое представляет собой версию SDK версии 16!
Убедитесь, что целевая версия совпадает с вашей целевой целевой библиотекой Android. Если вы не знаете, что такое ваша целевая библиотека, щелкните правой кнопкой мыши ваш проект → Путь сборки → Android, и он должен быть отмечен.
Кроме того, как указывали другие, укажите правильные разрешения для доступа в Интернет:
<uses-permission android:name="android.permission.INTERNET"/>
NetworkOnMainThreadException
- это Guardian, который говорит вам: не стреляйте себе в ногу ... ваше решение: давайте вернемся в прошлое, когда не было Guardian - теперь я могу стрелять по моя нога свободно
Просто для того, чтобы четко сказать что-то:
Основной поток - это в основном поток пользовательского интерфейса.
Поэтому, говоря, что вы не можете выполнять сетевые операции в основном потоке, вы не можете выполнять сетевые операции в потоке пользовательского интерфейса, а это значит, что вы не можете выполнять сетевые операции в блоке *runOnUiThread(new Runnable() { ... }*
внутри какого-либо другого потока.
(У меня просто был длинный головокружающий момент, пытаясь понять, почему я получаю эту ошибку где-то, кроме моего основного потока. Вот почему, этот поток помог, и, надеюсь, этот комментарий поможет кому-то другому.)
Это исключение возникает из-за любой тяжелой задачи, выполняемой в основном потоке, если эта выполняющая задача занимает слишком много времени.
Чтобы этого избежать, мы можем обрабатывать его с помощью потоков или исполнителей
Executors.newSingleThreadExecutor().submit(new Runnable() {
@Override
public void run() {
// You can perform your task here.
}
});
Используйте это в своей деятельности
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 intent = new Intent(getApplicationContext(), ComposeMail.class);
//intent.putParcelableArrayListExtra("valuesList", value);
startActivity(intent);
}
}).start();
}
});
Говоря простыми словами,
НЕ ДЕЛАЙТЕ СЕТЕВЫЕ РАБОТЫ В РЕЖИМЕ UI
Например, если вы выполняете HTTP-запрос, это сетевое действие.
Решение:
Путь:
Поместите все свои работы внутри
run()
новый потокdoInBackground()
класса AsyncTask.Но:
Когда вы получаете что-то из ответа сети и хотите показать его на своем представлении (например, сообщение ответа дисплея в TextView), вам нужно вернуться обратно в поток пользовательского интерфейса.
Если вы этого не сделаете, вы получите ViewRootImpl$CalledFromWrongThreadException
.
Как?
onPostExecute()
runOnUiThread()
и обновите представление внутри метода run()
.На этот вопрос уже много замечательных ответов, но с тех пор, как эти ответы были опубликованы, появилось много замечательных библиотек. Это предназначено как своего рода новичок-гид.
Я расскажу о нескольких вариантах использования для выполнения сетевых операций и решения или двух для каждого.
Обычно Json, может быть XML или что-то еще
Скажем, вы пишете приложение, которое позволяет пользователям отслеживать цены акций, процентные ставки и курсовые курсы. Вы найдете Json API, который выглядит примерно так:
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 с несколькими конечными точками и позволяет объявлять конечные точки REST вместо того, чтобы кодировать их отдельно, как с другими библиотеками, такими как ion или Volley. (веб-сайт: http://square.github.io/retrofit/)
Как вы используете его с API финансов?
build.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);
}
}
Фундамент Фрагмент
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/questions/42898920/add-header-parameter-in-retrofit).
Скажем, вы создаете приложение "погода в настроении", которое просматривает местоположение GPS-пользователей и проверяет текущую температуру в этой области и сообщает им настроение. Для этого типа приложения не требуется объявлять конечные точки API; ему просто нужно иметь доступ к одной конечной точке API.
Это отличная библиотека для такого типа доступа.
Пожалуйста, прочитайте msysmilu отличный ответ (https://stackoverflow.com/questions/6343166/how-do-i-fix-android-os-networkonmainthreadexception)
Volley также может использоваться для API-интерфейсов ReST, но из-за более сложной настройки я предпочитаю использовать Retrofit from Square, как указано выше (http://square.github.io/retrofit/)
Скажем, вы строите приложение для социальных сетей и хотите загружать фотографии друзей друзей.
build.gradle
Добавьте эту строку в свой уровень модуля buid.gradle:
implementation 'com.android.volley:volley:1.0.0'
ImageFetch.java
Volley требует больше настроек, чем дооснащение. Вам нужно будет создать такой класс, чтобы настроить 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 (Fragment, Activity) или конструктор (Dialog):
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
}
});
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()
, мы хотим наблюдать это Observable в потоке пользовательского интерфейса, т.е. мы хотим, чтобы наш обратный вызов onNext()
вызывался в потоке пользовательского интерфейса
Новые Thread
и AsyncTask уже были объяснены.
AsyncTask
в идеале следует использовать для коротких операций. Обычный Thread
не является предпочтительным для Android.
Посмотрите на альтернативное решение, используя HandlerThread и Handler
HandlerThread
Удобный класс для запуска нового потока, в котором есть петлитель. Затем петлитель можно использовать для создания классов обработчиков. Обратите внимание, что
start()
еще нужно вызвать.
Handler:
Обработчик позволяет отправлять и обрабатывать объекты Message и Runnable, связанные с потоком MessageQueue. Каждый экземпляр Handler связан с одним потоком и этой очереди сообщений потока. Когда вы создаете нового обработчика, он привязан к очереди потоков/сообщений потока, который его создает - с этой точки он будет доставлять сообщения и исполняемые файлы в очередь сообщений и выполнять их по мере их выхода из сообщения очереди.
Решение:
Создать HandlerThread
Вызов start()
on HandlerThread
Создайте Handler
, получив Looper
из HanlerThread
Вставьте код, связанный с вашей сетью, в Runnable
object
Отправьте задачу 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
для каждой сетевой операции дорого. Thread/AsyncTask
будет уничтожен и воссоздан для следующих сетевых операций. Но с подходом Handler
и HandlerThread
вы можете отправить множество сетевых операций (как выполняемых задач) на один HandlerThread
с помощью Handler
.Доступ к сетевым ресурсам из основного (UI) потока вызывает это исключение. Используйте отдельный поток или AsyncTask для доступа к сетевому ресурсу, чтобы избежать этой проблемы.
В Android, сетевые операции не могут выполняться в основном потоке. Для выполнения сетевых операций вы можете использовать Thread, AsyncTask (краткосрочные задачи), Service (длительные задачи).
Это работает. Просто сделал доктор Лийджи немного проще.
new Thread() {
@Override
public void run() {
try {
//Your code goes here
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
Существует еще один очень удобный способ решения этой проблемы - используйте возможности rxJava concurrency. Вы можете выполнить любую задачу в фоновом режиме и опубликовать результаты в основной поток очень удобным способом, чтобы эти результаты были переданы цепочке обработки.
Первый проверенный ответ - использование 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 этот метод позволяет переключать планировщики произвольным числом раз (скажем, извлекать данные на одном планировщике и обрабатывать эти данные на другом (скажем, Scheduler.computation()). Вы также можете определить собственные планировщики.
Чтобы использовать эту библиотеку, включите следующие строки в файл build.gradle:
compile 'io.reactivex:rxjava:1.1.5'
compile 'io.reactivex:rxandroid:1.2.0'
Последняя зависимость включает поддержку планировщика .mainThread().
Существует отличная книга для rx-java.
Вам не разрешено выполнять сетевые операции в потоке пользовательского интерфейса на Android. Вам нужно будет использовать класс AsyncTask для выполнения связанных с сетью операций, таких как отправка запроса API, загрузка изображения с URL-адреса и т.д. И использование методов обратного вызова AsyncTask, вы можете получить результат inPostExecute 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. Он будет работать как шарм. :)
Вы можете использовать KOTLIN
и ANKO
.
Kotlin - новый официальный язык для Android, о котором вы можете найти здесь https://kotlinlang.org/docs/tutorials/kotlin-android.html
Anko поддерживаемая библиотека для Kotlin в Android, некоторые документы здесь https://github.com/Kotlin/anko
Решение, которое действительно полезно и имеет только несколько строк кода, написанных @AntonioLeiva https://antonioleiva.com/anko-background-kotlin-android/
doAsync {
var result = runLongTask()
uiThread {
toast(result)
}
}
Простой, как есть, NetworkOnMainThread
возникает, когда вы запускаете фоновое задание на UI Thread
, поэтому вам нужно просто запустить longTask job
в фоновом режиме. Вы можете сделать это, используя этот метод и Kotlin с Anko в своем приложении для Android.
Это исключение возникает, когда приложение пытается выполнить сетевую операцию в своем основном потоке. Если ваша задача заняла более пяти секунд, она требует силы.
Запустите свой код в AsyncTask
:
class RetrieveFeedTask extends AsyncTask<String, Void, Boolean> {
protected RSSFeed doInBackground(String... urls) {
// TODO: Connect
}
protected void onPostExecute(RSSFeed feed) {
// TODO: Check this.exception
// TODO: Do something with the feed
}
}
Я решил эту проблему простым способом...
Я добавил после oncreate
StrictMode.enableDefaults();
и решил это.
или
используйте Service
или AsyncTask
для решения этой проблемы
Примечание:
Do not change SDK version
Do not use a separate thread
Для более проверьте это.
Вы можете переместить часть своего кода в другой поток, чтобы выгрузить main thread
и не получать ANR, NetworkOnMainThreadException, IllegalStateException (например, не удается получить доступ базы данных в основном потоке, так как это может потенциально заблокировать пользовательский интерфейс в течение длительного периода времени).
Есть несколько подходов, которые вы должны выбрать, зависит от ситуации
Java Thread или Android HandlerThread
Java-потоки одноразового использования и умирают после выполнения его метода выполнения.
HandlerThread - удобный класс для запуска нового потока с петлевым механизмом.
AsyncTask предназначен для вспомогательного класса вокруг Thread и Handler и не является общей инфраструктурой потоков. AsyncTasks в идеале следует использовать для коротких операций (максимум за несколько секунд). Если вам нужно поддерживать потоки в течение длительного периода времени, настоятельно рекомендуется использовать различные API-интерфейсы, предоставляемые пакетом java.util.concurrent, например Исполнитель, ThreadPoolExecutor и FutureTask.
Реализация пула потоков ThreadPoolExecutor, ScheduledThreadPoolExecutor...
Класс ThreadPoolExecutor, который реализует ExecutorService, который обеспечивает прекрасный контроль над пулом потоков (например, размер основного пула, максимальный размер пула, сохранение времени жизни и т.д.).
ScheduledThreadPoolExecutor - класс, который расширяет ThreadPoolExecutor. Он может планировать задачи после заданной задержки или периодически.
FutureTask выполняет асинхронную обработку, однако, если результат еще не готов или обработка еще не завершена, вызов get() будет блокировать поток
AsyncTaskLoaders, поскольку они решают много проблем, которые присущи AsyncTask
Это выбор дефакто для длительной обработки на Android, хорошим примером может быть загрузка или загрузка больших файлов. Загрузка и загрузка могут продолжаться, даже если пользователь выходит из приложения, и вы, конечно же, не хотите блокировать пользователя от возможности использовать приложение во время выполнения этих задач.
Эффективно, вам нужно создать Сервис и создать задание с помощью JobInfo.Builder, который задает критерии для того, когда запускать службу.
Библиотека для компоновки асинхронных и событийных программ с использованием наблюдаемых последовательностей.
Coroutines (Kotlin)
Основной смысл этого заключается в том, что асинхронный код выглядит так же, как синхронный
Подробнее здесь:
8 способов сделать асинхронную обработку в Android и подсчете
Эволюция доступа к сети Android
Использование пула потоков в Android
Основной поток - это поток пользовательского интерфейса, и вы не можете выполнять операцию в основном потоке, которая может блокировать взаимодействие пользователя. Вы можете решить это двумя способами:
Принудительно выполнить задачу в основном потоке, подобном этому
StrictMode.ThreadPolicy threadPolicy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(threadPolicy);
Или создайте простой обработчик и обновите основной поток, если хотите.
Runnable runnable;
Handler newHandler;
newHandler = new Handler();
runnable = new Runnable() {
@Override
public void run() {
try {
//update UI
} catch (Exception e) {
e.printStackTrace();
}
}
};
newHandler.post(runnable);
И чтобы остановить использование потока:
newHandler.removeCallbacks(runnable);
Для получения дополнительной информации проверьте это: Плотная резьба
Вы также можете решить эту проблему, используя строгий режим, используя приведенный ниже код. Это также альтернатива решению этой проблемы.
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
Но лучше всего использовать AsyncTask.
android.os.NetworkOnMainThreadException вызывается, когда сетевые операции выполняются в основном потоке. Вам лучше сделать это в AsyncTask, чтобы удалить это исключение. Напишите это так:
new AsyncTask<Void,String,String>(){
@Override
protected Void doInBackground(Void... params) {
// Perform your network operation.
// Get JSON or XML string from the server.
// Store in a local variable (say response) and return.
return response;
}
protected void onPostExecute(String results){
// Response returned by doInBackGround() will be received
// by onPostExecute(String results).
// Now manipulate your jason/xml String(results).
}
}.execute();
}
Как исправить android.os.NetworkOnMainThreadException
Что такое NetworkOnMainThreadException:
В Android все операции пользовательского интерфейса мы должны делать в потоке пользовательского интерфейса (основной поток). Если мы выполняем фоновые операции или некоторые операции с сетью в основном потоке, мы рискуем, что это исключение произойдет, и приложение не ответит.
Как это исправить:
Чтобы избежать этой проблемы, вы должны использовать другой поток для фоновых операций или сетевых операций, например, используя asyncTask и использовать некоторую библиотеку для сетевых операций, таких как Volley, AsyncHttp и т.д.
Мы также можем использовать RxJava для перемещения сетевых операций в фоновый поток. И это довольно просто.
webService.doSomething(someData)
.subscribeOn(Schedulers.newThread())-- This for background thread
.observeOn(AndroidSchedulers.mainThread()) -- for callback on UI
.subscribe(result -> resultText.setText("It worked!"),
e -> handleError(e));
Вы можете сделать намного больше материала с помощью RxJava. Вот некоторые ссылки для RxJava. Не стесняйтесь копаться.
Вы не можете вызвать сеть в основном потоке или потоке пользовательского интерфейса. На Android, если вы хотите позвонить в сеть, есть два варианта -
Лично я предпочитаю asynctask. Для получения дополнительной информации вы можете сослаться на эту ссылку.
Поскольку Android работает над одним потоком, вы не должны выполнять сетевую операцию в основном потоке. Существуют различные способы избежать этого.
Используйте следующий способ выполнения сетевой операции
Никогда не используйте StrictMode.setThreadPolicy(политика), так как это заморозит ваш пользовательский интерфейс и вовсе не является хорошей идеей.
Исключение NetworkOnMainThread возникает, потому что вы вызывали некоторую сетевую операцию по потоку по умолчанию, то есть поток пользовательского интерфейса. В соответствии с версией Android Android 3 (Honeycomb), которая не разрешена, вы должны вызывать сетевую операцию вне основного потока.
Вы можете использовать AsyncTask, IntentService или создать свой собственный поток и вызвать внутри метода run. Для получения дополнительной информации посетите Подключение к сети.
Фактически вы можете начать новую тему, у меня была эта проблема до этого и решить ее таким образом.
Если вы работаете в kotlin и anko, вы можете добавить
doAsync {
method()
}
отключить StrictMode
с помощью этого кода
if (android.os.Build.VERSION.SDK_INT > 9) {
StrictMode.ThreadPolicy policy =
new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
}
Другое разумное использование AsyncTask
private class MyAsyncTask extends AsyncTask {
@Override
protected Object doInBackground(Object... arg0) {
////Execute the network related option here
}
}
Для получения дополнительной информации обратитесь на сайт разработчиков Android.
Различные варианты:
используйте обычный поток java runnable для обработки сетевой задачи и можете использовать runOnUIThread() для обновления пользовательского интерфейса
intentservice/async можно использовать, если вы хотите обновить интерфейс после получения ответа сети
Начиная с 2018 года, я бы рекомендовал использовать RxJava в Kotlin для сетевой выборки. Ниже приведен простой пример.
Single.fromCallable {
// Your Network Fetching Code
Network.fetchHttp(url)
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
// What you need to do with your result on the view
result -> view.updateScreen(result)
}
Никогда не выполняйте длительную работу с потоком пользовательского интерфейса, эта работа в течение длительного времени может быть связана с сервером, читать/записывать в файл и т.д. Эти задачи должны быть в фоновом потоке, поэтому Service
, AsyncTask
, Threads
создано. Вы можете отключить StrictMode
, чтобы предотвратить сбой, но это никогда не рекомендуется.
Я предлагаю вам воспользоваться преимуществом StrictMode
по крайней мере в режиме отладки. Используйте нижеприведенный код для получения журналов любой проблемы, которая замедляет ваше приложение в основном потоке.
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectAll()
.penaltyLog()
.build());
Вы можете установить разные штрафы -
penaltyLog() // to print log
penaltyDeath() // This will crash you App(so costly penalty)
penaltyDialog() // Show alert when something went lazy on Main thread
Существует так много https://developer.android.com/reference/android/os/StrictMode.html
Используйте приведенный ниже код для выполнения тяжелых задач.
// Your package here
import java.util.List;
import org.apache.http.NameValuePair;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.os.AsyncTask;
import android.view.View.OnSystemUiVisibilityChangeListener;
public class AsyncRequest extends AsyncTask<String, Integer, String> {
Context context;
ProgressDialog pDialog;
// Three Constructors
public AsyncRequest(Activity a, String m, List<NameValuePair> p) {
context = a;
method = m;
parameters = p;
}
public AsyncRequest(Activity a) {
this.caller = (OnAsyncRequestComplete) a;
context = a;
}
public String doInBackground(String... urls) {
//Perform your task here
return result;
}
public void onPreExecute() {
pDialog = new ProgressDialog(context);
pDialog.setMessage("Please wait..");
pDialog.setCancelable(false);
pDialog.show();
}
public void onProgressUpdate(Integer... progress) {
// You can implement some progressBar and update it in this record.
// setProgressPercent(progress[0]);
}
public void onPostExecute(String response) {
if (pDialog != null && pDialog.isShowing()) {
pDialog.dismiss();
}
// Get the result here
}
protected void onCancelled(String response) {
if (pDialog != null && pDialog.isShowing()) {
pDialog.dismiss();
}
}
}
Вам нужно просто добавить следующую строку в manifest.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);
}
Android не разрешает отдельный процесс в основном потоке активности, а HTTP-соединение - это независимый поток. Именно по этой причине вы получаете " android.os.NetworkOnMainThreadException".
Может возникнуть необходимость, когда вы хотите проверить фактическое подключение к Интернету, прежде чем показывать веб-браузер пользователю, потому что, если нет Интернета, веб-просмотр покажет пользователю ошибку, которая не была найдена, что обычно не выполняется что показать.
Для проверки доступности Интернета можно использовать команду ping, но в случае подключения Wi-Fi на сервере Wi-Fi можно отключить, поэтому в этом случае вы используете HTTP-соединение для проверки состояния запроса.
Это может быть правильный подход, если вы проверяете свой собственный URL-адрес веб-просмотра, прежде чем показывать веб-браузер пользователю. В этом случае вы можете использовать строгий режим Android, но не разрешаете всю политику, потому что вам это не нужно.
Вы должны предоставить политику разрешений сети только для строгого режима. Просто добавьте строку ниже в свой код, и вы не получите эту ошибку.
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitNetwork().build();
StrictMode.setThreadPolicy(policy);