'android.os.NetworkOnMainThreadException'를 수정하는 방법?


2394

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

이 문제를 어떻게 해결할 수 있습니까?


131
자세한 정보는 NetworkOnMainThreadException 에서이 블로그 게시물 읽으십시오 . Android 3.0 이상에서 왜 발생하는지 설명합니다.
Adrian Monk

6
라이트 트랙에 먼저 안드로이드의 네트워크 요청에 대해 읽은 다음 "Volley"를 공부하는 것이 좋습니다.
Anuj Sharma

3
이 문제를 해결하는 많은 대체 라이브러리가 있습니다. 이 페이지의 맨 아래에 많은 것이 나열되어 있습니다 . 더 많은 것을 얻으면, 우리는 그것들을 가져
옵니다

메인 (UI) 스레드와는 별도의 스레드에서 인터넷 활동을 실행해야합니다.
Naveed Ahmad

"이전 버전의 Android의 버그로 인해 시스템은 기본 스레드의 TCP 소켓에 쓰기를 엄격 모드 위반으로 플래그하지 않았습니다. Android 7.0은이 버그를 수정합니다.이 동작을 나타내는 앱은 이제 android.os를 발생시킵니다. NetworkOnMainThreadException. " -그래서 우리 중 일부는 최근까지 이것을 치지 않았습니다! developer.android.com/about/versions/nougat/…
Jay

답변:


2547

이 예외는 응용 프로그램이 기본 스레드에서 네트워킹 작업을 수행하려고 할 때 발생합니다. 다음에서 코드를 실행하십시오 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"/>

37
위의 코드 스 니펫은 하위 클래스 (내부 클래스), 바람직하게는 개인 클래스라고 가정 할 가치가 있다고 생각합니다. 이렇게하면 AsyncTask가 완료된 후에도 클래스의 내부를 조작 할 수 있습니다.
dyslexicanaboko

4
u는 위에서 언급하지만 m이 오류에 직면 같은 사실 저도 같은 일을했다 java.lang.RuntimeException가을 () Looper.prepare라고하지 스레드 내부 핸들러를 만들 수 없습니다
두식 Tyagi

68
이것은 정답이 아닙니다. 나는 사람들의 코드에서 이것을 항상 만나고, 항상 고쳐야하는 성가심. AsyncTask는 액티비티 라이프 사이클이 아니라 액티비티와 연결되어 있으므로 네트워크 액티비티에 사용해서는 안됩니다. 이 작업으로 기기를 회전하면 예외가 발생하고 앱이 중단됩니다. 대신 sqlite 데이터베이스에서 데이터를 삭제하는 IntentService를 사용하십시오.
Brill Pappin

5
주의, AsyncTask는 종종 활동 당 네트워크 작업에 사용되지 않아야 할 때 사용됩니다. 수명주기가 활동과 동기화되지 않았습니다. 데이터를 가져 오려면 IntentService와 뷰 뒤에있는 데이터베이스를 사용해야합니다.
Brill Pappin 2016 년

1
@BrillPappin, FWIW, 이 Android 개발자 안내서 는를 사용 AsyncTask하고 구성 변경을 처리합니다.
HeyJude

676

거의 항상 네트워크 작업을 스레드 또는 비동기 작업으로 실행해야합니다.

그러나 이다 이러한 제한을 제거하고 결과를 받아 들일 경우 당신이 기본 동작을 재정의 할 수 있습니다.

더하다:

StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();

StrictMode.setThreadPolicy(policy); 

수업 시간에

android manifest.xml 파일에서이 권한을 추가하십시오.    

<uses-permission android:name="android.permission.INTERNET"/>

결과 :

인터넷 연결이 빈약 한 영역에서 앱이 응답하지 않고 잠기고 사용자가 느리게 인식하고 강제 종료를 수행해야하며 활동 관리자가 앱을 종료하고 사용자에게 앱이 중지되었음을 알리는 위험이 있습니다.

안드로이드는 반응 형 디자인을위한 훌륭한 프로그래밍 실습에 대한 유용한 팁을 제공합니다 : http://developer.android.com/reference/android/os/NetworkOnMainThreadException.html


456
이것은 매우 나쁜 생각입니다. 해결책은 메인 스레드에서 네트워크 IO를 피하는 것입니다 (허용 된 답변이 표시됨).
MByD

74
이것으로 실제 문제 만 숨길 수 있습니다.
Alex

28
@TwistedUmbrella AsyncTask는 코드 페이지를 추가하지 않고 6 줄 (클래스 선언, 재정의 주석, doInBackground선언, 닫는 괄호 및 호출 execute())을 추가합니다. 반면에 언급 한 바와 같이 사이트에서 한 번만 가져 오더라도 UI 응답 성이 크게 지연됩니다. 게으르지 마십시오.
Zoltán

19
적절한 AsyncTask를 구현하기 전에 샘플 코드 덩어리를 실행하여 무언가 작동하는지 확인하려는 경우 이것이 완벽한 솔루션이라고 생각합니다. 그렇기 때문에 다른 사람들이 말했듯이 프로덕션 응용 프로그램 에서이 작업을 수행해서는 안되며 dev 테스트를위한 빠른 해결 방법으로 만 수행해야합니다.
hooked82

94
공감. 이 답변은 정확하며 순진하거나 어리석지 않지만 단순히 동기식 (즉 , 앱을 차단해야 함 ) 호출 이 필요한 많은 프로그래머에게 이것이 정확히 필요한 것입니다. 나는 안드로이드가 기본적으로 예외를 던지는 것에 대해 더 행복하다. 그것.
Adam

428

나는 새로운을 사용 하여이 문제를 해결했다 Thread.

Thread thread = new Thread(new Runnable() {

    @Override
    public void run() {
        try  {
            //Your code goes here
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
});

thread.start(); 

7
네트워크 작업을 수행 할 때마다 새 스레드를 만드는 대신 단일 스레드 실행기 서비스를 사용할 수도 있습니다.
Alex Lockwood

66
간단하지만 위험합니다. 익명의 Runnable은 엔 클로징 클래스 (예 : Activity 또는 Fragment)를 암시 적으로 참조하므로 스레드가 완료 될 때까지 가비지 수집되지 않습니다. 최소한 우선 순위를 Process.BACKGROUND로 설정해야합니다. 그렇지 않으면이 스레드는 기본 / ui 스레드와 동일한 우선 순위로 실행되며 라이프 사이클 메소드 및 UI 프레임 비율에 맞습니다 (안무가의 로그에서 경고를 확인하십시오).
Stevie

1
@Stevie 우선 순위를 설정하는 방법? runnble이나 executorService 모두 그러한 setter 메소드를 가지고 있지 않습니다
JK

1
@JK ExecutorService에 커스텀 ThreadFactory를 제공하고 Thread.setPriority를 ​​호출하기 전에 호출합니다.
Stevie

1
"Android에서 직접 스레드를 사용하는 것은 권장되지 않습니다. 해결하는 것보다 더 많은 문제가 발생합니다." 실제로 AsyncTask는 정확히 사용되지 않습니다 ... techyourchance.com/asynctask-deprecated
Fran Marzoa

169

허용되는 답변에는 몇 가지 중요한 단점이 있습니다. 실제로 수행중인 작업을 모르면 네트워킹에 AsyncTask를 사용하지 않는 것이 좋습니다 . 단점 중 일부는 다음과 같습니다.

  • 정적이 아닌 내부 클래스로 생성 된 AsyncTask는 둘러싸는 Activity 객체, 해당 컨텍스트 및 해당 활동에 의해 생성 된 전체 View 계층 구조에 대한 암시 적 참조를 갖습니다. 이 참조는 AsyncTask의 백그라운드 작업이 완료 될 때까지 Activity가 가비지 수집되지 않도록합니다. 사용자의 연결 속도가 느리거나 다운로드가 큰 경우 이러한 단기 메모리 누수는 문제가 될 수 있습니다. 예를 들어 방향이 여러 번 변경되거나 실행중인 작업을 취소하지 않은 경우 또는 사용자 활동에서 멀리 이동합니다.
  • AsyncTask는 실행하는 플랫폼에 따라 실행 특성이 다릅니다. API 레벨 4 이전 AsyncTask는 단일 백그라운드 스레드에서 직렬로 실행됩니다. API 레벨 4에서 API 레벨 10까지 AsyncTasks는 최대 128 개의 스레드 풀에서 실행됩니다. API 레벨 11부터 AsyncTask는 단일 백그라운드 스레드에서 직렬로 실행됩니다 (오버로드 된 executeOnExecutor메소드 를 사용하고 대체 실행 프로그램을 제공 하지 않는 한 ). ICS에서 직렬로 실행할 때 정상적으로 작동하는 코드는 Gingerbread에서 동시에 실행될 때 작동하지 않을 수 있습니다 (예 : 부주의 한 실행 순서 종속성이있는 경우).

단기 메모리 누수를 피하고 모든 플랫폼에서 실행 특성을 올바르게 정의하고 실제로 강력한 네트워크 처리를 구축 할 수있는 기반이있는 경우 다음을 고려할 수 있습니다.

  1. 당신이의 좋은 작업을 수행하는 라이브러리를 사용하여 - 거기에 libs와 네트워킹의 좋은 비교의 이 질문은 , 또는
  2. Service또는 IntentService대신에 a를 사용 PendingIntent하여 Activity의 onActivityResult메소드 를 통해 결과를 반환하십시오 .

IntentService 접근

단점 :

  • AsyncTask생각보다 많지는 않지만 보다 많은 코드와 복잡성
  • 요청을 대기열에 넣고 단일 백그라운드 스레드 에서 실행합니다 . 당신은 쉽게 교체하여이를 제어 할 수있는 IntentService동등한와 Service아마 같은 구현 이 하나 .
  • 음, 지금은 다른 사람을 생각할 수 없어요

업사이드 :

  • 단기 메모리 누수 문제 방지
  • 네트워크 작업이 진행되는 동안 활동이 다시 시작되면 해당 onActivityResult방법을 통해 여전히 다운로드 결과를받을 수 있습니다
  • 강력한 네트워킹 코드를 빌드하고 재사용하기 위해 AsyncTask보다 나은 플랫폼. 예 : 중요한 업로드를 할 필요가 있다면, 당신은에서 그것을 할 수 AsyncTaskActivity있지만 사용자의 전화를 받아 앱 밖으로 문맥-전환하는 경우, 시스템이 업로드 완료되기 전에 응용 프로그램을 죽인다. active로 응용 프로그램을 종료 할 가능성적습니다Service .
  • IntentService위의 링크 버전과 같은 자체 동시 버전을 사용 하는 경우을 통해 동시성 수준을 제어 할 수 있습니다 Executor.

구현 요약

IntentService단일 백그라운드 스레드에서 다운로드를 매우 쉽게 구현할 수 있습니다 .

1 단계 : IntentService다운로드를 생성하기 위한 생성 Intent엑스트라 를 통해 무엇을 다운로드할지 알려주고 PendingIntent결과를 다음으로 반환 하는 데 사용할 수 있습니다 Activity.

import android.app.IntentService;
import android.app.PendingIntent;
import android.content.Intent;
import android.util.Log;

import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;

public class DownloadIntentService extends IntentService {

    private static final String TAG = DownloadIntentService.class.getSimpleName();

    public static final String PENDING_RESULT_EXTRA = "pending_result";
    public static final String URL_EXTRA = "url";
    public static final String RSS_RESULT_EXTRA = "url";

    public static final int RESULT_CODE = 0;
    public static final int INVALID_URL_CODE = 1;
    public static final int ERROR_CODE = 2;

    private IllustrativeRSSParser parser;

    public DownloadIntentService() {
        super(TAG);

        // make one and re-use, in the case where more than one intent is queued
        parser = new IllustrativeRSSParser();
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        PendingIntent reply = intent.getParcelableExtra(PENDING_RESULT_EXTRA);
        InputStream in = null;
        try {
            try {
                URL url = new URL(intent.getStringExtra(URL_EXTRA));
                IllustrativeRSS rss = parser.parse(in = url.openStream());

                Intent result = new Intent();
                result.putExtra(RSS_RESULT_EXTRA, rss);

                reply.send(this, RESULT_CODE, result);
            } catch (MalformedURLException exc) {
                reply.send(INVALID_URL_CODE);
            } catch (Exception exc) {
                // could do better by treating the different sax/xml exceptions individually
                reply.send(ERROR_CODE);
            }
        } catch (PendingIntent.CanceledException exc) {
            Log.i(TAG, "reply cancelled", exc);
        }
    }
}

2 단계 : 매니페스트에 서비스를 등록합니다.

<service
        android:name=".DownloadIntentService"
        android:exported="false"/>

3 단계 : 서비스가 결과를 리턴하는 데 사용할 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 프로젝트는 여기에서 사용할 수 있습니다 .


AsyncTask가 그렇게하지 않는 방법이기 때문에 IntentService가 올바른 방법입니다.
Brill Pappin

3
@BrillPappin 나는 거의 전적으로 동의하고 AsyncTask의 단점을 강조하기 위해 단어를 다시 작성했습니다. (아직도 당신이하고있는 일을 정말로 알고 있다면 AsyncTask를 사용하는 것이 좋을 수도 있지만 받아 들여진 대답은 단점을 지적하지 않으며 좋은 점에 너무 인기가있는 경우 는 매우 적습니다. 안드로이드).
Stevie

1
실제로 필요 IllustrativeRSS합니까? RSS로 작업하지 않으면 어떻게됩니까?
Cullub

제 생각에는 Google은 프로그래머 측에 베르 돈을두기보다는 가비지 수집의 나쁜 구현을 변경해야합니다. 이것이 OS의 책임입니다. 프로그래머가 Android 구현의 근본적인 문제로 인해 IntentService 또는 Service를 사용하여 작업을 수행하는 경우 몇 년 후 Google은 IntentService도 나쁜 습관이며 다른 것을 제안합니다. 그리고이 이야기는 계속됩니다. 따라서 Google 개발자는 프로그래머가 아닌 Android 불량 메모리 관리를 해결해야합니다.
saeed khalafinejad 2016 년

나는 단지 그것을 말할 것입니다 :이 답변은에 대한 더 나은 대안보다 프로젝트를 공유하는 더 많은 방법처럼 보입니다 AsyncTask. 이 오류는 개발자가 UI를 지연시키는 것을 막기위한 것이지, 의도 나 서비스로 보안 위험을 감수 할 필요는 없습니다.
버려진 카트

144

Honeycomb 의 UI 스레드에서 네트워크 I / O 를 수행 할 수 없습니다 . 기술적으로 이전 버전의 Android에서 가능하지만 앱의 응답을 중지하고 OS가 앱을 종료시켜 앱이 제대로 작동하지 않을 수 있으므로 실제로 나쁜 생각입니다. 백그라운드 프로세스를 실행하거나 AsyncTask를 사용하여 백그라운드 스레드에서 네트워크 트랜잭션을 수행해야합니다.

Android 개발자 사이트 에 Painless Threading 에 대한 기사가 있으며 여기에 대한 좋은 소개가 있으며 여기에 실제로 제공 할 수있는 것보다 훨씬 더 나은 답변을 제공합니다.


76
  1. strictMode를 사용하지 마십시오 (디버그 모드에서만)
  2. SDK 버전을 변경하지 마십시오
  3. 별도의 스레드를 사용하지 마십시오

서비스 또는 AsyncTask 사용

스택 오버플로 질문도 참조하십시오.

android.os.NetworkOnMainThreadException 안드로이드에서 이메일을 보내는 중


8
아마도 서비스를 사용하는 경우 여전히 별도의 스레드를 만들어야한다는 점을 강조 할 가치가 있습니다. 메인 스레드에서 서비스 콜백이 실행됩니다. 반면에 IntentService는 백그라운드 스레드에서 onHandleIntent 메소드를 실행합니다.
Stevie

장시간 실행되는 작업에는 AsyncTask를 사용해서는 안됩니다! 지침은 최대 2 ~ 3 초를 지정합니다.
Dage

76

다른 스레드에서 네트워크 작업 수행

예를 들어 :

new Thread(new Runnable(){
    @Override
    public void run() {
        // Do network action in this function
    }
}).start();

그리고 이것을 AndroidManifest.xml에 추가하십시오

<uses-permission android:name="android.permission.INTERNET"/>

5
그러나 UI 스레드에서 다음 작업 세트를 수행 할 수 있도록 스레드가 완료되는 시점을 어떻게 알 수 있습니까? AsyncTask는이를위한 기능을 제공합니다. 실행 가능한 스레드를 사용하여 동일한 작업을 수행하는 방법이 있습니까?
Piyush Soni

3
그것은 너무 코드의 끝에서, 단계별로 코드 단계를 처리합니다 마무리, 당신은 UI 스레드에 핸들러 다시 사용할 필요가
henry4343

1
작업자 스레드에서 실행되므로 비동기 작업 또는 인 텐트 서비스를 사용할 수 있습니다.
Chetan Chaudhari

64

다음 코드를 사용하여 엄격 모드를 비활성화합니다.

if (android.os.Build.VERSION.SDK_INT > 9) {
    StrictMode.ThreadPolicy policy = 
        new StrictMode.ThreadPolicy.Builder().permitAll().build();
    StrictMode.setThreadPolicy(policy);
}

권장하지 않습니다 : AsyncTask인터페이스를 사용하십시오 .

두 방법 모두에 대한 전체 코드


2
예 ANR 오류가 발생합니다. 앱이 5 초 안에 응답하지 않음을 의미합니다.
Muhammad Mubashir

11
이것은 정말 나쁜 대답입니다. 스레드 정책을 변경하지 말고 더 나은 코드를 작성해야합니다. 기본 스레드에서 네트워크 작업을 수행하지 마십시오!
shkschneider

@ Sandeep 당신과 다른 시청자도 이것을 읽어야합니다. stackoverflow.com/a/18335031/3470479
Prakhar1001

54

메인 스레드에서 네트워크 기반 작업을 실행할 수 없습니다. 자식 스레드에서 모든 네트워크 기반 작업을 실행하거나 AsyncTask를 구현해야합니다.

다음은 자식 스레드에서 작업을 실행하는 방법입니다.

new Thread(new Runnable(){
    @Override
    public void run() {
        try {
            // Your implementation goes here
        } 
        catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}).start();

익명 Runnable은 엔 클로징 클래스에 대한 암시 적 참조를 가지며 스레드가 완료 될 때까지 GC가되지 않도록하기 때문에 가장 좋은 방법은 아닙니다! 또한이 스레드는 기본 / 미국 스레드와 동일한 우선 순위로 실행되며 수명주기 방법 및 UI 프레임 속도에 맞습니다!
Yousha Aleayoub

49

내부에 코드를 넣으십시오.

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
    }
}

두 번째는 11보다 높은 api에 대해 첫 번째보다 우수합니다
Rohit Goswami

46

이것은 Android 3.0 이상에서 발생합니다. Android 3.0 이상에서는 기본 스레드 / UI 스레드 (활동에서 생성 및 재개시 생성되는 메소드)에서 실행되는 네트워크 작업 (인터넷에 액세스하는 기능) 사용을 제한했습니다.

네트워크 작업에 별도의 스레드를 사용하도록 권장합니다. 올바른 방법으로 네트워크 활동을 수행하는 방법에 대한 자세한 내용 은 AsyncTask 를 참조하십시오.


46

안드로이드 주석 사용 은 옵션입니다. 백그라운드 스레드에서 모든 메소드를 간단하게 실행할 수 있습니다.

// normal method
private void normal() {
    doSomething(); // do something in background
}

@Background
protected void doSomething() 
    // run your networking code here
}

단순성과 가독성의 이점을 제공하지만 단점이 있습니다.


6
@Gavriel 메소드, 액티비티, 프래그먼트, 싱글 톤 등 주석이있는 모든 내용의 복제본을 생성하므로 코드가 두 배나 많아 컴파일 시간이 더 걸립니다. 라이브러리의 버그로 인해 일부 문제가있을 수도 있습니다. 디버깅 및 오류 찾기가 더 어려워졌습니다.
Oleksiy

43

오류는 메인 스레드에서 오래 실행되는 작업으로 인해 발생합니다 . 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
    }
});

42

네트워크 작업, 파일 I / O 또는 SQLite 데이터베이스 작업과 같이 주 스레드 (UI 스레드)에서 시간 소모적 인 작업을 수행해서는 안됩니다. 따라서 이러한 종류의 작업을 위해서는 작업자 스레드를 만들어야하지만 문제는 작업자 스레드에서 UI 관련 작업을 직접 수행 할 수 없다는 것입니다. 이를 위해서는을 사용 Handler하고 전달해야합니다 Message.

이 모든 일을 단순화하기 위해, 안드로이드는 다양한 방법처럼 제공 AsyncTask, AsyncTaskLoader, CursorLoader또는 IntentService. 따라서 요구 사항에 따라 이들 중 하나를 사용할 수 있습니다.


40

spektom 의 최고 답변은 완벽하게 작동합니다.

AsyncTask인라인을 작성하고 클래스로 확장하지 않는 경우이 중에서 응답을 가져와야 AsyncTask할 경우 다음 get()과 같이 메소드를 사용할 수 있습니다 .

RSSFeed feed = new RetreiveFeedTask().execute(urlToRssFeed).get();

(그의 예에서)


5
사용 get()하는 것은 나쁜 생각입니다 ... 그것은 다시 AsyncTask를 "동기화"
시킵니다

더 나은 다른 방법이 있습니까? @Selvin
sivag1

2
결과에 대한 메인 스레드 정보를 얻을 수 있다고 생각합니다. 예를 들어 결과를 포함하여 메인 스레드에 브로드 캐스트를 보냅니다.
Chine Gary

32

이것은 Honeycomb SDK 이상을 대상으로하는 응용 프로그램에서만 발생합니다 . 이전 SDK 버전을 대상으로하는 응용 프로그램은 기본 이벤트 루프 스레드에서 네트워킹을 수행 할 수 있습니다.

오류는 SDK 경고입니다!


28

나를 위해 이것은 다음과 같습니다.

<uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="10" />

내 앱을 테스트 한 장치는 SDK 버전 16 인 4.1.2였습니다!

대상 버전이 Android 대상 라이브러리와 동일한 지 확인하십시오. 대상 라이브러리가 무엇인지 확실하지 않은 경우 프로젝트-> 빌드 경로 -> Android를 마우스 오른쪽 단추로 클릭하십시오 .

또한 다른 사람들이 언급했듯이 인터넷에 액세스 할 수있는 올바른 권한을 포함하십시오.

<uses-permission android:name="android.permission.INTERNET"/>

11
당신이 여기서하고있는 것을 설명해 드리겠습니다 : NetworkOnMainThreadException당신에게 말하는 가디언입니다 : 당신 자신의 발로 쏘지 마십시오 ... 당신의 해결책은 : 가디언이 없었던 과거로 돌아가 봅시다-이제 나는 쏠 수 있습니다. 발을 자유롭게
Selvin

1
나는이 접근법을 취했고 아무런 문제가 없었습니다. 가디언은 때때로 너무 까다 롭습니다.
FractalBob

25

당신의 활동에 이것을 사용

    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();
        }
    });

23

명시 적으로 무언가를 철자하십시오.

기본 스레드는 기본적으로 UI 스레드입니다.

따라서 메인 스레드에서 네트워킹 작업을 수행 할 수 없다는 것은 UI 스레드에서 네트워킹 작업을 수행 할 수 없다는*runOnUiThread(new Runnable() { ... }* 것을 의미 합니다. 즉 , 다른 스레드 내부 블록 에서 네트워킹 작업을 수행 할 수 없습니다 .

(주요 스레드가 아닌 다른 곳에서 해당 오류가 발생하는 이유를 알아 내려는 데 오랜 시간이 걸렸습니다.이 이유는이 스레드가 도움이 되었으며이 의견이 다른 사람에게 도움이되기를 바랍니다.)


22

이 예외는 수행하는 작업이 너무 많은 시간 이 걸리면 주 스레드에서 수행 된 많은 작업으로 인해 발생합니다 .

이를 피하기 위해 스레드 또는 실행기를 사용하여 처리 할 수 ​​있습니다.

Executors.newSingleThreadExecutor().submit(new Runnable() {
    @Override
    public void run() {
        // You can perform your task here.
    }
});

18

이 질문에는 이미 많은 훌륭한 답변이 있지만 그 답변이 게시 된 이후 많은 훌륭한 도서관이 나왔습니다. 이것은 일종의 초보자 안내서입니다.

내가 네트워크 운영 및 수행에 대한 몇 가지 사용 사례 다룰 것 솔루션 또는 각각 두.

HTTP를 통한 ReST

일반적으로 Json은 XML 또는 다른 것일 수 있습니다.

완전한 API 액세스

사용자가 주가, 금리 및 환율을 추적 할 수있는 앱을 작성한다고 가정 해 보겠습니다. 다음과 같은 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/ )

Finances 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);
}

재정

public class FinancesApiBuilder {
    public static FinancesApi build(String baseUrl){
        return new Retrofit.Builder()
                    .baseUrl(baseUrl)
                    .addConverterFactory(GsonConverterFactory.create())
                    .build()
                    .create(FinancesApi.class);
    }
}

프레 그먼트 스 니펫

FinancesApi api = FinancesApiBuilder.build("http://api.example.com/"); //trailing '/' required for predictable behavior
api.getStock("INTC").enqueue(new Callback<Stock>(){
    @Override
    public void onResponse(Call<Stock> stockCall, Response<Stock> stockResponse){
        Stock stock = stockCall.body();
        //do something with the stock
    }
    @Override
    public void onResponse(Call<Stock> stockCall, Throwable t){
        //something bad happened
    }
}

API에 API 키 또는 사용자 토큰 등의 다른 헤더를 보내야하는 경우 Retrofit을 사용하면이를 쉽게 수행 할 수 있습니다 (자세한 내용은이 멋진 답변 참조 : https://stackoverflow.com/a/42899766/1024412 ).

일회용 ReST API 액세스

사용자 GPS 위치를 조회하고 해당 지역의 현재 온도를 확인하고 분위기를 알려주는 "기상 날씨"앱을 구축한다고 가정 해 보겠습니다. 이 유형의 앱은 API 엔드 포인트를 선언 할 필요가 없습니다. 하나의 API 엔드 포인트에 액세스 할 수 있어야합니다.

이온

이 유형의 액세스를위한 훌륭한 라이브러리입니다.

msysmilu의 위대한 답변을 읽으십시오 ( https://stackoverflow.com/a/28559884/1024412 )

HTTP를 통해 이미지로드

발리

발리는 ReST API에도 사용할 수 있지만 더 복잡한 설정이 필요하기 때문에 위와 같이 Square에서 Retrofit을 사용하는 것을 선호합니다 ( http://square.github.io/retrofit/ )

소셜 네트워킹 앱을 구축 중이며 친구의 프로필 사진을로드하려고한다고 가정 해 보겠습니다.

build.gradle

이 줄을 모듈 레벨 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 메소드 (Fragment, Activity) 또는 생성자 (Dialog)에 추가하십시오.

NetworkImageView profilePicture = view.findViewById(R.id.profile_picture);
profilePicture.setImageUrl("http://example.com/users/images/profile.jpg", ImageFetch.getImageLoader(getContext());

피카소

Square의 또 다른 훌륭한 도서관. 좋은 예를 보려면 사이트를 참조하십시오 : http://square.github.io/picasso/


16

간단히 말해서

UI 스레드에서 네트워크 작업을 수행하지 마십시오

예를 들어, HTTP 요청을하는 경우 이는 네트워크 작업입니다.

해결책:

  1. 새 스레드를 만들어야합니다
  2. 또는 AsyncTask 클래스를 사용하십시오.

방법:

모든 작품을 안에 넣으십시오

  1. run() 새로운 실의 방법
  2. 또는 doInBackground() AsyncTask 클래스의 메소드입니다.

그러나:

네트워크 응답에서 무언가를 가져 와서 TextView의 디스플레이 응답 메시지와 같이보기에 표시 하려면 UI 스레드 로 다시 돌아와야합니다 .

당신이 그것을하지 않으면 얻을 것이다 ViewRootImpl$CalledFromWrongThreadException.

어떻게?

  1. AsyncTask를 사용하는 동안 onPostExecute()메소드 에서 뷰를 업데이트하십시오.
  2. 또는runOnUiThread() 메소드 내부의 run()메소드 및 업데이트보기를 호출 하십시오 .

12

코드의 일부를 다른 스레드로 이동하여 오프로드하고 ANR , NetworkOnMainThreadException , IllegalStateException (예 : 메인 스레드의 데이터베이스에 액세스 할 수 없으므로 UI가 오랫동안 잠길 수 있으므로)을 main thread피할 수 있습니다.

상황에 따라 선택해야 할 접근법이 있습니다

자바 스레드 또는 안드로이드 핸들러

Java 스레드는 한 번만 사용되며 run 메소드를 실행 한 후 죽습니다.

HandlerThread는 루 퍼가있는 새 스레드를 시작하기위한 편리한 클래스입니다.

비동기 작업

AsyncTaskThread and Handler에 대한 도우미 클래스로 설계되었으며 일반적인 스레딩 프레임 워크를 구성하지 않습니다. AsyncTasks는 짧은 작업 (최대 몇 초)에 이상적으로 사용되어야합니다. 스레드를 장시간 계속 실행해야하는 경우 java.util.concurrent 패키지에서 제공하는 다양한 API (예 : Executor , ThreadPoolExecutorFutureTask .

스레드 풀 구현 ThreadPoolExecutor , ScheduledThreadPoolExecutor ...

스레드 풀 (예 : 코어 풀 크기, 최대 풀 크기, 활성 시간 유지 등)을 세부적으로 제어하는 ​​ExecutorService를 구현하는 ThreadPoolExecutor 클래스

ScheduledThreadPoolExecutor-ThreadPoolExecutor를 확장하는 클래스 지정된 지연 후 또는 주기적으로 작업을 예약 할 수 있습니다.

미래 작업

FutureTask는 비동기 처리를 수행하지만 결과가 아직 준비되지 않았거나 처리가 완료되지 않은 경우 get ()을 호출하면 스레드가 차단됩니다.

AsyncTaskLoaders

AsyncTaskLoader는 AsyncTask 고유의 많은 문제를 해결합니다.

IntentService

이것은 Android에서 장기 실행 처리를위한 사실상의 선택입니다. 좋은 예는 큰 파일을 업로드하거나 다운로드하는 것입니다. 사용자가 앱을 종료하더라도 이러한 작업이 진행되는 동안 사용자가 앱을 사용할 수 없도록 차단하지 않으려는 경우에도 업로드 및 다운로드가 계속 될 수 있습니다.

JobScheduler

효과적으로 서비스를 작성하고 서비스 실행시기에 대한 기준을 지정하는 JobInfo.Builder를 사용하여 작업을 작성해야합니다.

RxJava

관찰 가능한 시퀀스를 사용하여 비동기 및 이벤트 기반 프로그램을 작성하기위한 라이브러리.

루틴 (코 틀린)

그것의 주요 요점은 비동기 코드를 동기식으로 보이게합니다.

자세히보기 여기 , 여기 , 여기 , 여기


나를 위해 일했습니다 ... AsyncTask를 광범위하게 사용했지만 한 작업이 실행될 때 다른 작업이 대기합니다. 이 문제를 해결하고 싶습니다. 이제 executeonexecutor로 작업하십시오. 메모리 부족 장치에서 어떻게 작동하는지 봅시다.
Suraj Shingade

방법을 살펴보십시오 : asyncTask.executeOnExecutor (AsyncTask.THREAD_POOL_EXECUTOR, params); 작업을 동시에 실행
yoAlex5

10

위에 거대한 솔루션 풀이 있지만 아무도 언급하지 않았습니다 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
    }
});

10

새로운 ThreadAsyncTask를 솔루션은 이미 설명했다.

AsyncTask짧은 작업에는 이상적으로 사용해야합니다. ThreadAndroid에서는 보통 이 바람직하지 않습니다.

HandlerThreadHandler를 사용하는 대체 솔루션을 살펴보십시오.

핸들러 스레드

루 퍼가있는 새 스레드를 시작하기위한 편리한 클래스입니다. 그런 다음 루퍼를 사용하여 핸들러 클래스를 작성할 수 있습니다. 참고 start()여전히 호출해야합니다.

매니저:

핸들러를 사용하면 스레드의 MessageQueue와 연관된 Message 및 Runnable 객체를 보내고 처리 할 수 ​​있습니다. 각 핸들러 인스턴스는 단일 스레드 및 해당 스레드의 메시지 큐와 연관됩니다. 새 처리기를 만들면 처리기를 만드는 스레드의 스레드 / 메시지 큐에 바인딩됩니다. 이때부터 메시지와 실행 파일을 해당 메시지 큐로 전달하고 메시지에서 나올 때 실행합니다. 열.

해결책:

  1. 창조하다 HandlerThread

  2. 전화 start()HandlerThread

  3. 에서 Handler가져 와서 생성LooperHanlerThread

  4. 네트워크 작업 관련 코드를 Runnable객체에 포함

  5. 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);

이 방법을 사용하는 장점 :

  1. Thread/AsyncTask각 네트워크 작업마다 새로 만들기 비용이 많이 듭니다. 이 Thread/AsyncTask네트워크는 다음 네트워크 작업을 위해 파괴되고 다시 만들어집니다. 그러나로 HandlerHandlerThread접근 방법, 당신은 하나에 (Runnable를 작업으로) 많은 네트워크 작업을 제출할 수 있습니다 HandlerThread사용하여 Handler.

8

RxAndroid이 문제에 대한 또 다른 좋은 대안이며 스레드를 생성 한 다음 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);
    }
});
  1. 을 지정 (Schedulers.io())하면 RxAndroid가 getFavoriteMusicShows() 다른 스레드에서 실행 됩니다.

  2. 이를 사용하여 AndroidSchedulers.mainThread()UI 스레드에서이 Observable을 관찰하려고합니다. 즉, UI 스레드에서 onNext()콜백을 호출하려고합니다.


8

기본 스레드는 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);

자세한 내용은 다음을 확인하십시오. 무통 나사 가공


고마워. 버전 1은 onCreate에서 첫 번째 조치로 추가 할 때 도움이됩니다.
Ingo

7

작동합니다. Dr.Luiji의 대답을 좀 더 단순하게 만들었습니다.

new Thread() {
    @Override
    public void run() {
        try {
            //Your code goes here
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}.start();

7

Android에서는 네트워크 작업을 기본 스레드에서 실행할 수 없습니다. Thread, AsyncTask (단기 실행 작업), Service (장기 실행 작업)를 사용하여 네트워크 작업을 수행 할 수 있습니다.


7

기본 (UI) 스레드에서 네트워크 자원에 액세스하면이 예외가 발생합니다. 이 문제를 피하려면 네트워크 리소스에 액세스하기 위해 별도의 스레드 또는 AsyncTask를 사용하십시오.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.