Wystąpił błąd podczas uruchamiania projektu Android dla RssReader.
Kod:
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();
I pokazuje poniższy błąd:
android.os.NetworkOnMainThreadException
Jak mogę rozwiązać ten problem?
java
android
android-networking
networkonmainthread
raduj się z george
źródło
źródło
Odpowiedzi:
Ten wyjątek jest zgłaszany, gdy aplikacja próbuje wykonać operację sieciową w swoim głównym wątku. Uruchom swój kod w
AsyncTask
:Jak wykonać zadanie:
W
MainActivity.java
pliku możesz dodać ten wiersz do swojejoncreate()
metodyNie zapomnij dodać tego do
AndroidManifest.xml
pliku:źródło
AsyncTask
i dba o zmianę konfiguracji.Prawie zawsze powinieneś uruchamiać operacje sieciowe na wątku lub jako zadanie asynchroniczne.
Ale tak jest możliwe, aby usunąć to ograniczenie i zastąpić domyślne zachowanie, jeśli jesteś w stanie zaakceptować konsekwencje.
Dodaj:
W Twojej klasie,
i
DODAJ to uprawnienie w pliku manifestu.xml Androida:
Konsekwencje:
Twoja aplikacja (w obszarach nieregularnego połączenia internetowego) przestanie reagować i zostanie zablokowana, użytkownik odczuwa powolność i musi wykonać wymuszone zabójstwo, a Ty ryzykujesz, że menedżer aktywności zabije aplikację i poinformuje użytkownika, że aplikacja się zatrzymała.
Android ma kilka dobrych wskazówek na temat dobrych praktyk programowania w zakresie projektowania responsywności: http://developer.android.com/reference/android/os/NetworkOnMainThreadException.html
źródło
doInBackground
deklaracja, 2 nawiasy zamykające i wywołanie doexecute()
). Z drugiej strony, nawet jedno pobranie z witryny, jak wspomniałeś, powoduje znaczne opóźnienie w reakcji interfejsu użytkownika. Nie bądź leniwy.Rozwiązałem ten problem za pomocą nowego
Thread
.źródło
Przyjęta odpowiedź ma pewne wady. Nie zaleca się używania AsyncTask do pracy w sieci, chyba że naprawdę wiesz, co robisz. Niektóre wady obejmują:
executeOnExecutor
metody i podasz alternatywny executor). Kod, który działa poprawnie, gdy działa szeregowo na ICS, może ulec uszkodzeniu, gdy jest wykonywany jednocześnie na Piernik, powiedzmy, jeśli masz niezamierzone zależności od kolejności wykonania.Jeśli chcesz uniknąć krótkotrwałych wycieków pamięci, mieć dobrze zdefiniowane cechy wykonania na wszystkich platformach i mieć bazę do zbudowania naprawdę solidnej obsługi sieci, możesz rozważyć:
Service
lubIntentService
zamiast, być może z a,PendingIntent
aby zwrócić wynik za pomocąonActivityResult
metody działania.Podejście IntentService
Wady:
AsyncTask
, choć nie tak bardzo, jak mogłoby się wydawaćIntentService
równoważnąService
implementacją, być może taką jak ta .Zalety:
onActivityResult
metodyAsyncTask
w sposóbActivity
, ale jeśli użytkownik przełączy kontekst z aplikacją do podjęcia rozmowy telefonicznej, system może zabić aplikację zanim zakończy wysyłania. Jest mniej prawdopodobne, że zabije aplikację z aktywnymService
.IntentService
(takiej jak ta, którą zamieściłem powyżej), możesz kontrolować poziom współbieżności za pośrednictwemExecutor
.Podsumowanie wdrożenia
Możesz
IntentService
dość łatwo zaimplementować opcję pobierania w jednym wątku w tle.Krok 1: Utwórz,
IntentService
aby wykonać pobieranie. Możesz powiedzieć, co pobrać za pośrednictwemIntent
dodatkowych, i przekazać to,PendingIntent
aby użyć, aby zwrócić wynik doActivity
:Krok 2: Zarejestruj usługę w manifeście:
Krok 3: Wywołaj usługę z działania, przekazując obiekt PendingResult, którego usługa użyje do zwrócenia wyniku:
Krok 4: Obsługa wyniku w funkcji onActivityResult:
Projekt Github zawierający kompletny działający projekt Android-Studio / Gradle jest dostępny tutaj .
źródło
IllustrativeRSS
? Co jeśli nie pracujesz z materiałami RSS?AsyncTask
. Błąd miał na celu powstrzymanie deweloperów przed opóźnieniem interfejsu użytkownika, niekoniecznie zachęcając ich do ryzykowania bezpieczeństwa za pomocą szeregu intencji / usług.Nie można wykonać operacji we / wy sieci w wątku interfejsu użytkownika w programie Honeycomb . Technicznie jest to możliwe we wcześniejszych wersjach Androida, ale jest to naprawdę zły pomysł, ponieważ spowoduje, że Twoja aplikacja przestanie odpowiadać i może spowodować, że system operacyjny zabije twoją aplikację za złe zachowanie. Musisz uruchomić proces w tle lub użyć AsyncTask, aby wykonać transakcję sieciową w wątku w tle.
Na stronie dewelopera Androida jest artykuł o Bezbolesnym wątku, który jest dobrym wstępem do tego i zapewni o wiele lepszą głębię odpowiedzi, niż można realistycznie podać tutaj.
źródło
Użyj usługi lub AsyncTask
Zobacz także pytanie Przepełnienie stosu:
android.os.NetworkOnMainThreadException wysyłanie wiadomości e-mail z Androida
źródło
Wykonaj akcje sieciowe w innym wątku
I dodaj to do AndroidManifest.xml
źródło
Wyłącz tryb ścisły za pomocą następującego kodu:
Nie jest to zalecane : użyj
AsyncTask
interfejsu.Pełny kod dla obu metod
źródło
Operacje sieciowe nie mogą być uruchamiane w głównym wątku. Musisz uruchomić wszystkie zadania sieciowe w wątku potomnym lub zaimplementować AsyncTask.
Oto jak uruchamiasz zadanie w wątku potomnym:
źródło
Umieść swój kod w środku:
Lub:
źródło
Dzieje się tak w Androidzie 3.0 i nowszych. W Androidzie 3.0 i nowszych ograniczyły korzystanie z operacji sieciowych (funkcji dostępu do Internetu) przed uruchomieniem w głównym wątku / wątku interfejsu użytkownika (co pojawia się podczas tworzenia i wznawiania metod w działaniu).
Ma to na celu zachęcenie do używania osobnych wątków do operacji sieciowych. Zobacz AsyncTask, aby uzyskać więcej informacji na temat wykonywania działań sieciowych we właściwy sposób.
źródło
Korzystanie z adnotacji na Androida jest opcją. Umożliwi to po prostu uruchomienie dowolnej metody w wątku w tle:
Należy pamiętać, że chociaż zapewnia zalety prostoty i czytelności, ma swoje wady.
źródło
Błąd wynika z wykonywania długotrwałych operacji w głównym wątku. Problem można łatwo rozwiązać za pomocą AsynTask lub Thread . Możesz pobrać tę bibliotekę AsyncHTTPClient dla lepszej obsługi.
źródło
Nie powinieneś wykonywać żadnych czasochłonnych zadań w głównym wątku (wątku interfejsu użytkownika), takich jak operacje sieciowe, operacje we / wy pliku lub operacje na bazie danych SQLite. W przypadku tego rodzaju operacji należy utworzyć wątek roboczy, ale problem polega na tym, że nie można bezpośrednio wykonać żadnej operacji związanej z interfejsem użytkownika z wątku roboczego. W tym celu musisz użyć
Handler
i przekazaćMessage
.Aby uprościć wszystkie te rzeczy, Android zapewnia różne sposoby, jak
AsyncTask
,AsyncTaskLoader
,CursorLoader
lubIntentService
. Możesz więc użyć dowolnego z nich zgodnie ze swoimi wymaganiami.źródło
Najlepsza odpowiedź spektom działa idealnie.
Jeśli piszesz
AsyncTask
inline, a nie rozszerzasz jako klasę, a ponadto, jeśli istnieje potrzeba uzyskania odpowiedzi zAsyncTask
, możesz użyćget()
metody opisanej poniżej.(Z jego przykładu.)
źródło
get()
jest złym pomysłem ... powoduje, że AsyncTask ponownie „synchronizuje”Jest to generowane tylko w przypadku aplikacji kierowanych na zestaw SDK o strukturze plastra miodu lub nowszy. Aplikacje kierowane na wcześniejsze wersje SDK mogą tworzyć sieci w swoich głównych wątkach pętli zdarzeń.
Błąd to ostrzeżenie SDK!
źródło
Dla mnie było to tak:
Urządzenie, na którym testowałem moją aplikację, to 4.1.2, czyli SDK w wersji 16!
Upewnij się, że wersja docelowa jest taka sama jak Twoja biblioteka docelowa Androida. Jeśli nie masz pewności, jaka jest twoja biblioteka docelowa, kliknij prawym przyciskiem myszy swój Projekt -> Ścieżka kompilacji -> Android i powinna być zaznaczona.
Ponadto, jak wspomnieli inni, obejmują prawidłowe uprawnienia dostępu do Internetu:
źródło
NetworkOnMainThreadException
czy Strażnik mówi ci: nie strzelaj do własnej stopy ... Twoim rozwiązaniem jest: wróćmy do przeszłości, gdy nie było Strażnika - teraz mogę strzelać do mojego pieszo swobodnieWykorzystaj to w swojej działalności
źródło
Aby wyrazić coś wprost:
Główny wątek to w zasadzie wątek interfejsu użytkownika.
Mówienie, że nie można wykonywać operacji sieciowych w głównym wątku oznacza, że nie można wykonywać operacji sieciowych w wątku interfejsu użytkownika, co oznacza, że nie można również wykonywać operacji sieciowych w
*runOnUiThread(new Runnable() { ... }*
bloku wewnątrz innego wątku.(Właśnie miałem długi moment, kiedy próbowałem zrozumieć, dlaczego dostaję ten błąd gdzieś indziej niż mój główny wątek. Właśnie dlatego; ten wątek pomógł; i mam nadzieję, że ten komentarz pomoże komuś innemu.)
źródło
Ten wyjątek występuje z powodu każdego ciężkiego zadania wykonanego w głównym wątku, jeśli wykonanie tego zadania zajmuje zbyt dużo czasu .
Aby tego uniknąć, możemy sobie z tym poradzić przy użyciu wątków lub programów wykonawczych
źródło
Istnieje wiele wspaniałych odpowiedzi na to pytanie, ale od czasu opublikowania tych odpowiedzi pojawiło się wiele świetnych bibliotek. Jest to przeznaczone jako rodzaj przewodnika dla początkujących.
Omówię kilka przypadków użycia do wykonywania operacji sieciowych i do roztworu lub dwa dla każdego.
ReST przez HTTP
Zazwyczaj Json może być XML lub czymś innym
Pełny dostęp do interfejsu API
Załóżmy, że piszesz aplikację, która pozwala użytkownikom śledzić ceny akcji, stopy procentowe i kursy walut. Znajdziesz Json API, który wygląda mniej więcej tak:
Modernizacja z Square
Jest to doskonały wybór dla interfejsu API z wieloma punktami końcowymi i pozwala zadeklarować punkty końcowe ReST zamiast kodować je indywidualnie, jak w przypadku innych bibliotek, takich jak ion lub Volley. (strona internetowa: http://square.github.io/retrofit/ )
Jak korzystać z interfejsu API finansów?
build.gradle
Dodaj następujące wiersze do poziomu modułu buid.gradle:
FinancesApi.java
FinanseApiBuilder
Fragment fragmentu finansów
Jeśli Twój interfejs API wymaga wysłania klucza API lub innego nagłówka, takiego jak token użytkownika itp., Retrofit ułatwia to (zobacz tę niesamowitą odpowiedź, aby uzyskać szczegółowe informacje: https://stackoverflow.com/a/42899766/1024412 ).
Jednorazowy dostęp do interfejsu API ReST
Załóżmy, że budujesz aplikację „nastrojowa pogoda”, która sprawdza lokalizację GPS użytkowników, sprawdza aktualną temperaturę w tym obszarze i informuje ich o nastroju. Ten typ aplikacji nie musi deklarować punktów końcowych interfejsu API; po prostu musi mieć dostęp do jednego punktu końcowego interfejsu API.
Jon
To świetna biblioteka dla tego typu dostępu.
Przeczytaj wspaniałą odpowiedź msysmilu ( https://stackoverflow.com/a/28559884/1024412 )
Załaduj obrazy przez HTTP
Salwa
Volley może być również użyty dla interfejsów API ReST, ale z powodu bardziej skomplikowanej konfiguracji wolę używać Retrofit z Square jak wyżej ( http://square.github.io/retrofit/ )
Załóżmy, że tworzysz aplikację społecznościową i chcesz załadować zdjęcia profilowe znajomych.
build.gradle
Dodaj tę linię do poziomu modułu buid.gradle:
ImageFetch.java
Siatkówka wymaga większej konfiguracji niż modernizacja. Będziesz musiał stworzyć taką klasę, aby skonfigurować RequestQueue, ImageLoader i ImageCache, ale nie jest tak źle:
user_view_dialog.xml
Dodaj następujące elementy do pliku XML układu, aby dodać obraz:
UserViewDialog.java
Dodaj następujący kod do metody onCreate (fragment, działanie) lub konstruktora (okno dialogowe):
Picasso
Kolejna doskonała biblioteka z Square. Proszę zobaczyć na stronie kilka świetnych przykładów: http://square.github.io/picasso/
źródło
W prostych słowach
NIE PRACUJ W SIECI W NICI UI
Na przykład, jeśli wykonasz żądanie HTTP, jest to działanie sieciowe.
Rozwiązanie:
Droga:
Umieść wszystkie swoje prace w środku
run()
metoda nowego wątkudoInBackground()
metoda klasy AsyncTask.Ale:
Gdy otrzymasz coś z odpowiedzi sieciowej i chcesz pokazać go w swoim widoku (np. Komunikat odpowiedzi wyświetlacza w TextView), musisz wrócić do wątku interfejsu użytkownika .
Jeśli tego nie zrobisz, dostaniesz
ViewRootImpl$CalledFromWrongThreadException
.Jak?
onPostExecute()
metodyrunOnUiThread()
metodę i aktualizuj widok wewnątrzrun()
metody.źródło
Możesz przenieść część kodu do innego wątku, aby odciążyć
main thread
i uniknąć uzyskania ANR , NetworkOnMainThreadException , IllegalStateException (np. Nie można uzyskać dostępu do bazy danych w głównym wątku, ponieważ może to potencjalnie zablokować interfejs użytkownika na długi okres czasu).Istnieje kilka metod, które należy wybrać w zależności od sytuacji
Wątek Java lub Android HandlerThread
AsyncTask
Implementacja puli wątków ThreadPoolExecutor , ScheduledThreadPoolExecutor ...
FutureTask
AsyncTaskLoaders
IntentService
JobScheduler
RxJava
Coroutines (Kotlin)
Czytaj więcej tutaj , tutaj , tutaj , tutaj
źródło
Chociaż powyżej znajduje się ogromna pula rozwiązań, nikt nie wspominał
com.koushikdutta.ion
: https://github.com/koush/ionJest również asynchroniczny i bardzo prosty w użyciu:
źródło
Nowe
Thread
i AsyncTask zostały już wyjaśnione.AsyncTask
idealnie nadaje się do krótkich operacji. NormalnyThread
nie jest preferowany dla Androida.Zobacz alternatywne rozwiązanie za pomocą HandlerThread i Handler
HandlerThread
Treser:
Rozwiązanie:
Stwórz
HandlerThread
zadzwoń
start()
naHandlerThread
Twórz
Handler
, pobierającLooper
zHanlerThread
Osadź kod związany z operacją sieci w
Runnable
obiekciePrześlij
Runnable
zadanie doHandler
Przykładowy fragment kodu, którego adres
NetworkOnMainThreadException
Zalety stosowania tego podejścia:
Thread/AsyncTask
dla każdej operacji sieciowej jest kosztowne.Thread/AsyncTask
Zostaną zniszczone i ponownie tworzone dla kolejnych operacji sieciowych. Ale za pomocąHandler
iHandlerThread
podejście można przesłać wiele operacji sieciowych (jako zadania Runnable) do pojedynczegoHandlerThread
za pomocąHandler
.źródło
RxAndroid
jest kolejną lepszą alternatywą dla tego problemu i oszczędza nam kłopotów z tworzeniem wątków, a następnie publikowaniem wyników w wątku interfejsu użytkownika Androida. Musimy tylko określić wątki, na których zadania muszą zostać wykonane, a wszystko jest obsługiwane wewnętrznie.Przez specifiying
(Schedulers.io())
, RxAndroid będzie działaćgetFavoriteMusicShows()
na innym wątku.Za pomocą
AndroidSchedulers.mainThread()
tego chcemy obserwować ten Obserable w wątku interfejsu użytkownika, tzn. Chcemy, aby naszeonNext()
wywołanie zwrotne było wywoływane w wątku interfejsu użytkownikaźródło
Główny wątek jest wątkiem interfejsu użytkownika i nie można wykonać operacji w głównym wątku, która może zablokować interakcję użytkownika. Możesz to rozwiązać na dwa sposoby:
Wymuś wykonanie zadania w głównym wątku, takim jak ten
Lub stwórz prostą procedurę obsługi i zaktualizuj główny wątek, jeśli chcesz.
Aby zatrzymać wątek, użyj:
Aby uzyskać więcej informacji, sprawdź to: Bezbolesne nawlekanie
źródło
To działa. Po prostu uprościłem odpowiedź Dr.Luiji.
źródło
W systemie Android operacji sieciowych nie można uruchamiać w głównym wątku. Do wykonywania operacji sieciowych można używać Thread, AsyncTask (zadania krótkotrwałe), Service (zadania długoterminowe).
źródło
Dostęp do zasobów sieciowych z głównego wątku (UI) powoduje ten wyjątek. Użyj osobnego wątku lub AsyncTask, aby uzyskać dostęp do zasobu sieciowego, aby uniknąć tego problemu.
źródło