Вы отправляете JSON, как это
$.ajax(url, {
data : JSON.stringify(myJSObject),
contentType : 'application/json',
type : 'POST',
...
, если вы передаете объект как settings.data jQuery будет преобразовывать его в параметры запроса и по умолчанию отправлять с помощью приложения типа данных / x-www-form- urlencoded; charset = UTF-8, возможно, не то, что вы хотите
Это исключение возникает, когда приложение пытается выполнить сетевую операцию в своем основном потоке. Запустите свой код в 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()
method
new RetrieveFeedTask().execute(urlToRssFeed);
Не забудьте добавить это в файл 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
.
Ошибка связана с выполнением длительных операций в основном потоке. Вы можете легко устранить проблему, используя 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
}
});
Простыми словами,
НЕ ДЕЛАЙТЕ СЕТЕВЫЕ РАБОТЫ В РЕЖИМЕ UI
Например, если вы выполняете HTTP-запрос, это сетевое действие.
Решение:
Путь:
Поместите все ваши работы внутри
run()
метода нового потока doInBackground()
метода класса AsyncTask. Но:
Когда вы получаете что-то из ответа сети и хотите показать его на своем экране (например, сообщение с ответом на отображение в TextView), вам нужно вернуться обратно в пользовательский интерфейс thread.
Если вы этого не сделаете, вы получите ViewRootImpl$CalledFromWrongThreadException
.
Как?
onPostExecute()
runOnUiThread()
и обновите внутри метода run()
. get()
- плохая идея ... это делает AsyncTask & sync & quot; еще раз
– Selvin
17 September 2013 в 16:29
IllustrativeRSS
? Что делать, если вы не работаете с материалами RSS?
– Cullub
14 April 2018 в 19:13
**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();
}
});
Вы должны почти всегда запускать сетевые операции в потоке или как асинхронную задачу.
Но это является возможным для удаления этого ограничения, и вы переопределяете поведение по умолчанию, если вы готовы принять последствия.
Добавить:
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
В вашем классе
и
ДОБАВЛЯЕТ это разрешение в android Файл manifest.xml:
<uses-permission android:name="android.permission.INTERNET"/>
Последствия:
Ваше приложение (в зонах пятнистого интернет-соединения) перестает отвечать на запросы и блокируется, пользователь воспринимает медленность и должен делать force kill, и вы рискуете, что менеджер активности убил ваше приложение и сказал пользователю, что приложение остановилось.
У Android есть несколько хороших советов по хорошему программированию для разработки для реагирования: http: // developer.android.com/reference/android/os/NetworkOnMainThreadException.html
doInBackground
, 2 закрывающие скобки и вызов execute()
). С другой стороны, даже один выбор из сайта, как вы упоминаете, приводит к значительному отставанию в пользовательском интерфейсе. Не ленитесь.
– Zoltán
4 September 2012 в 10:39
Выполнять сетевые действия в другом потоке
Пример:
blockquote>new Thread(new Runnable(){ @Override public void run() { // Do network action in this function } }).start();
И добавить это в AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET"/>
Это работает. Просто сделал ответ доктора Льюиджи немного проще.
new Thread() {
@Override
public void run() {
try {
//Your code goes here
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
На этот вопрос уже много замечательных ответов, но с тех пор, как эти ответы были опубликованы, появилось много замечательных библиотек.
Я рассмотрю несколько вариантов использования сетевых операций и a решение или два для каждого.
Обычно 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 вместо того, чтобы кодировать их отдельно, как с другими библиотеками, такими как ион или волейбол. (веб-сайт: 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);
}
}
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
}
}
Если ваш API требует, чтобы API-ключ или другой заголовок, такой как токен пользователя и т. д., отправлялся, Retrofit делает это проще (см. этот удивительный ответ для деталей: https://stackoverflow.com/a/42899766/1024412 ).
Предположим, вы создавая приложение «погода настроения», которое просматривает местоположение GPS-пользователей и проверяет текущую температуру в этой области и сообщает им настроение. Для этого типа приложения не требуется объявлять конечные точки API; он просто должен иметь доступ к одной конечной точке API.
Это отличная библиотека для этого типа доступа.
Пожалуйста, прочитайте отличный ответ msysmilu ( https://stackoverflow.com/a/28559884/1024412 )
Volley также может использоваться для API-интерфейсов ReST, но из-за более сложной настройки я предпочитаю использовать Retrofit from Square, как указано выше ( http://square.github.io/retrofit/ )
Предположим, вы создаете приложение для социальных сетей и хотите загружать фотографии друзей друзей.
build.gradle
Добавьте эту строку в свой модуль level buid.gradle:
implementation 'com.android.volley:volley:1.0.0'
ImageFetch.java
Волейбол требует больше настроек, чем дооснащение. Вам нужно будет создать такой класс, чтобы настроить RequestQueue, ImageLoader и ImageCache, но это не так уж плохо:
public class ImageFetch {
private static ImageLoader imageLoader = null;
private static RequestQueue imageQueue = null;
public static ImageLoader getImageLoader(Context ctx){
if(imageLoader == null){
if(imageQueue == null){
imageQueue = Volley.newRequestQueue(ctx.getApplicationContext());
}
imageLoader = new ImageLoader(imageQueue, new ImageLoader.ImageCache() {
Map<String, Bitmap> cache = new HashMap<String, Bitmap>();
@Override
public Bitmap getBitmap(String url) {
return cache.get(url);
}
@Override
public void putBitmap(String url, Bitmap bitmap) {
cache.put(url, bitmap);
}
});
}
return imageLoader;
}
}
user_view_dialog.xml
Добавить следующее в ваш XML-файл макета для добавления изображения:
<com.android.volley.toolbox.NetworkImageView
android:id="@+id/profile_picture"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
app:srcCompat="@android:drawable/spinner_background"/>
UserViewDialog.java
Добавьте следующий код в метод onCreate (Фрагмент, Активность) или конструктор (Диалог):
NetworkImageView profilePicture = view.findViewById(R.id.profile_picture);
profilePicture.setImageUrl("http://example.com/users/images/profile.jpg", ImageFetch.getImageLoader(getContext());
Еще одна отличная библиотека с площади. Пожалуйста, посетите сайт для некоторых замечательных примеров: http://square.github.io/picasso/
Хотя выше есть огромный пул решений, никто не упомянул com.koushikdutta.ion
: https://github.com/koush/ion
Он также асинхронный и очень простой для использования:
Ion.with(context)
.load("http://example.com/thing.json")
.asJsonObject()
.setCallback(new FutureCallback<JsonObject>() {
@Override
public void onCompleted(Exception e, JsonObject result) {
// do stuff with the result or error
}
});
Просто для того, чтобы четко указать что-то явно:
Основной поток - это в основном поток пользовательского интерфейса.
Так что, говоря, что вы не можете выполнять сетевые операции в основном потоке, вы не можете создавать сети операции в потоке пользовательского интерфейса, что означает , вы не можете выполнять сетевые операции в блоке *runOnUiThread(new Runnable() { ... }*
внутри некоторого другого потока.
(у меня был только длинный момент царапин на голове пытаясь понять, почему я получал эту ошибку где-то, кроме моего основного потока. Вот почему, этот поток помог, и, надеюсь, этот комментарий поможет кому-то другому.)
Использование Аннотации Android - это опция. Это позволит вам просто запустить любой метод в фоновом потоке:
// normal method
private void normal() {
doSomething(); // do something in background
}
@Background
protected void doSomething()
// run your networking code here
}
Обратите внимание, что, хотя он обеспечивает преимущества простоты и удобочитаемости, у него есть свои недостатки.
Существует еще один очень удобный способ решения этой проблемы - использовать возможности параллелизма rxJava. Вы можете выполнить любую задачу в фоновом режиме и опубликовать результаты в основном потоке очень удобным способом, поэтому эти результаты будут переданы цепочке обработки.
Первый проверенный ответ - использование AsynTask. Да, это решение, но сейчас оно устарело, потому что вокруг есть новые инструменты.
String getUrl() {
return "SomeUrl";
}
private Object makeCallParseResponse(String url) {
return null;
//
}
private void processResponse(Object o) {
}
Метод getUrl предоставляет URL-адрес, и он будет выполнен в основном потоке.
makeCallParseResponse (..) - делает фактическую работу
processResponse (..) - обрабатывает результат по основному потоку.
Код для асинхронного выполнения будет выглядеть так:
rx.Observable.defer(new Func0<rx.Observable<String>>() {
@Override
public rx.Observable<String> call() {
return rx.Observable.just(getUrl());
}
})
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.map(new Func1<String, Object>() {
@Override
public Object call(final String s) {
return makeCallParseResponse(s);
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Object>() {
@Override
public void call(Object o) {
processResponse(o);
}
},
new Action1<Throwable>() {
@Override
public void call(Throwable throwable) {
// Process error here, it will be posted on
// the main thread
}
});
По сравнению с AsyncTask этот метод позволяет переключать планировщики произвольным числом раз (скажем, извлекать данные на одном планировщике и обрабатывать эти данные на другом (скажем, Scheduler.computation ()). также можно определить собственные планировщики.
Чтобы использовать эту библиотеку, включите следующие строки в файл build.gradle:
compile 'io.reactivex:rxjava:1.1.5'
compile 'io.reactivex:rxandroid:1.2.0'
Последняя зависимость включает в себя поддержку .mainThread ().
Существует отличная книга g0 для rx-java .
NetworkOnMainThreadException
- Хранитель, который говорит вам: не стреляйте в свою собственную ногу ... ваше решение: вернемся к прошлому, когда не было Хранителя - теперь я могу стрелять в мою ногу свободно
– Selvin
17 September 2013 в 16:36
Новые Thread
и решения AsyncTask уже объяснены.
AsyncTask
в идеале следует использовать для коротких операций. Обычный Thread
не является предпочтительным для Android.
Посмотрите альтернативное решение, используя HandlerThread и Обработчик
HandlerThread
Handy класс для запуска нового потока, который имеет петлитель. Затем петлитель можно использовать для создания классов обработчиков. Обратите внимание, что еще нужно вызвать
start()
.Обработчик:
Обработчик позволяет отправлять и обрабатывать объекты Message и Runnable, связанные с MessageQueue потока , Каждый экземпляр Handler связан с одним потоком и очередью сообщений этого потока. Когда вы создаете нового обработчика, он привязан к очереди потоков / сообщений потока, который его создает - с этой точки он будет доставлять сообщения и исполняемые файлы в очередь сообщений и выполнять их по мере их выхода из сообщения
Решение:
- Создать
HandlerThread
- Вызов
start()
наHandlerThread
- Создайте
Handler
, получивLooper
изHanlerThread
- Вставьте код связанного с вашей сетью объекта
Runnable
- Отправьте задание
Runnable
наHandler
Пример фрагмента кода, адрес
NetworkOnMainThreadException
HandlerThread handlerThread = new HandlerThread("URLConnection"); handlerThread.start(); handler mainHandler = new Handler(handlerThread.getLooper()); Runnable myRunnable = new Runnable() { @Override public void run() { try { Log.d("Ravi", "Before IO call"); URL page = new URL("http://www.google.com"); StringBuffer text = new StringBuffer(); HttpURLConnection conn = (HttpURLConnection) page.openConnection(); conn.connect(); InputStreamReader in = new InputStreamReader((InputStream) conn.getContent()); BufferedReader buff = new BufferedReader(in); String line; while ( (line = buff.readLine()) != null) { text.append(line + "\n"); } Log.d("Ravi", "After IO call"); Log.d("Ravi",text.toString()); }catch( Exception err){ err.printStackTrace(); } } }; mainHandler.post(myRunnable);
Плюсы использования этого подхода:
- Создание нового
Thread/AsyncTask
для каждая операция сети стоит дорого.Thread/AsyncTask
будет уничтожен и повторно создан для следующих сетевых операций. Но с подходамиHandler
иHandlerThread
вы можете отправить множество сетевых операций (как выполняемых задач) в одиночныйHandlerThread
с помощьюHandler
.
Я решил эту проблему, используя новый Thread
.
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
//Your code goes here
} catch (Exception e) {
e.printStackTrace();
}
}
});
thread.start();
Верхний ответ spektom работает идеально.
Если вы пишете AsyncTask
inline и не распространяетесь как класс, и, кроме того, если есть необходимо получить ответ из AsyncTask
, можно использовать метод get()
, как показано ниже.
RSSFeed feed = new RetreiveFeedTask().execute(urlToRssFeed).get();
(Из его примера.)
get()
- плохая идея ... это делает AsyncTask & sync & quot; еще раз
– Selvin
17 September 2013 в 16:29
IllustrativeRSS
? Что делать, если вы не работаете с материалами RSS?
– Cullub
14 April 2018 в 19:13
Вам не разрешено выполнять сетевые операции в потоке пользовательского интерфейса на 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. Он будет работать как шарм. :)
NetworkOnMainThreadException
- Хранитель, который говорит вам: не стреляйте в свою собственную ногу ... ваше решение: вернемся к прошлому, когда не было Хранителя - теперь я могу стрелять в мою ногу свободно
– Selvin
17 September 2013 в 16:36
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 в потоке UI, т. е. хотим, чтобы наш обратный вызов onNext()
вызывался в потоке пользовательского интерфейса