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에서 동시에 실행될 때 작동하지 않을 수 있습니다 (예 : 부주의 한 실행 순서 종속성이있는 경우).
단기 메모리 누수를 피하고 모든 플랫폼에서 실행 특성을 올바르게 정의하고 실제로 강력한 네트워크 처리를 구축 할 수있는 기반이있는 경우 다음을 고려할 수 있습니다.
- 당신이의 좋은 작업을 수행하는 라이브러리를 사용하여 – 거기에 libs와 네트워킹의 좋은 비교의 이 질문은 , 또는
Service
또는IntentService
대신에 a를 사용PendingIntent
하여 Activity의onActivityResult
메소드 를 통해 결과를 반환하십시오 .
IntentService 접근
단점 :
AsyncTask
생각보다 많지는 않지만 보다 많은 코드와 복잡성- 요청을 대기열에 넣고 단일 백그라운드 스레드 에서 실행합니다 . 당신은 쉽게 교체하여이를 제어 할 수있는
IntentService
동등한와Service
아마 같은 구현 이 하나 . - 음, 지금은 다른 사람을 생각할 수 없어요
업사이드 :
- 단기 메모리 누수 문제 방지
- 네트워크 작업이 진행되는 동안 활동이 다시 시작되면 해당
onActivityResult
방법을 통해 여전히 다운로드 결과를받을 수 있습니다 - 강력한 네트워킹 코드를 빌드하고 재사용하기 위해 AsyncTask보다 나은 플랫폼. 예 : 중요한 업로드를 할 필요가 있다면, 당신은에서 그것을 할 수
AsyncTask
에Activity
있지만 사용자의 전화를 받아 앱 밖으로 문맥-전환하는 경우, 시스템이 수 업로드 완료되기 전에 응용 프로그램을 죽인다. 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 에 대한 기사가 있으며 여기에 대한 좋은 소개가 있으며 여기에 실제로 제공 할 수있는 것보다 훨씬 더 나은 답변을 제공합니다.
답변
- strictMode를 사용하지 마십시오 (디버그 모드에서만)
- SDK 버전을 변경하지 마십시오
- 별도의 스레드를 사용하지 마십시오
서비스 또는 AsyncTask 사용
스택 오버플로 질문도 참조하십시오.
답변
다른 스레드에서 네트워크 작업 수행
예를 들어 :
new Thread(new Runnable(){
@Override
public void run() {
// Do network action in this function
}
}).start();
그리고 이것을 AndroidManifest.xml에 추가하십시오
<uses-permission android:name="android.permission.INTERNET"/>