[java] ‘android.os.NetworkOnMainThreadException’를 수정하는 방법?

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.

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"/>


답변

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

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

더하다:

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


답변

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

Thread thread = new Thread(new Runnable() {

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

thread.start(); 


답변

허용되는 답변에는 몇 가지 중요한 단점이 있습니다. 실제로 수행중인 작업을 모르면 네트워킹에 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 프로젝트는 여기에서 사용할 수 있습니다 .


답변

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

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


답변

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

서비스 또는 AsyncTask 사용

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

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


답변

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

예를 들어 :

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

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

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