질문자 :bejoy george
RssReader용 Android 프로젝트를 실행하는 동안 오류가 발생했습니다.
암호:
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는 API 레벨 30에서 더 이상 사용되지 않습니다.
비동기 작업 | 안드로이드 개발자
이 예외는 응용 프로그램이 기본 스레드에서 네트워킹 작업을 수행하려고 할 때 throw됩니다. 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"/>
Michael Spector거의 항상 스레드에서 또는 비동기 작업으로 네트워크 작업을 실행해야 합니다.
그러나이 제한을 제거하고 결과를 받아 들일 경우 당신이 기본 동작을 재정의 할 수 있습니다.
추가하다:
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build(); StrictMode.setThreadPolicy(policy);
당신의 수업에서,
그리고
Android manifest.xml 파일에 다음 권한을 추가합니다.
<uses-permission android:name="android.permission.INTERNET"/>
결과:
귀하의 앱은 (인터넷 연결이 불안정한 영역에서) 응답하지 않고 잠기고, 사용자는 속도 저하를 감지하고 강제 종료를 수행해야 하며, 활동 관리자가 귀하의 앱을 종료하고 사용자에게 앱이 중지되었음을 알리는 위험을 감수해야 합니다.
Android에는 응답성을 위한 설계를 위한 좋은 프로그래밍 방법에 대한 몇 가지 유용한 팁이 있습니다. NetworkOnMainThreadException | 안드로이드 개발자
user1169115Thread
사용하여 이 문제를 해결했습니다.
Thread thread = new Thread(new Runnable() { @Override public void run() { try { //Your code goes here } catch (Exception e) { e.printStackTrace(); } } }); thread.start();
Dr.Luiji허용되는 답변에는 몇 가지 중요한 단점이 있습니다. 당신이 무엇을 하고 있는지 정말로 알지 못한다면 네트워킹을 위해 AsyncTask를 사용하는 것은 바람직하지 않습니다. 일부 단점은 다음과 같습니다.
- 비정적 내부 클래스로 생성된 AsyncTask에는 둘러싸는 Activity 개체, 해당 컨텍스트 및 해당 활동에 의해 생성된 전체 View 계층 구조에 대한 암시적 참조가 있습니다. 이 참조는 AsyncTask의 백그라운드 작업이 완료될 때까지 Activity가 가비지 수집되는 것을 방지합니다. 사용자의 연결이 느리거나 다운로드가 큰 경우 이러한 단기 메모리 누수가 문제가 될 수 있습니다. 예를 들어 방향이 여러 번 변경되고 실행 중인 작업을 취소하지 않는 경우 또는 사용자가 활동에서 멀리 탐색합니다.
- AsyncTask는 실행하는 플랫폼에 따라 실행 특성이 다릅니다. API 레벨 4 이전에는 AsyncTask가 단일 백그라운드 스레드에서 직렬로 실행됩니다. API 레벨 4에서 API 레벨 10까지 AsyncTask는 최대 128개의 스레드 풀에서 실행됩니다. API 레벨 11부터 AsyncTask는 단일 백그라운드 스레드에서 직렬로 실행됩니다(오버로드된
executeOnExecutor
메서드를 사용하고 대체 실행기를 제공하지 않는 한). ICS에서 직렬로 실행할 때 제대로 작동하는 코드는 예를 들어 의도하지 않은 실행 순서 종속성이 있는 경우 Gingerbread에서 동시에 실행될 때 중단될 수 있습니다.
단기 메모리 누수를 피하고 모든 플랫폼에서 실행 특성이 잘 정의되어 있으며 강력한 네트워크 처리를 구축할 수 있는 기반이 있는 경우 다음을 고려할 수 있습니다.
- 이 작업을 잘 수행하는 라이브러리 사용 - 이 질문 에 네트워킹 라이브러리에 대한 좋은 비교가 있습니다.
-
onActivityResult
메서드를 통해 결과를 반환하기 PendingIntent
와 함께 Service
또는 IntentService
대신 사용합니다.
IntentService 접근 방식
단점:
-
AsyncTask
보다 더 많은 코드와 복잡성을 가지고 있지만 생각만큼 많지는 않습니다. - 요청을 대기열에 넣고 단일 백그라운드 스레드에서 실행합니다. 당신은 쉽게 교체하여이를 제어 할 수 있습니다
IntentService
동등한와 Service
아마도처럼 구현 이 하나 . - 음, 지금은 사실 다른 사람이 생각나지 않아
장점:
- 단기 메모리 누수 문제 방지
- 네트워크 작업이 진행 중인 동안 활동이 다시 시작되면
onActivityResult
메서드를 통해 다운로드 결과를 계속 수신할 수 있습니다. - AsyncTask보다 강력한 네트워킹 코드를 빌드하고 재사용하는 더 나은 플랫폼입니다. 예: 중요한 업로드를 수행해야 하는
Activity
AsyncTask
에서 수행할 수 있지만 사용자가 전화를 받기 위해 앱을 컨텍스트 전환 하면 업로드가 완료되기 전에 시스템이 앱을 종료할 수 있습니다. Service
있는 응용 프로그램을 종료 할 가능성 이 적습니다. - 자신의 동시 버전의
IntentService
(위에 링크된 것과 같은)를 사용하는 경우 Executor
를 통해 동시성 수준을 제어할 수 있습니다.
구현 요약
IntentService
를 구현하여 단일 백그라운드 스레드에서 매우 쉽게 다운로드를 수행할 수 있습니다.
1단계: IntentService
를 생성하여 다운로드를 수행합니다. Intent
extras를 통해 무엇을 다운로드할지 알려주고 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 reuse, in the case where more than one intent is queued parser = new IllustrativeRSSParser(); } @Override protected void onHandleIntent(Intent intent) { PendingIntent reply = intent.getParcelableExtra(PENDING_RESULT_EXTRA); InputStream in = null; try { try { URL url = new URL(intent.getStringExtra(URL_EXTRA)); IllustrativeRSS rss = parser.parse(in = url.openStream()); Intent result = new Intent(); result.putExtra(RSS_RESULT_EXTRA, rss); reply.send(this, RESULT_CODE, result); } catch (MalformedURLException exc) { reply.send(INVALID_URL_CODE); } catch (Exception exc) { // could do better by treating the different sax/xml exceptions individually reply.send(ERROR_CODE); } } catch (PendingIntent.CanceledException exc) { Log.i(TAG, "reply cancelled", exc); } } }
2단계: 매니페스트에 서비스를 등록합니다.
<service android:name=".DownloadIntentService" android:exported="false"/>
3단계: 서비스가 결과를 반환하는 데 사용할 PendingResult 개체를 전달하여 활동에서 서비스를 호출합니다.
PendingIntent pendingResult = createPendingResult( RSS_DOWNLOAD_REQUEST_CODE, new Intent(), 0); Intent intent = new Intent(getApplicationContext(), DownloadIntentService.class); intent.putExtra(DownloadIntentService.URL_EXTRA, URL); intent.putExtra(DownloadIntentService.PENDING_RESULT_EXTRA, pendingResult); startService(intent);
4단계: onActivityResult에서 결과 처리:
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == RSS_DOWNLOAD_REQUEST_CODE) { switch (resultCode) { case DownloadIntentService.INVALID_URL_CODE: handleInvalidURL(); break; case DownloadIntentService.ERROR_CODE: handleError(data); break; case DownloadIntentService.RESULT_CODE: handleRSS(data); break; } handleRSS(data); } super.onActivityResult(requestCode, resultCode, data); }
완벽하게 작동하는 Android Studio/ Gradle 프로젝트가 포함된 GitHub 프로젝트는 여기에서 사용할 수 있습니다.
StevieHoneycomb 의 UI 스레드에서는 네트워크 I/O 를 수행할 수 없습니다. 기술적으로, 그것은 안드로이드의 이전 버전에서 가능하지만, 그것은 앱이 응답하지 않을 것이고, 나쁘게 행동하고 대한 귀하의 응용 프로그램을 죽이는 OS 초래할 수 있으므로 정말 나쁜 생각입니다. 백그라운드 프로세스를 실행하거나 AsyncTask를 사용하여 백그라운드 스레드에서 네트워크 트랜잭션을 수행해야 합니다.
Android 개발자 사이트에 이에 대한 좋은 소개인 Painless Threading 에 대한 기사가 있으며 여기에서 현실적으로 제공할 수 있는 것보다 훨씬 더 깊이 있는 답변을 제공할 것입니다.
Mark Allison이 문제에 대한 두 가지 해결책이 있습니다.
기본 UI 스레드에서 네트워크 호출을 사용하지 마십시오. 이를 위해 비동기 작업을 사용하십시오.
다음 코드를 MainActivity 파일에 setContentView(R.layout.activity_main); :
if (android.os.Build.VERSION.SDK_INT > 9) { StrictMode.ThreadPolicy 정책 = new StrictMode.ThreadPolicy.Builder().permitAll().build(); StrictMode.setThreadPolicy(정책); }
그리고 아래 import 문을 Java 파일로 가져옵니다.
import android.os.StrictMode;
Dipak Keshariya다른 스레드에서 네트워크 작업을 수행합니다.
예를 들어:
new Thread(new Runnable(){ @Override public void run() { // Do network action in this function } }).start();
그리고 이것을 AndroidManifest.xml 파일에 추가하십시오.
<uses-permission android:name="android.permission.INTERNET"/>
henry4343- strictMode를 사용하지 마십시오(디버그 모드에서만).
- SDK 버전을 변경하지 마십시오.
- 별도의 실을 사용하지 마십시오.
서비스 또는 AsyncTask 사용
스택 오버플로 질문도 참조하십시오.
Android에서 이메일을 보내는 android.os.NetworkOnMainThreadException
venergiac다음 코드를 사용하여 엄격 모드를 비활성화합니다.
if (android.os.Build.VERSION.SDK_INT > 9) { StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build(); StrictMode.setThreadPolicy(policy); }
이것은 권장하지 않습니다 AsyncTask
인터페이스를 사용하십시오.
두 방법 모두에 대한 전체 코드
Piyush-Ask Any Difference네트워크 기반 작업은 기본 스레드에서 실행할 수 없습니다. 자식 스레드에서 모든 네트워크 기반 작업을 실행하거나 AsyncTask를 구현해야 합니다.
다음은 자식 스레드에서 작업을 실행하는 방법입니다.
new Thread(new Runnable(){ @Override public void run() { try { // Your implementation goes here } catch (Exception ex) { ex.printStackTrace(); } } }).start();
Dhruv Jindal코드를 다음 안에 넣으십시오.
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 } }
Vaishali Sutariya이것은 Android 3.0 이상에서 발생합니다. Android 3.0 이상부터 네트워크 작업(인터넷에 액세스하는 기능)을 사용하여 기본 스레드/UI 스레드(활동의 on create 및 on resume 메서드에서 생성되는 항목)에서 실행하는 것을 제한했습니다.
이는 네트워크 작업에 별도의 스레드를 사용하도록 권장하기 위한 것입니다. 네트워크 활동을 올바르게 수행하는 방법에 대한 자세한 내용은 AsyncTask를 참조하세요.
raihan ahmedAndroid 주석 사용은 옵션입니다. 백그라운드 스레드에서 모든 메서드를 간단히 실행할 수 있습니다.
// normal method private void normal() { doSomething(); // do something in background } @Background protected void doSomething() // run your networking code here }
단순성과 가독성의 이점을 제공하지만 단점이 있습니다.
Oleksiy이 오류는 메인 스레드에서 장기 실행 작업을 실행하기 때문에 발생합니다. 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 } });
Ashwin S Ashok네트워크 작업, 파일 I/O 또는 SQLite 데이터베이스 작업과 같이 시간이 많이 걸리는 작업을 메인 스레드(UI 스레드)에서 수행해서는 안 됩니다. 따라서 이러한 작업을 위해서는 작업자 스레드를 생성해야 하지만 작업자 스레드에서 UI 관련 작업을 직접 수행할 수 없다는 것이 문제입니다. 이를 위해 Handler
Message
전달해야 합니다.
이 모든 것을 단순화하기 위해 Android는 AsyncTask
, AsyncTaskLoader
, CursorLoader
또는 IntentService
와 같은 다양한 방법을 제공합니다. 따라서 요구 사항에 따라 이들 중 하나를 사용할 수 있습니다.
Kapil Vatsspektom의 최고 답변은 완벽하게 작동합니다.
AsyncTask
인라인으로 작성하고 클래스로 확장하지 않고 그 위에 AsyncTask
에서 응답을 가져와야 하는 경우 아래와 같이 get()
메서드를 사용할 수 있습니다.
RSSFeed feed = new RetreiveFeedTask().execute(urlToRssFeed).get();
(그의 예에서.)
sivag1이것은 Honeycomb SDK 이상을 대상으로 하는 애플리케이션에 대해서만 발생합니다. 이전 SDK 버전을 대상으로 하는 애플리케이션은 메인 이벤트 루프 스레드에서 네트워킹을 수행할 수 있습니다.
오류는 SDK 경고입니다!
perry저에게는 이랬습니다.
<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="10" />
내 앱을 테스트한 기기는 SDK 버전 16인 4.1.2였습니다!
대상 버전이 Android 대상 라이브러리와 동일한지 확인하십시오. 대상 라이브러리가 무엇인지 확실하지 않은 경우 프로젝트 -> 빌드 경로 -> Android 를 마우스 오른쪽 버튼으로 클릭하면 체크되어 있어야 합니다.
또한 다른 사람들이 언급했듯이 인터넷에 액세스할 수 있는 올바른 권한을 포함하십시오.
<uses-permission android:name="android.permission.INTERNET"/>
rharvey귀하의 활동에서 이것을 사용하십시오
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(); } });
dhiraj kakran뭔가를 명시적으로 설명하기 위해:
기본 스레드는 기본적으로 UI 스레드입니다.
따라서 메인 스레드에서 네트워킹 작업을 수행할 수 없다는 것은 UI 스레드에서 네트워킹 작업을 수행할 수 없다는 것을 의미합니다. 즉, 다른 스레드 내부 *runOnUiThread(new Runnable() { ... }*
블록에서 네트워킹 작업을 수행할 수 없음을 의미합니다. , 어느 하나.
(저는 메인 스레드가 아닌 다른 곳에서 왜 그 오류가 발생하는지 알아내느라 머리를 긁는 긴 시간을 보냈습니다. 이것이 이유였습니다. 이 스레드가 도움이 되었으며 이 댓글이 다른 사람에게 도움이 되기를 바랍니다.)
Novak이 예외는 수행하는 작업에 너무 많은 시간 이 소요되는 경우 주 스레드에서 수행된 무거운 작업으로 인해 발생합니다.
이를 피하기 위해 스레드 또는 실행자를 사용하여 처리할 수 있습니다.
Executors.newSingleThreadExecutor().submit(new Runnable() { @Override public void run() { // You can perform your task here. } });
amardeep이 질문에 대한 훌륭한 답변이 이미 많이 있지만 해당 답변이 게시된 이후로 많은 훌륭한 라이브러리가 출시되었습니다. 이것은 일종의 초보자 가이드로 의도되었습니다.
내가 네트워크 운영과 솔루션을 각 두 가지를 수행하기위한 몇 가지 사용 사례를 다룰 것입니다.
HTTP를 통한 REST
일반적으로 JSON이지만 XML 또는 다른 것일 수 있습니다.
전체 API 액세스
사용자가 주가, 이자율 및 환율을 추적할 수 있는 앱을 작성한다고 가정해 보겠습니다. 다음과 같은 JSON API를 찾습니다.
http://api.example.com/stocks // ResponseWrapper<String> object containing a // list of strings 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에 탁월한 선택이며 Amazon Ion Java 또는 Volley (웹사이트: Retrofit )와 같은 다른 라이브러리에서와 같이 개별적으로 코딩할 필요 없이 REST 엔드포인트를 선언할 수 있습니다.
금융 API와 함께 사용하는 방법은 무엇입니까?
파일 build.gradle
모듈 수준 build.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); }
Class FinancesApiBuilder
public class FinancesApiBuilder { public static FinancesApi build(String baseUrl){ return new Retrofit.Builder() .baseUrl(baseUrl) .addConverterFactory(GsonConverterFactory.create()) .build() .create(FinancesApi.class); } }
클래스 FinancesFragment 스니펫
FinancesApi api = FinancesApiBuilder.build("http://api.example.com/"); //trailing '/' required for predictable behavior api.getStock("INTC").enqueue(new Callback<Stock>(){ @Override public void onResponse(Call<Stock> stockCall, Response<Stock> stockResponse){ Stock stock = stockCall.body(); // Do something with the stock } @Override public void onResponse(Call<Stock> stockCall, Throwable t){ // Something bad happened } }
API가 API 키 또는 사용자 토큰과 같은 다른 헤더를 전송해야 하는 경우 Retrofit을 사용하면 이를 쉽게 수행할 수 있습니다( 자세한 내용은 Retrofit에 헤더 매개변수 추가에 대한 이 멋진 답변 참조).
일회성 REST API 액세스
사용자의 GPS 위치를 찾고 해당 지역의 현재 온도를 확인하고 기분을 알려주는 "기분 날씨" 앱을 구축한다고 가정해 보겠습니다. 이 유형의 앱은 API 엔드포인트를 선언할 필요가 없습니다. 하나의 API 엔드포인트에 액세스할 수만 있으면 됩니다.
이온
이것은 이러한 유형의 액세스를 위한 훌륭한 라이브러리입니다.
'android.os.NetworkOnMainThreadException'을 어떻게 고칠 수 있습니까?에 대한 msysmilu의 훌륭한 답변 을 읽으십시오. .
HTTP를 통해 이미지 로드
발리
Volley는 REST API에도 사용할 수 있지만 더 복잡한 설정이 필요하기 때문에 위와 같이 Square에서 Retrofit을 사용하는 것을 선호합니다.
소셜 네트워킹 앱을 구축 중이고 친구의 프로필 사진을 로드하려고 한다고 가정해 보겠습니다.
파일 build.gradle
모듈 수준 build.gradle 파일에 다음 줄을 추가합니다.
implementation 'com.android.volley:volley:1.0.0'
파일 ImageFetch.java
발리는 Retrofit보다 더 많은 설정이 필요합니다. RequestQueue, ImageLoader 및 ImageCache를 설정하려면 다음과 같은 클래스를 생성해야 하지만 그렇게 나쁘지는 않습니다.
public class ImageFetch { private static ImageLoader imageLoader = null; private static RequestQueue imageQueue = null; public static ImageLoader getImageLoader(Context ctx){ if(imageLoader == null){ if(imageQueue == null){ imageQueue = Volley.newRequestQueue(ctx.getApplicationContext()); } imageLoader = new ImageLoader(imageQueue, new ImageLoader.ImageCache() { Map<String, Bitmap> cache = new HashMap<String, Bitmap>(); @Override public Bitmap getBitmap(String url) { return cache.get(url); } @Override public void putBitmap(String url, Bitmap bitmap) { cache.put(url, bitmap); } }); } return imageLoader; } }
파일 user_view_dialog.xml
레이아웃 XML 파일에 다음을 추가하여 이미지를 추가합니다.
<com.android.volley.toolbox.NetworkImageView android:id="@+id/profile_picture" android:layout_width="32dp" android:layout_height="32dp" android:layout_alignParentTop="true" android:layout_centerHorizontal="true" app:srcCompat="@android:drawable/spinner_background"/>
파일 UserViewDialog.java
onCreate 메서드(Fragment, Activity) 또는 생성자(Dialog)에 다음 코드를 추가합니다.
NetworkImageView profilePicture = view.findViewById(R.id.profile_picture); profilePicture.setImageUrl("http://example.com/users/images/profile.jpg", ImageFetch.getImageLoader(getContext());
피카소
Picasso 는 Square의 또 다른 훌륭한 도서관입니다. 몇 가지 좋은 예를 보려면 웹 사이트를 참조하십시오.
KG6ZVP간단히 말해서,
UI 스레드에서 네트워크 작업을 하지 마십시오.
예를 들어 HTTP 요청을 수행하는 경우 이는 네트워크 작업입니다.
해결책:
- 새 스레드를 생성해야 합니다.
- 또는 AsyncTask 클래스를 사용하십시오.
방법:
모든 작품을 안에 넣어
- 새 스레드의
run()
- 또는 AsyncTask 클래스의
doInBackground()
메서드입니다.
하지만:
네트워크 응답에서 무언가를 얻고 이를 보기에 표시하려면(예: TextView의 응답 메시지 표시) UI 스레드 로 다시 돌아가야 합니다.
그렇게 하지 않으면 ViewRootImpl$CalledFromWrongThreadException
합니다.
어떻게
-
onPostExecute()
메서드에서 보기를 업데이트합니다. - 또는
runOnUiThread()
메소드를 호출하고 run()
메소드 내부의 뷰를 업데이트하십시오.
Nabin코드의 일부를 다른 스레드로 이동하여 main thread
스레드를 오프로드하고 ANR , NetworkOnMainThreadException , IllegalStateException 발생을 방지할 수 있습니다(예: 오랜 시간 동안 잠재적으로 UI를 잠글 수 있기 때문에 메인 스레드의 데이터베이스에 액세스할 수 없음) .
상황에 따라 선택해야 하는 몇 가지 접근 방식이 있습니다.
Java 스레드 또는 Android HandlerThread :
Java 스레드는 일회용이며 실행 메소드를 실행한 후 종료됩니다.
HandlerThread는 루퍼가 있는 새 스레드를 시작하기 위한 편리한 클래스입니다.
AsyncTask (API 레벨 30에서 더 이상 사용되지 않음)
AsyncTask 는 Thread 및 Handler 주변의 도우미 클래스로 설계되었으며 일반 스레딩 프레임워크를 구성하지 않습니다. AsyncTask는 짧은 작업(최대 몇 초)에 이상적으로 사용해야 합니다. 스레드를 장기간 실행해야 하는 경우 다음과 같은 java.util.concurrent 패키지에서 제공하는 다양한 API를 사용하는 것이 좋습니다. Executor , ThreadPoolExecutor 및 FutureTask .
메인 쓰레드가 UI 컴포넌트를 독점하기 때문에 일부 View에 접근할 수 없기 때문에 Handler가 구출됩니다.
[실행자 프레임워크]
스레드 풀(예: 코어 풀 크기, 최대 풀 크기, 연결 유지 시간 등)에 대한 미세 제어를 제공하는 ExecutorService를 구현하는 ThreadPoolExecutor 클래스
ScheduledThreadPoolExecutor - ThreadPoolExecutor를 확장하는 클래스입니다. 주어진 지연 후 또는 주기적으로 작업을 예약할 수 있습니다.
퓨처태스크
FutureTask는 비동기 처리를 수행하지만 결과가 아직 준비되지 않았거나 처리가 완료되지 않은 경우 get()을 호출하면 스레드가 차단됩니다.
AsyncTaskLoader
AsyncTask에 내재된 많은 문제를 해결하는 AsyncTaskLoader
인텐트 서비스
이것은 Android에서 장기 실행 처리를 위한 사실상의 선택입니다. 좋은 예는 대용량 파일을 업로드하거나 다운로드하는 것입니다. 사용자가 앱을 종료하더라도 업로드 및 다운로드는 계속될 수 있으며 이러한 작업이 진행되는 동안 사용자가 앱을 사용할 수 없도록 차단하고 싶지는 않습니다.
작업 스케줄러
효과적으로 서비스를 생성하고 언제 서비스를 실행할지에 대한 기준을 지정하는 JobInfo.Builder를 사용하여 작업을 생성해야 합니다.
Rx자바
관찰 가능한 시퀀스를 사용하여 비동기 및 이벤트 기반 프로그램을 구성하기 위한 라이브러리입니다.
코루틴 (코틀린)
그것의 주요 요지는 비동기 코드를 동기 코드처럼 보이게 만든다는 것입니다.
자세히보기 여기 , 여기 , 여기 , 그리고 여기 .
yoAlex5새로운 Thread
및 AsyncTask 솔루션은 이미 설명했습니다.
AsyncTask
는 짧은 작업에 이상적으로 사용해야 합니다. Normal Thread
는 Android에 적합하지 않습니다.
HandlerThread 및 Handler를 사용하는 대체 솔루션 살펴보기
핸들러 스레드
루퍼가 있는 새 스레드를 시작하기 위한 편리한 클래스입니다. 그런 다음 루퍼를 사용하여 처리기 클래스를 만들 수 있습니다. start()
는 여전히 호출되어야 합니다.
매니저:
핸들러를 사용하면 스레드의 MessageQueue와 연결된 Message 및 Runnable 개체를 보내고 처리할 수 있습니다. 각 Handler 인스턴스는 단일 스레드 및 해당 스레드의 메시지 대기열과 연결됩니다. 새로운 Handler를 생성할 때, 그것을 생성하는 쓰레드의 쓰레드/메시지 대기열에 바인딩됩니다. -- 그 시점부터 메시지와 실행 파일을 해당 메시지 대기열로 전달하고 메시지에서 나올 때 실행합니다. 대기 줄.
해결책:
HandlerThread
생성
HandlerThread
에서 start()
호출
HanlerThread
에서 Looper
를 가져와서 Handler
생성
Runnable
개체에 네트워크 작업 관련 코드 포함
Handler
Runnable
작업 제출
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
접근 방식을 사용하면 Handler
HandlerThread
에 많은 네트워크 작업(Runnable 작업)을 제출할 수 있습니다.
Ravindra babu위에 거대한 솔루션 풀이 있지만 아무도 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 } });
msysmilu메인 쓰레드는 UI 쓰레드이며, 메인 쓰레드에서는 사용자 상호작용을 차단할 수 있는 작업을 할 수 없습니다. 두 가지 방법으로 이 문제를 해결할 수 있습니다.
다음과 같이 메인 스레드에서 강제로 작업을 수행합니다.
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);
자세한 내용은 다음을 확인하십시오. 무통 스레딩
Sharath kumarRxAndroid
는 이 문제에 대한 또 다른 더 나은 대안이며 스레드를 생성한 다음 Android UI 스레드에 결과를 게시하는 번거로움을 덜어줍니다.
작업을 실행해야 하고 모든 것이 내부적으로 처리되는 스레드를 지정하기만 하면 됩니다.
Observable<List<String>> musicShowsObservable = Observable.fromCallable(new Callable<List<String>>() { @Override public List<String> call() { return mRestClient.getFavoriteMusicShows(); } }); mMusicShowSubscription = musicShowsObservable .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer<List<String>>() { @Override public void onCompleted() { } @Override public void onError(Throwable e) { } @Override public void onNext(List<String> musicShows) { listMusicShows(musicShows); } });
(Schedulers.io())
를 지정하면 RxAndroid는 다른 스레드에서 getFavoriteMusicShows()
AndroidSchedulers.mainThread()
를 사용하여 UI 스레드에서 이 Observable을 관찰하기를 원합니다. 즉, UI 스레드에서 onNext()
콜백이 호출되기를 원합니다.
Shinoo Goyal이 문제를 해결하는 또 다른 매우 편리한 방법이 있습니다. 바로 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() 스케줄러에 대한 지원을 포함합니다.
RxJava에 대한 훌륭한 전자책이 있습니다 .
Alex Shutov이것은 작동합니다. 방금 Dr.Luiji의 답변 을 조금 더 간단하게 만들었습니다.
new Thread() { @Override public void run() { try { //Your code goes here } catch (Exception e) { e.printStackTrace(); } } }.start();
Kacy출처 : http:www.stackoverflow.com/questions/6343166/how-can-i-fix-android-os-networkonmainthreadexception