Jak naprawić Androida.os.NetworkOnMainThreadException?
Dostałem błąd podczas uruchamiania mojego 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?
30 answers
Ten wyjątek jest wyrzucany, gdy aplikacja próbuje wykonać operację sieciową w swoim głównym wątku. Uruchom swój kod w 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
}
}
Jak wykonać zadanie:
W pliku MainActivity.java
możesz dodać tę linię w swojej metodzie oncreate()
new RetrieveFeedTask().execute(urlToRssFeed);
Nie zapomnij dodać tego do AndroidManifest.xml
pliku:
<uses-permission android:name="android.permission.INTERNET"/>
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-28 11:57:55
Prawie zawsze należy uruchamiać operacje sieciowe w wątku lub jako zadanie asynchroniczne.
Ale jest możliwe usunięcie tego ograniczenia i nadpisanie domyślnego zachowania, jeśli chcesz zaakceptować konsekwencje.
Dodaj:
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
W twojej klasie,
I
Dodaj to uprawnienie w manifeście Androida.plik xml:
<uses-permission android:name="android.permission.INTERNET"/>
Konsekwencje:
Twoja aplikacja (w obszarach spotty połączenia z Internetem) stanie się użytkownik odczuwa powolność i musi zabić siłą, a ty ryzykujesz, że menedżer aktywności zabije Twoją aplikację i poinformuje użytkownika, że aplikacja została zatrzymana.
Android ma kilka dobrych wskazówek na temat dobrych praktyk programistycznych, aby zaprojektować responsywność: http://developer.android.com/reference/android/os/NetworkOnMainThreadException.html
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-11 14:18:49
Rozwiązałem ten problem używając nowego Thread
.
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
//Your code goes here
} catch (Exception e) {
e.printStackTrace();
}
}
});
thread.start();
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-05 09:42:24
Nie można wykonać sieci I/Ow wątku UI na Honeycomb. Technicznie rzecz biorąc, to jest możliwe we wcześniejszych wersjach Androida, ale jest to naprawdę zły pomysł, ponieważ spowoduje to, że 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ć funkcji AsyncTask, aby wykonać transakcję sieciową w wątku w tle.
Jest artykuł o bezbolesne Gwintowanie na Androida strona dewelopera, która jest dobrym wstępem do tego, i zapewni Ci znacznie lepszą głębię odpowiedzi, niż można realistycznie podać tutaj.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-09 16:25:22
Przyjęta odpowiedź ma kilka znaczących stron w dół. Nie jest wskazane, aby używać AsyncTask do sieci, chyba że naprawdę wiesz, co robisz. Niektóre z down-boków to:
- AsyncTask utworzone jako niestatyczne klasy wewnętrzne mają ukryte odniesienie do otaczającego obiektu Activity, jego kontekstu i całej hierarchii widoków utworzonej przez tę aktywność. To odniesienie zapobiega zbieraniu śmieci do momentu, w którym AsyncTask będzie w tle praca zakończona. Jeśli połączenie użytkownika jest wolne i / lub pobieranie jest Duże, te krótkotrwałe wycieki pamięci mogą stanowić problem - na przykład jeśli orientacja zmienia się kilka razy (i nie anulujesz wykonywanych zadań) lub użytkownik odchodzi od aktywności.
- AsyncTask ma różne właściwości wykonawcze w zależności od platformy, na której jest wykonywany: przed poziomem API 4 AsyncTask wykonuje się seryjnie na jednym wątku tła; od poziomu API 4 do poziomu API 10, AsyncTasks wykonuje na puli do 128 wątków; począwszy od poziomu API 11 AsyncTask wykonuje seryjnie na jednym wątku tła (chyba że użyjesz przeciążonej metody
executeOnExecutor
i dostarczysz alternatywnego executora). Kod, który działa dobrze, gdy jest uruchamiany seryjnie na ICS, może się zepsuć, gdy jest wykonywany jednocześnie na Gingerbread, powiedzmy, jeśli masz nieumyślne zależności od kolejności wykonania.
Jeśli chcesz uniknąć wycieków pamięci krótkotrwałej, miej dobrze zdefiniowaną charakterystykę wykonania we wszystkich platformy i mają podstawę do zbudowania naprawdę solidnej obsługi sieci, warto rozważyć: {]}
-
W tym kontekście warto zwrócić uwagę na to, że biblioteki sieciowe są w pełni kompatybilne z bibliotekami sieciowymi, które są w pełni kompatybilne z bibliotekami sieciowymi.]}
- używając zamiast tego
Service
lubIntentService
, być może za pomocąPendingIntent
, aby zwrócić wynik za pomocą metodyonActivityResult
działania.
IntentService approach
Dół-boki:
- więcej kodu i złożoności niż
AsyncTask
, choć nie tak bardzo, jak myślisz - ustawia żądania w kolejce i uruchamia je na pojedynczym wątku w tle. Można to łatwo kontrolować, zastępując
IntentService
równoważną implementacjąService
, Być może jak ta . - Um, I can ' t think of any other right actually
Up-sides:
- unika problemu wycieku pamięci krótkotrwałej
- jeśli Twoja aktywność zostanie uruchomiona ponownie, gdy operacje sieciowe są w locie, nadal może otrzymać wynik pobierania za pomocą metody
onActivityResult
- lepsza Platforma niż AsyncTask do budowania i ponownego wykorzystania solidnego kodu sieciowego. Przykład: jeśli musisz wykonać ważne przesłanie, możesz to zrobić z
AsyncTask
WActivity
, ale jeśli kontekst użytkownika wyłącza się z aplikacji, aby wykonać połączenie telefoniczne, system Może zabić aplikację przed zakończeniem przesyłania. Jest mniej prawdopodobne, aby zabić aplikację z aktywnymService
. - jeśli używasz własnej współbieżnej wersji
IntentService
(jak ten, który podlinkowałem powyżej) możesz kontrolować poziom współbieżności za pomocąExecutor
.
Podsumowanie realizacji
Możesz zaimplementować IntentService
, aby dość łatwo pobierać pliki w jednym wątku tła.
Krok 1: Utwórz IntentService
, aby wykonać pobieranie. Możesz powiedzieć, co pobrać za pomocą Intent
extra ' s, i przekazać go PendingIntent
, aby użyć do powrotu wyniku do 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);
}
}
}
Krok 2: Zarejestruj usługę w "manifest": {]}
<service
android:name=".DownloadIntentService"
android:exported="false"/>
Krok 3: wywołaj usługę z aktywności, przekazując obiekt PendingResult, którego usługa użyje do zwrócenia wyniku:
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);
Krok 4: obsłuż wynik w 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);
}
Projekt github zawierający kompletny działający projekt Android-Studio/gradle jest dostępny tutaj .
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:44
- nie używaj strictMode (tylko w trybie debugowania)
- nie zmieniaj wersji SDK
- nie używaj osobnego wątku
Użyj usługi lub AsyncTask
Zobacz też pytanie o przepełnienie stosu:
android.os.NetworkOnMainThreadException wysyłanie wiadomości e-mail z Androida
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:26:38
Wykonaj akcje sieciowe na innym wątku
Na Przykład:
new Thread(new Runnable(){
@Override
public void run() {
// Do network action in this function
}
}).start();
I dodaj to do AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET"/>
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-13 19:49:55
Wyłączasz tryb ścisły używając następującego kodu:
if (android.os.Build.VERSION.SDK_INT > 9) {
StrictMode.ThreadPolicy policy =
new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
}
Nie jest to zalecane : Użyj interfejsu AsyncTask
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-20 09:04:29
Operacje sieciowe nie mogą być uruchamiane w głównym wątku. Musisz uruchomić wszystkie zadania sieciowe w wątku podrzędnym lub zaimplementować AsyncTask.
Tak uruchamiasz zadanie w wątku potomnym:
new Thread(new Runnable(){
@Override
public void run() {
try {
// Your implementation goes here
}
catch (Exception ex) {
ex.printStackTrace();
}
}
}).start();
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-22 21:08:38
Wpisz swój kod w środku:
new Thread(new Runnable(){
@Override
public void run() {
try {
// Your implementation
}
catch (Exception ex) {
ex.printStackTrace();
}
}
}).start();
Lub:
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
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-22 21:13:15
// normal method
private void normal() {
doSomething(); // do something in background
}
@Background
protected void doSomething()
// run your networking code here
}
Zauważ, że chociaż zapewnia zalety prostoty i czytelności, ma swoje wady.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-30 16:46:11
To się dzieje w Androidzie 3.0 i nowszych. Od Androida 3.0 i nowszych, mają ograniczone korzystanie z operacji sieciowych (funkcje, które uzyskują dostęp do Internetu) od uruchomienia w głównym wątku / wątku UI (co powstaje z Twoich metod on create i on CV w aktywności).
Ma to zachęcić do używania oddzielnych wątków do operacji sieciowych. Zobacz AsyncTask Aby uzyskać więcej informacji na temat prawidłowego wykonywania działań sieciowych.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-09 16:27:30
Nie powinieneś wykonywać żadnych czasochłonnych zadań na głównym wątku (wątku UI), takich jak operacje sieciowe, wejścia/wyjścia plików lub operacje bazy danych SQLite. Tak więc 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 należy użyć Handler
i przekazać Message
.
Aby uprościć te wszystkie rzeczy, Android zapewnia różne sposoby, takie jak AsyncTask
, AsyncTaskLoader
, CursorLoader
lub IntentService
. Więc możesz użyć dowolnego z nich zgodnie z Twoimi wymaganiami.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-09 16:29:41
[[4]}top odpowiedź spektom działa idealnie.
Jeśli piszesz AsyncTask
inline i nie rozszerzasz jako klasa, a ponadto, jeśli istnieje potrzeba uzyskania odpowiedzi z AsyncTask
, można użyć metody get()
, Jak Poniżej.
RSSFeed feed = new RetreiveFeedTask().execute(urlToRssFeed).get();
(z jego przykładu.)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:47:29
Błąd jest spowodowany wykonywaniem długich operacji w głównym wątku,możesz łatwo naprawić problem za pomocą Asyntask lub Thread . Możesz sprawdzić tę bibliotekę AsyncHTTPClient dla lepszej obsługi.
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
}
});
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-07 04:53:07
Jest to rzucane tylko dla aplikacji ukierunkowanych na plaster miodu SDK lub wyższy. Aplikacje obsługujące wcześniejsze wersje SDK mogą tworzyć sieć w swoich głównych wątkach pętli zdarzeń.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-22 21:09:34
Dla mnie było tak:
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="10" />
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 Biblioteka docelowa Androida. Jeśli nie jesteś pewien, jaka jest twoja docelowa biblioteka, kliknij prawym przyciskiem myszy projekt - > buduj ścieżkę -> Android, i to powinien być ten, który jest zaznaczony.
Również, jak inni wspominali, Dołącz prawidłowe uprawnienia dostępu do Internetu:
<uses-permission android:name="android.permission.INTERNET"/>
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-22 21:07:28
Po prostu wymówić coś wprost:
Główny wątek to w zasadzie wątek interfejsu użytkownika.
Więc mówienie, że nie możesz wykonywać operacji sieciowych w głównym wątku oznacza, że nie możesz wykonywać operacji sieciowych w wątku UI, co oznacza, że nie możesz wykonywać operacji sieciowych w bloku *runOnUiThread(new Runnable() { ... }*
wewnątrz innego wątku.
(po prostu miałem długą chwilę drapania głową próbując dowiedzieć się, dlaczego dostaję ten błąd gdzieś indziej niż mój główny wątek. To dlaczego; ten wątek pomógł; i mam nadzieję, że ten komentarz pomoże komuś innemu.)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-24 21:32:53
Ten wyjątek występuje z powodu ciężkich zadań wykonywanych w głównym wątku, jeśli to zadanie zajmuje zbyt dużo czasu .
Aby tego uniknąć, możemy obsłużyć to za pomocą wątków lub executerów
Executors.newSingleThreadExecutor().submit(new Runnable() {
@Override
public void run() {
// You can perform your task here.
}
});
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-22 21:12:36
**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();
}
});
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-01 13:43:59
W prostych słowach,
NIE WYKONUJ PRACY SIECIOWEJ W WĄTKU UI
Na przykład, jeśli wykonasz żądanie HTTP, jest to akcja sieciowa.
Rozwiązanie:
- musisz utworzyć nowy wątek
- lub użyj klasy AsyncTask
Sposób:
Włóż wszystkie swoje prace do środka
-
run()
metoda nowego wątku -
lub
doInBackground()
Metoda AsyncTask klasy.
Ale:
Gdy otrzymasz coś z odpowiedzi sieci i chcesz pokazać to w widoku (np. wyświetl komunikat odpowiedzi w TextView), musisz wrócić z powrotem do wątku UI.
Jeśli tego nie zrobisz, dostanieszViewRootImpl$CalledFromWrongThreadException
.
Jak?
- podczas korzystania z AsyncTask, Aktualizuj widok z metody
onPostExecute()
-
lub wywołanie
runOnUiThread()
metoda i widok aktualizacji wewnątrz metodyrun()
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:10:45
Istnieje wiele świetnych odpowiedzi na to pytanie, ale wiele świetnych bibliotek pojawiło się od czasu opublikowania tych odpowiedzi. Jest to swego rodzaju przewodnik dla początkujących.
Omówię kilka przypadków użycia do wykonywania operacji sieciowych i rozwiązanie lub dwa dla każdego.
Odpoczynek przez HTTP
zazwyczaj Json, może być XML lub coś innego
Pełny dostęp do API
Załóżmy, że piszesz aplikację, która pozwala użytkownikom śledzić ceny akcji, stopy procentowe i kursy walut. Znajdziesz API Json, które wygląda mniej więcej tak:
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)
Modernizacja z kwadratu
Jest to doskonały wybór dla API z wieloma punktami końcowymi i pozwala zadeklarować pozostałe punkty końcowe zamiast konieczności kodowania ich indywidualnie, jak w przypadku innych bibliotek, takich jak ion lub Volley. (strona internetowa: http://square.github.io/retrofit/)
Jak to wykorzystać w finansach API?
Buduj.gradle
Dodaj te linie do buid poziomu modułu.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 snippet
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
}
}
Jeśli Twoje API wymaga klucza API lub innego nagłówka, takiego jak token użytkownika itp. aby zostać wysłanym, modernizacja ułatwia to (zobacz tę niesamowitą odpowiedź po szczegóły: https://stackoverflow.com/a/42899766/1024412).
Jednorazowy dostęp do ReST API
Załóżmy, że budujesz aplikację "Pogoda nastroju", która sprawdza lokalizację GPS użytkowników i sprawdza aktualną temperaturę w tym obszarze i informuje ich o nastroju. Tego typu aplikacja nie musi deklarować punktów końcowych API; musi tylko mieć dostęp do jednego punktu końcowego API.
Ion
Jest to świetna biblioteka dla tego typu dostępu.
Proszę przeczytać msysmilu ' s great odpowiedź ( https://stackoverflow.com/a/28559884/1024412 )
Ładowanie obrazów przez HTTP
Volley
Volley może być również używany do ReST API, ale ze względu na bardziej skomplikowaną wymaganą konfigurację wolę używać Retrofit z kwadratu jak wyżej (http://square.github.io/retrofit/)
Załóżmy, że budujesz aplikację społecznościową i chcesz załadować zdjęcia profilowe znajomych.
Buduj.gradle
Dodaj ten wiersz do Twój buid poziomu modułu.gradle:
implementation 'com.android.volley:volley:1.0.0'
ImageFetch.java
Volley wymaga więcej konfiguracji niż modernizacji. Będziesz musiał utworzyć taką klasę, aby skonfigurować RequestQueue, ImageLoader i ImageCache, ale nie jest tak źle: {]}
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
Dodaj następujący plik XML układu, aby dodać obraz:
<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
Dodaj następujący kod do metody onCreate (Fragment, Activity) lub konstruktor (Dialog):
NetworkImageView profilePicture = view.findViewById(R.id.profile_picture);
profilePicture.setImageUrl("http://example.com/users/images/profile.jpg", ImageFetch.getImageLoader(getContext());
Picasso
[[9]}kolejna wspaniała biblioteka od Square. Zajrzyj na stronę, aby zobaczyć kilka świetnych przykładów: http://square.github.io/picasso/Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-17 08:26:37
Chociaż powyżej jest ogromna pula rozwiązań, nikt nie wspomniał com.koushikdutta.ion
: https://github.com/koush/ion
Jest również asynchroniczny i bardzo prosty w użyciu:
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
}
});
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-17 10:31:31
To działa. Tylko uprościłem odpowiedź Dr. Luiji ' ego.
new Thread() {
@Override
public void run() {
try {
//Your code goes here
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-13 23:10:17
Na Androidzie operacje sieciowe nie mogą być uruchamiane w głównym wątku. Do wykonywania operacji sieciowych można użyć wątku, AsyncTask (zadania o krótkim czasie działania), Service (zadania o długim czasie działania).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-25 09:32:51
Dostęp do zasobów sieciowych z głównego wątku (UI) powoduje ten wyjątek. Aby uzyskać dostęp do zasobów sieciowych, użyj oddzielnego wątku lub funkcji AsyncTask, aby uniknąć tego problemu.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-25 09:35:31
RxAndroid
to kolejna lepsza alternatywa 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 Androida.
Musimy tylko określić wątki, na których mają być wykonywane zadania i wszystko jest obsługiwane wewnętrznie.
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);
}
});
Po określeniu
(Schedulers.io())
, RxAndroid uruchomigetFavoriteMusicShows()
w innym wątku.-
Używając
AndroidSchedulers.mainThread()
chcemy obserwować to, co obserwowalne w wątku UI, tzn. chcemy, aby nasze wywołanie zwrotneonNext()
zostało wywołane NA wątek UI
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-10 07:52:47
Nowe rozwiązania Thread
i AsyncTask zostały już wyjaśnione.
AsyncTask
idealnie nadaje się do krótkich operacji. Normal Thread
nie jest korzystne dla Androida.
Spójrz na alternatywne rozwiązanie za pomocą HandlerThread i Handler
HandlerThread
Poręczna Klasa do uruchamiania nowego wątku z looperem. Looper może być następnie użyty do tworzenia klas obsługi. Zauważ, że
start()
musi być dzwoniłem.
Obsługa pozwala na wysyłanie i przetwarzanie wiadomości i obiektów, które można uruchomić, związanych z wiadomością w wątku. Każda instancja obsługi jest powiązana z pojedynczym wątkiem i kolejką wiadomości tego wątku. Kiedy tworzysz nową funkcję obsługi, jest ona powiązana z kolejką wątków / wiadomości wątku, który ją tworzy - od tego momentu będzie ona dostarczać wiadomości i pliki uruchamiające do tej kolejki i wykonywać je w miarę ich wychodzenia kolejki komunikatów.
Rozwiązanie:
Utwórz
HandlerThread
Call
start()
onHandlerThread
Utwórz
Handler
pobierającLooper
zHanlerThread
-
Osadzenie kodu związanego z operacjami sieciowymi w obiekcie
Runnable
Zgłoś
Runnable
zadanie doHandler
Przykładowy fragment kodu, który Adres 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);
Zalety stosowania tego podejścia:
- Tworzenie nowego
Thread/AsyncTask
dla każda operacja sieciowa jest kosztowna.Thread/AsyncTask
zostanie zniszczony i ponownie utworzony dla następnych operacji sieciowych. Ale przy podejściuHandler
iHandlerThread
, można przesyłać wiele operacji sieciowych (jako zadania uruchamialne) do pojedynczegoHandlerThread
za pomocąHandler
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-05 04:41:03
Nie możesz zaimplementować operacji sieciowych w wątku interfejsu użytkownika na Androidzie. Będziesz musiał użyć klasy AsyncTask do wykonywania operacji związanych z siecią, takich jak wysyłanie żądania API, pobieranie obrazu z adresu URL itp. korzystając z metod wywołania zwrotnego AsyncTask, możesz uzyskać wynik w onPostExecute menthod i będziesz w wątku UI i możesz wypełnić UI danymi z serwisu internetowego lub coś w tym stylu.
Przykład: załóżmy, że chcesz pobrać obraz z adresu URL: https://www.samplewebsite.com/sampleimage.jpg
Rozwiązanie przy użyciu AsyncTask: są odpowiednio.
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);
}
}
}
Uwaga: nie zapomnij dodać uprawnienia do Internetu w pliku manifestu Androida. Będzie działać jak urok. :)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-25 09:39:03
Jest jeszcze jeden bardzo wygodny sposób rozwiązania tego problemu-użyj możliwości współbieżności rxJava. Możesz wykonać dowolne zadanie w tle i opublikować wyniki do głównego wątku w bardzo wygodny sposób, więc wyniki te zostaną przekazane do łańcucha przetwarzania.
Pierwszą zweryfikowaną odpowiedzią jest użycie AsynTask. Tak, jest to rozwiązanie, ale obecnie jest przestarzałe, ponieważ wokół są nowe narzędzia.
String getUrl() {
return "SomeUrl";
}
private Object makeCallParseResponse(String url) {
return null;
//
}
private void processResponse(Object o) {
}
Metoda getUrl podaje adres URL i zostanie wykonana na głównym wątku.
MakeCallParseResponse(..)- czy faktycznie działa
ProcessResponse(..)- będzie obsługiwać wynik na głównym wątku.
Kod wykonania asynchronicznego będzie wyglądał następująco:
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
}
});
W porównaniu do AsyncTask, ta metoda pozwala na przełączanie harmonogramów dowolną liczbę razy (powiedzmy, pobieranie danych z jednego harmonogramu i przetwarzanie tych danych na innym (powiedzmy, Scheduler.obliczenie ()). Możesz również zdefiniować własne harmonogramy.
Aby korzystać z tej biblioteki, Dołącz następujące linie do swojego kompilatora.plik gradle:
compile 'io.reactivex:rxjava:1.1.5'
compile 'io.reactivex:rxandroid:1.2.0'
Ostatnia zależność zawiera wsparcie dla .mainthread () scheduler.
Istnieje doskonały ebook dla rx-java .
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-25 09:48:34