如何修复“android.os.NetworkOnMainThreadException”?

bz4sfanl  于 2022-09-16  发布在  Java
关注(0)|答案(29)|浏览(305)

我在运行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

如何解决此问题?

qrjkbowd

qrjkbowd1#

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()
1.通过使用AndroidSchedulers.mainThread(),我们希望在UI线程上观察到这个可观察的,即,我们希望我们的onNext()回调在UI线程中被调用。

zengzsys

zengzsys2#

这仅适用于针对HiveSDK或更高版本的应用程序。允许针对早期SDK版本的应用程序在其主事件循环线程上进行联网。
The error is the SDK warning!

8cdiaqws

8cdiaqws3#

我使用新的Thread解决了这个问题。

Thread thread = new Thread(new Runnable() {

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

thread.start();
4uqofj5v

4uqofj5v4#

公认的答案有一些明显的缺点。除非您“真正”知道自己在做什么,否则不建议使用AsyncTask进行联网。一些不利方面包括:

  • 作为非静态内部类创建的AsyncTask具有对封闭Activity对象、其上下文和该活动创建的整个View层次结构的隐式引用。此引用可防止在AsyncTask的后台工作完成之前对“活动”进行垃圾收集。如果用户的连接速度慢,并且/或者下载量大,这些短期内存泄漏可能会成为一个问题-例如,如果方向改变了几次(并且您没有取消正在执行的任务),或者用户导航离开“活动”。
  • AsyncTask具有不同的执行特性,具体取决于它执行的平台:在API级别4之前,异步任务在单个后台线程上连续执行;从API级别4到API级别10,AsyncTasks在最多128个线程的池中执行;从API级别11开始,AsyncTask在单个后台线程上连续执行(除非使用重载executeOnExecutor方法并提供替代执行器)。在ICS上串行运行时工作良好的代码在Gingerbread上并发执行时可能会中断,比如说,如果您无意中有执行依赖项的顺序。

如果您想避免短期内存泄漏,在所有平台上都有定义良好的执行特征,并且有构建真正健壮的网络处理的基础,那么您可能需要考虑:
1.使用一个为您做得很好的库-在this question中有一个很好的网络库比较,或者
1.使用e1d1e或IntentService代替,可能使用e1d 3d1e通过“活动”的onActivityResult方法返回结果。

意向服务方法

缺点:

  • AsyncTask更多的代码和复杂性,尽管没有您想象的那么多
  • 将请求排队并在一个单个后台线程上运行它们。您可以通过将IntentService替换为等效的Service实现来轻松控制这一点,比如this one
  • 嗯,实际上我现在想不出其他人了

正面:

  • 避免短期内存泄漏问题
  • 如果您的活动在网络运行过程中重新启动,它仍然可以通过onActivityResult方法接收下载结果
  • 比AsyncTask更好的平台,可以构建和重用健壮的网络代码。示例:如果您需要进行重要上传,您可以在Activity中从AsyncTask进行上传,但如果用户上下文切换出应用程序以进行电话呼叫,系统可能会在上传完成之前*终止应用程序。使用活动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 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项目here

ibrsph3r

ibrsph3r5#

您不能在Hive上的UI线程上执行网络I/O。从技术上讲,这在早期版本的Android上是可能的,但这是一个非常糟糕的主意,因为它会导致你的应用停止响应,并可能导致操作系统因为你的应用表现不好而杀死你的应用。您需要运行后台进程或使用AsyncTask在后台线程上执行网络事务。
Android开发者网站上有一篇关于Painless Threading的文章,这是一篇很好的介绍,它将为您提供比这里实际提供的更深入的答案。

7fhtutme

7fhtutme6#

这个问题有两种解决方案。
1.不要在主UI线程中使用网络调用。为此使用异步任务。
1.在*setContentView(R.layout.activity_main);*之后将以下代码写入MainActivity文件:
如果(android.os.Build.VERSION.SDK_INT>9){StrictMode.ThreadPolicy policy=new-StrictMode.ThreadPolicy.Builder().permitAll().Build();StrictModel.SetThreadPolicys(policy);}
并将下面的import语句导入到Java文件中。

import android.os.StrictMode;
mfpqipee

mfpqipee7#

在另一个线程上执行网络操作。
例如:

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

并将其添加到文件AndroidManifest.xml

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

fhg3lkii8#

1.不要使用strictMode(仅在调试模式下)
1.不要更改SDK版本
1.不要使用单独的螺纹
使用服务或异步任务
另请参阅堆栈溢出问题:

  • android.os。NetworkOnMainThreadException从Android发送电子邮件*
pepwfjgg

pepwfjgg9#

您可以使用以下代码禁用严格模式:

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

不建议这样做:使用AsyncTask接口。

Full code for both the methods

bjp0bcyl

bjp0bcyl10#

基于网络的操作无法在主线程上运行。您需要在子线程上运行所有基于网络的任务,或者实现AsyncTask。
这是如何在子线程中运行任务:

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

ktecyv1j11#

将代码放入:

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

f0ofjuux12#

这在Android 3.0及以上版本中发生。从Android 3.0及以上版本开始,他们限制使用网络操作(访问互联网的功能)在主线程/UI线程中运行(从活动中的创建和恢复方法生成)。
这是为了鼓励使用单独的线程进行网络操作。有关如何正确执行网络活动的更多详细信息,请参阅AsyncTask

f45qwnt8

f45qwnt813#

可以选择使用Android Annotations。它将允许您在后台线程中简单地运行任何方法:

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

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

注意,尽管它提供了简单易读的优点,但也有其缺点。

isr3a4wc

isr3a4wc14#

该错误是由于在主线程中执行长时间运行的操作导致的,您可以使用AsynTaskThread轻松纠正该问题。您可以 checkout 此库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
    }
});
up9lanfz

up9lanfz15#

您不应该在主线程(UI线程)上执行任何耗时的任务,如任何网络操作、文件I/O或SQLite数据库操作。因此,对于这种操作,您应该创建一个工作线程,但问题是您不能从工作线程直接执行任何与UI相关的操作。为此,您必须使用Handler并通过Message
为了简化所有这些,Android提供了多种方式,如AsyncTaskAsyncTaskLoaderCursorLoaderIntentService。因此,您可以根据您的需求使用这些。

相关问题