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

How can I fix 'android.os.NetworkOnMainThreadException'?

提问人:bejoy george 提问时间:6/14/2011 最后编辑:Peter Mortensenbejoy george 更新时间:7/5/2023 访问量:1488638

问:

我在为 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

如何解决此问题?

java android android 网络 networkonmainthread

评论

139赞 Adrian Monk 8/7/2012
有关详细信息,请阅读有关 NetworkOnMainThreadException 的这篇博客文章。它解释了为什么在 Android 3.0 及更高版本上会发生这种情况。
6赞 Anuj Sharma 1/23/2014
要走上礼仪轨道,首先阅读 android 中的网络请求,然后我建议学习“凌空抽射”。
3赞 Snicolas 2/12/2014
有许多替代库可以解决这个问题。本页底部列出了许多内容。如果您有更多,我们会带他们:)
0赞 Jay 11/20/2017
“由于以前版本的 Android 中存在一个错误,系统没有将写入主线程上的 TCP 套接字标记为严格模式违规。Android 7.0 修复了此错误。出现此行为的应用现在会抛出 android.os.NetworkOnMainThreadException。 - 因此,我们中的一些人直到最近才遇到这种情况!developer.android.com/about/versions/nougat/......

答:

152赞 Mark Allison 6/14/2011 #1

您无法在 Honeycomb 上的 UI 线程上执行网络 I/O。从技术上讲,这在早期版本的 Android 上可能的,但这是一个非常糟糕的主意,因为它会导致您的应用程序停止响应,并可能导致操作系统因行为不佳而杀死您的应用程序。需要运行后台进程或使用 AsyncTask 在后台线程上执行网络事务。

Android 开发者网站上有一篇关于无痛线程的文章,这是一个很好的介绍,它将为您提供比此处实际提供的更好的答案深度。

2674赞 Michael Spector 6/14/2011 #2

注意:AsyncTask 在 API 级别 30 中已弃用。
异步任务 |Android 开发者

当应用程序尝试在其主线程上执行网络操作时,将引发此异常。在 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.javaoncreate()

new RetrieveFeedTask().execute(urlToRssFeed);

不要忘记将其添加到文件中:AndroidManifest.xml

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

评论

0赞 y_159 6/14/2021
因此,这种在主线程上运行网络操作仅在 android 中存在问题,而不是在标准 java 代码(用 java 编写的代码,但不适用于 android 应用程序)中。
23赞 Erel Segal-Halevi 4/5/2022
由于 AsyncTask 已弃用,最新的解决方案是什么?
0赞 Harshil 11/4/2022
@ErelSegal-haleti 使用线程。
0赞 DimeCadmium 5/3/2023
@Harshil......如何做到这一点?
772赞 user1169115 2/15/2012 #3

您几乎总是应该在线程上运行网络操作或作为异步任务运行网络操作。

但是,如果您愿意接受后果,则可以删除此限制并覆盖默认行为。

加:

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

StrictMode.setThreadPolicy(policy);

在你的课堂上,

在 Android 清单 .xml 文件中添加此权限:

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

后果:

您的应用将(在 Internet 连接不稳定的区域)变得无响应并锁定,用户会察觉到速度缓慢,必须强制杀伤,并且您冒着活动管理器终止您的应用并告诉用户应用已停止的风险。

Android 提供了一些关于良好编程实践的好提示,用于设计响应能力:NetworkOnMainThreadException |Android 开发者

评论

1赞 MosesK 6/3/2021
哇,谢谢你的解释,我现在明白了。我看到了一个应用程序,它在其 java 类中实现了 ThreadPolicy,我有点困惑它在做什么。当网络不足时,我看到了你所说的后果。
75赞 Piyush-Ask Any Difference 10/24/2012 #4

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

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

不建议这样做:使用界面。AsyncTask

两种方法的完整代码

评论

2赞 Muhammad Mubashir 5/6/2013
是的,ANR错误会到来。表示应用程序在 5 秒内没有响应。
13赞 shkschneider 8/29/2013
这是一个非常糟糕的答案。你不应该改变线程的策略,而应该写出更好的代码:不要在主线程上进行网络操作!
130赞 Dipak Keshariya 10/30/2012 #5

这个问题有两种解决方案。

  1. 不要在主 UI 线程中使用网络调用。为此使用异步任务。

  2. 将以下代码写入 MainActivity 文件的 setContentView(R.layout.activity_main);

    如果 (android.os.Build.VERSION.SDK_INT > 9) { StrictMode.ThreadPolicy 策略 = new StrictMode.ThreadPolicy.Builder().permitAll().build(); StrictMode.setThreadPolicy(策略); }

并将下面的 import 语句添加到您的 Java 文件中。

import android.os.StrictMode;

评论

14赞 Richi González 5/28/2013
遵循第二个解决方案是一种不好的做法。异步是正确执行此操作的方法。如果你改变政策,你就像隐藏你的问题!
521赞 Dr.Luiji 1/22/2013 #6

我使用新的.Thread

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

thread.start(); 

        

评论

0赞 Isiah 6/3/2021
你怎么会把参数传递给这个?
0赞 Donald Duck 8/9/2021
如果需要在请求后访问 UI,则需要在末尾返回主线程,如此所述。
44赞 sivag1 7/19/2013 #7

spektom 的最佳答案非常完美。

如果您正在编写内联而不是扩展为类,并且最重要的是,如果需要从 中获取响应,则可以使用以下方法。AsyncTaskAsyncTaskget()

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

(以他为例。

83赞 venergiac 8/18/2013 #8
  1. 不要使用 strictMode(仅在调试模式下)
  2. 请勿更改 SDK 版本
  3. 不要使用单独的线程

使用 Service 或 AsyncTask

另请参阅 Stack Overflow 问题:

android.os.NetworkOnMainThreadException 从 Android 发送电子邮件

评论

8赞 Stevie 1/22/2014
也许值得强调的一点是,如果你使用一个服务,你仍然需要创建一个单独的线程 - 服务回调在主线程上运行。另一方面,IntentService 在后台线程上运行其 onHandleIntent 方法。
0赞 Dage 2/4/2014
不应将 AsyncTask 用于长时间运行的操作!指南规定最多 2 到 3 秒。
51赞 raihan ahmed 9/5/2013 #9

这发生在 Android 3.0 及更高版本中。从 Android 3.0 及更高版本开始,它们限制了使用网络操作(访问互联网的函数)在主线程/界面线程(从 Activity 中的 on create 和 on resume 方法生成的操作)中运行。

这是为了鼓励使用单独的线程进行网络操作。有关如何以正确方式执行网络活动的更多详细信息,请参阅 AsyncTask

28赞 rharvey 9/17/2013 #10

对我来说是这样的:

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

我测试应用程序的设备是 4.1.2,即 SDK 版本 16!

确保目标版本与您的 Android 目标库相同。如果您不确定目标库是什么,请右键单击您的项目 -> 构建路径 -> Android,它应该是勾选的那个。

此外,正如其他人所提到的,包括访问 Internet 的正确权限:

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

评论

11赞 Selvin 9/17/2013
让我向你解释你在这里做什么:是卫报告诉你:不要向自己的脚开枪......你的解决方案是:让我们回到没有守护者的过去——现在我可以自由地向我的脚射击NetworkOnMainThreadException
1赞 FractalBob 10/26/2013
我也采用了这种方法,没有任何问题。Guardian 有时太挑剔了。
96赞 henry4343 12/24/2013 #11

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

例如:

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

并将其添加到文件AndroidManifest.xml中:

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

评论

0赞 Sujith S Manjavana 7/29/2022
这有什么缺点吗?它与 ExecutorService 有何不同?
45赞 Kapil Vats 1/6/2014 #12

不应在主线程(UI 线程)上执行任何耗时的任务,例如任何网络操作、文件 I/O 或 SQLite 数据库操作。所以对于这种操作,你应该创建一个工作线程,但问题是你不能直接从你的工作线程执行任何与UI相关的操作。为此,您必须使用并传递 .HandlerMessage

为了简化所有这些操作,Android 提供了多种方式,例如 、 或 .因此,您可以根据需要使用其中任何一个。AsyncTaskAsyncTaskLoaderCursorLoaderIntentService

64赞 Dhruv Jindal 1/14/2014 #13

基于网络的操作不能在主线程上运行。您需要在子线程上运行所有基于网络的任务或实现 .AsyncTask

以下是在子线程中运行任务的方式:

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

评论

1赞 Yousha Aleayoub 3/12/2016
Anonymous Runnable 不是最好的方法,因为它对封闭类有一个隐式引用,并阻止它被 GC 编辑,直到线程完成!此外,此线程将以与主线程/美国线程相同的优先级运行,与生命周期方法和 UI 帧速率竞争!
0赞 Sujith S Manjavana 7/29/2022
@YoushaAleayoub那么,用什么来代替呢?
195赞 Stevie 1/22/2014 #14

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

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

如果要避免短期内存泄漏,在所有平台上具有明确定义的执行特征,并且具有构建真正强大的网络处理的基础,则可能需要考虑:

  1. 使用一个可以很好地为您完成这项工作的库 - 在这个问题中对网络库进行了很好的比较,或者
  2. 使用 a 或代替,也许使用 a 通过 Activity 的方法返回结果。ServiceIntentServicePendingIntentonActivityResult

IntentService 方法

缺点:

  • 代码和复杂性比 ,尽管没有您想象的那么多AsyncTask
  • 将对请求进行排队,并在单个后台线程上运行它们。您可以通过替换为等效的实现(可能像这个一样)来轻松控制它。IntentServiceService
  • 呃,我现在真的想不出其他的了

优点:

  • 避免短期内存泄漏问题
  • 如果您的活动在网络操作进行时重新启动,它仍然可以通过其方法接收下载结果onActivityResult
  • 一个比 AsyncTask 更好的平台,用于构建和重用强大的网络代码。示例:如果您需要执行重要上传,则可以从 中执行此操作,但如果用户在上下文中切换出应用以接听电话,则系统可能会在上传完成之前终止应用。它不太可能杀死具有活动 .AsyncTaskActivityService
  • 如果您使用自己的并发版本(如我上面链接的版本),则可以通过 .IntentServiceExecutor

实施摘要

您可以非常轻松地实现在单个后台线程上执行下载。IntentService

第 1 步:创建一个以执行下载。你可以通过extras告诉它要下载什么,并传递给它一个用于将结果返回给:IntentServiceIntentPendingIntentActivity

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 步:从 Activity 调用服务,传递一个 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 项目。

30赞 perry 2/25/2014 #15

这仅适用于面向 Honeycomb SDK 或更高版本的应用程序。允许面向早期 SDK 版本的应用程序在其主事件循环线程上执行联网。

错误是 SDK 警告!

26赞 dhiraj kakran 3/1/2014 #16

在“您的活动记录”中使用此功能

    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();
        }
    });
50赞 Oleksiy 6/12/2014 #17

使用 Android 注释是一种选择。它允许您简单地在后台线程中运行任何方法:

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

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

请注意,尽管它提供了简单性和可读性的好处,但它也有其缺点。

评论

6赞 Oleksiy 8/19/2015
@Gavriel它会创建您注释的所有内容的副本,无论是方法、活动、片段、单例等,因此代码数量是原来的两倍,编译它需要更长的时间。由于库中的错误,它也可能存在一些问题。调试和查找错误将变得更加困难。
24赞 Novak 6/25/2014 #18

只是为了明确地说明一些事情:

主线程基本上是 UI 线程。

所以说你不能在主线程中做网络操作,意味着你不能在UI线程中做网络操作,这意味着你不能在*runOnUiThread(new Runnable() { ... }* 阻塞在其他线程中。

(我只是有很长一段时间的挠头时刻,试图弄清楚为什么我在主线程以外的地方遇到这个错误。这就是为什么;这个线程有帮助;希望这个评论能帮助到其他人。

46赞 Ashwin S Ashok 7/16/2014 #19

该错误是由于在主线程中执行长时间运行的操作,您可以使用 AsynTaskThread 轻松纠正问题。您可以签出此库 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
    }
});
24赞 amardeep 7/21/2014 #20

如果执行任务花费的时间过长,则由于在主线程上执行的任何繁重任务,都会发生此异常。

为了避免这种情况,我们可以使用线程执行器来处理它

Executors.newSingleThreadExecutor().submit(new Runnable() {
    @Override
    public void run() {
        // You can perform your task here.
    }
});
53赞 Vaishali Sutariya 7/28/2014 #21

把你的代码放进去:

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
    }
}
-5赞 Kumarsunil 12/12/2014 #22

Android 不允许在主 Activity 线程中加入单独的进程,此处的 HTTP 连接是一个独立的线程。这就是您收到“android.os.NetworkOnMainThreadException”的原因。

在向用户显示 Web 视图之前,可能需要检查实际的 Internet 连接,因为如果没有 Internet,Web 视图将向用户显示“未找到页面”错误,这通常不会显示什么。

为了检查 Internet 可用性,可以使用 ping 命令,但在 Wi-Fi 的情况下,可以在 Wi-Fi 服务器上禁用 ping,因此在这种情况下,您可以使用 HTTP 连接来检查请求的状态。

如果您在向用户显示 Web 视图之前检查自己的 Web 视图 URL 链接,这可能是正确的方法。在这种情况下,您可以使用 Android 的严格模式,但不允许所有策略,因为您不需要它。

您只应为严格模式提供网络允许策略。只需将以下行添加到您的代码中,您就不会收到此错误。

StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitNetwork().build();
StrictMode.setThreadPolicy(policy);
3赞 Subhalaxmi 12/21/2014 #23

我用一种简单的方式解决了这个问题......

我添加并解决了这个问题。oncreateStrictMode.enableDefaults();

使用或解决此问题ServiceAsyncTask

注意:

Do not change SDK version
Do not use a separate thread

有关更多信息,请查看此内容。

评论

0赞 Ajay Pandya 12/9/2016
对于更多链接不起作用,您可以发布一些细节作为理解
-3赞 Gurpreet singh 1/27/2015 #24

使用以下代码执行繁重的任务。

// Your package here


import java.util.List;
import org.apache.http.NameValuePair;

import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.os.AsyncTask;
import android.view.View.OnSystemUiVisibilityChangeListener;

public class AsyncRequest extends AsyncTask<String, Integer, String> {

    Context context;
    ProgressDialog pDialog;

    // Three Constructors
    public AsyncRequest(Activity a, String m, List<NameValuePair> p) {
        context = a;
        method = m;
        parameters = p;
    }

    public AsyncRequest(Activity a) {
        this.caller = (OnAsyncRequestComplete) a;
        context = a;
    }

    public String doInBackground(String... urls) {

        //Perform your task here
        return result;
    }

    public void onPreExecute() {
        pDialog = new ProgressDialog(context);
        pDialog.setMessage("Please wait..");
        pDialog.setCancelable(false);
        pDialog.show();
    }

    public void onProgressUpdate(Integer... progress) {
        // You can implement some progressBar and update it in this record.
        //   setProgressPercent(progress[0]);
    }

    public void onPostExecute(String response) {
        if (pDialog != null && pDialog.isShowing()) {
            pDialog.dismiss();
        }
        // Get the result here
    }

    protected void onCancelled(String response) {

        if (pDialog != null && pDialog.isShowing()) {
            pDialog.dismiss();
        }
    }
}
10赞 Kacy 2/14/2015 #25

这行得通。我只是让 Luiji 博士的回答更简单一些。

new Thread() {
    @Override
    public void run() {
        try {
            //Your code goes here
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}.start();
10赞 msysmilu 2/17/2015 #26

虽然上面有一个巨大的解决方案池,但没有人提到:https://github.com/koush/ioncom.koushikdutta.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
    }
});
-3赞 prat3ik-patel 2/26/2015 #27

您只需在文件 manifest.xml 中的 manifest 标记后面添加以下行

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

在活动文件中,在绑定语句后添加以下代码:

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

评论

2赞 ci_ 3/20/2015
绕过 UI 线程上的网络代码检测确实是很糟糕的建议,一开始是有原因的。
0赞 netsplit 10/13/2021
不明白这个答案的仇恨,即使通常不是最佳的。很高兴有选择
7赞 Ponsuyambu 6/25/2015 #28

在 Android 上,网络操作不能在主线程上运行。您可以使用 Thread、AsyncTask(短期运行任务)、Service(长时间运行的任务)来执行网络操作。

16赞 Nabin 7/22/2015 #29

简单来说,

不要在 UI 线程中执行网络工作

例如,如果执行 HTTP 请求,则为网络操作。

溶液:

  1. 您必须创建一个新线程
  2. 或使用 AsyncTask 类

道路:

把你所有的作品都放进去

  1. 新线程的方法run()
  2. 或者 AsyncTask 类的方法。doInBackground()

但:

当您从网络响应中获取某些内容并希望将其显示在视图上(例如在 TextView 中显示响应消息)时,您需要返回到 UI 线程。

如果你不这样做,你会得到.ViewRootImpl$CalledFromWrongThreadException

操作方法

  1. 使用 AsyncTask 时,从方法更新视图onPostExecute()
  2. 或者调用 runOnUiThread() 方法并更新方法中的视图。run()
7赞 RevanthKrishnaKumar V. 8/13/2015 #30

从主 (UI) 线程访问网络资源会导致此异常。使用单独的线程或 AsyncTask 来访问网络资源以避免此问题。

0赞 RobotCharlie 8/17/2015 #31

您实际上可以启动一个新线程。我以前遇到过这个问题,并用这种方式解决了它。

6赞 Krishna Satwaji Khandagale 12/18/2015 #32

不允许在 Android 上的 UI 线程上实现网络操作。您必须使用 AsyncTask 类来执行与网络相关的操作,例如发送 API 请求、从 URL 下载图像等,并使用 的回调方法,您可以在 方法中获取结果,您将在 UI 线程中,您可以使用来自 Web 服务的数据填充 UI 或类似的东西。AsyncTaskonPostExecute

示例:假设您要从 URL 下载图像:https://www.samplewebsite.com/sampleimage.jpg

解决方案使用:分别。AsyncTask<String, Void, Bitmap><Params, Progress, Result>

    public class MyDownloader extends AsyncTask<String, Void, Bitmap>
    {
        @Override
        protected void onPreExecute() {
            // Show progress dialog
            super.onPreExecute();
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            //Populate Ui
            super.onPostExecute(bitmap);
        }

        @Override
        protected Bitmap doInBackground(String... params) {
            // Open URL connection read bitmaps and return form here
            return result;
        }

        @Override
        protected void onProgressUpdate(Void... values) {
            // Show progress update
            super.onProgressUpdate(values);
        }
    }
}

注意:不要忘记在 Android 清单文件中添加 Internet 权限。它会像魅力一样工作。:)

评论

0赞 CommonsWare 10/27/2023
请注意,AsyncTask 在几年前已弃用。
5赞 Adnan Abdollah Zaki 1/16/2016 #33

当应用程序尝试在其主线程上执行网络操作时,将引发此异常。 如果您的任务花费的时间超过 5 秒,则需要强制关闭。

在以下位置运行代码:AsyncTask

class RetrieveFeedTask extends AsyncTask<String, Void, Boolean> {

    protected RSSFeed doInBackground(String... urls) {
       // TODO: Connect
    }

    protected void onPostExecute(RSSFeed feed) {
        // TODO: Check this.exception
        // TODO: Do something with the feed
    }
}
1赞 BalaramNayak 1/19/2016 #34

发生 NetworkOnMainThread 异常的原因是您在默认线程(即 UI 线程)上调用了某些网络操作。 根据不允许的Android版本Android 3(Honeycomb),您应该在主线程之外调用网络操作。

可以使用 AsyncTask、IntentService,也可以创建自己的线程并在 run 方法中调用。 有关更多信息,请访问连接到网络

5赞 bpr10 5/23/2016 #35

我们还可以使用 RxJava 将网络操作移动到后台线程。而且这也相当简单。

webService.doSomething(someData)
          .subscribeOn(Schedulers.newThread())-- This for background thread
          .observeOn(AndroidSchedulers.mainThread()) -- for callback on UI
          .subscribe(result -> resultText.setText("It worked!"),
              e -> handleError(e));

你可以用 RxJava 做更多的事情。以下是 RxJava 的一些链接。随意深入研究。

Android 中的 RxJava 异步任务

http://blog.stablekernel.com/replace-asynctask-asynctaskloader-rx-observable-rxjava-android-patterns/

评论

0赞 Peter Mortensen 7/23/2021
第二个链接已断开 (404)。
0赞 bpr10 7/23/2021
谢谢,@PeterMortensen更新了文章的链接。
8赞 Alex Shutov 6/24/2016 #36

还有另一种非常方便的方法可以解决这个问题——使用 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的优秀电子书

4赞 Lovekush Vishwakarma 10/4/2016 #37

如何修复android.os.NetworkOnMainThreadException

什么是 NetworkOnMainThreadException:

在 Android 中,我们必须在 UI 线程(主线程)上执行所有 UI 操作。如果我们在主线程上执行后台操作或某些网络操作,那么我们就有可能发生此异常,并且应用程序将不会响应。

如何解决:

为了避免这个问题,你必须使用另一个线程进行后台操作或网络操作,比如使用 asyncTask,并使用一些库进行网络操作,如 Volley、AsyncHttp 等。

4赞 Mansuu.... 1/16/2017 #38

在主线程上执行网络操作时,会引发 android.os.NetworkOnMainThreadException。最好在 AsyncTask 中执行此操作以删除此异常。这样写:

    new AsyncTask<Void, String, String>() {

        @Override
        protected Void doInBackground(Void... params) {
            // Perform your network operation.
            // Get JSON or XML string from the server.
            // Store in a local variable (say response) and return.
            return response;
        }

        protected void onPostExecute(String results) {
            // Response returned by doInBackGround() will be received
            // by onPostExecute(String results).
            // Now manipulate your jason/xml String(results).
        }

    }.execute();
}

评论

0赞 CommonsWare 10/27/2023
请注意,这在几年前已被弃用。AsyncTask
5赞 Himanshu 2/19/2017 #39

您还可以通过使用以下代码使用严格模式来解决此问题。这也是解决此问题的替代方法。

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

但最佳做法是使用 AsyncTask。

2赞 aks 3/3/2017 #40

由于 Android 在单个线程上工作,因此您不应在主线程上执行任何网络操作。有多种方法可以避免这种情况。

使用以下方式执行网络操作

  • Asysnctask:适用于不需要太多时间的小型操作。
  • 意图服务:用于需要大量时间的网络操作。
  • 使用自定义库(如 VolleyRetrofit)进行处理 复杂的网络操作

永远不要使用 StrictMode.setThreadPolicy(policy),因为它会冻结你的 UI,根本不是一个好主意。

2赞 Hossain Ahamed 3/17/2017 #41

不能在主线程或UI线程上调用network。在Android上,如果要调用网络,则有两种选择:

  1. 调用 asynctask,它将运行一个后台线程来处理网络操作。
  2. 您可以创建自己的可运行线程来处理网络操作。

就我个人而言,我更喜欢 asynctask。有关更多信息,您可以参考此链接

11赞 Ravindra babu 5/6/2017 #42

新的和 AsyncTask 解决方案已经解释过了。Thread

AsyncTask理想情况下,应用于短期操作。对于Android来说,Normal是不可取的。Thread

查看使用 HandlerThreadHandler 的替代解决方案

处理程序线程

方便的类,用于启动具有 looper 的新线程。然后,可以使用循环器创建处理程序类。请注意,仍必须调用。start()

处理器:

Handler 允许您发送和处理与线程的 MessageQueue 关联的 Message 和 Runnable 对象。每个 Handler 实例都与单个线程和该线程的消息队列相关联。当您创建一个新的 Handler 时,它被绑定到创建它的线程的线程/消息队列 -- 从那时起,它将消息和可运行对象传递到该消息队列,并在它们从消息队列中出来时执行它们。

溶液:

  1. 创造HandlerThread

  2. 拜访start()HandlerThread

  3. 通过获取HandlerLooperHanlerThread

  4. 在对象中嵌入与网络操作相关的代码Runnable

  5. 将任务提交到RunnableHandler

示例代码片段,用于解决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. 为每个网络操作创建一个新的操作是昂贵的。将被销毁并重新创建,以用于下一次网络操作。但是,有了 和 方法,您可以使用 将许多网络操作(作为可运行任务)提交到 single。Thread/AsyncTaskThread/AsyncTaskHandlerHandlerThreadHandlerThreadHandler
8赞 Shinoo Goyal 6/9/2017 #43

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. 通过指定 ,RxAndroid 将在不同的线程上运行。(Schedulers.io())getFavoriteMusicShows()

  2. 通过使用,我们希望在 UI 线程上观察这个 Observable,即我们希望在 UI 线程上调用我们的回调。AndroidSchedulers.mainThread()onNext()

6赞 J.D.1731 7/10/2017 #44

您可以使用 KotlinAnko

KotlinAndroid 的新官方语言。您可以在此处找到有关它的更多信息:适用于 Android 的 Kotlin

Anko 是 Android 中 Kotlin 支持的库。一些文档位于 GitHub 页面上

该解决方案非常有用,只有几行代码由 @AntonioLeiva 编写:使用 Anko 在 Android 中使用 Kotlin 运行后台任务 (KAD 09)。

doAsync {
    var result = runLongTask()
    uiThread {
        toast(result)
    }
}

虽然很简单,但当您在 UI 线程上运行后台作业时会发生,因此您必须做的一件事是在后台运行 longTask 作业。您可以在 Android 应用程序中使用此方法和 KotlinAnko 来执行此操作。NetworkOnMainThread

评论

2赞 Peter Mortensen 7/23/2021
在 GitHub 页面:“Anko 已弃用。有关更多信息,请参阅此页面
9赞 Sharath kumar 9/4/2017 #45

主线程是 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);

有关更多信息,请查看以下内容: 无痛穿线

21赞 KG6ZVP 10/21/2017 #46

关于这个问题已经有很多很好的答案,但自从这些答案发布以来,已经出现了许多很棒的图书馆。这旨在作为一种新手指南。

我将介绍几个执行网络操作的用例,以及每个用例的一两个解决方案。

基于 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)

从Square进行改造

对于具有多个终端节点的 API 来说,这是一个很好的选择,它允许您声明 REST 终端节点,而不必像 Amazon Ion JavaVolley(网站:Retrofit)等其他库那样单独编写代码。

您如何将其与财务 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

文件财务Api.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);
}

财务ApiBuilder

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

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 端点。

离子

对于这种类型的访问来说,这是一个很棒的库。

请阅读 msysmilu 对如何修复“android.os.NetworkOnMainThreadException”的精彩回答?

通过 HTTP 加载图像

截击

Volley 也可用于 REST API,但由于需要更复杂的设置,我更喜欢使用 Square 的 Retrofit,如上所述。

假设您正在构建一个社交网络应用程序,并希望加载朋友的个人资料图片。

文件 build.gradle

将以下行添加到模块build.gradle 文件中:

implementation 'com.android.volley:volley:1.0.0'

文件图像提取.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的另一个优秀图书馆。请参阅网站以获取一些很好的例子。

-1赞 Elye 1/18/2018 #47

截至 2018 年,我建议在 Kotlin 中使用 RxJava 进行网络获取。下面是一个简单的例子。

Single.fromCallable {
        // Your Network Fetching Code
        Network.fetchHttp(url) 
    }
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe {
        // What you need to do with your result on the view 
        result -> view.updateScreen(result) 
    }

评论

0赞 Elye 1/18/2018
您可以从 github.com/elye/demo_android_network_evolution 找到完整的代码
0赞 Elye 1/18/2018
如果您想了解 Android 网络访问的历史总体趋势,请查看 medium.com/@elye.project/...
14赞 yoAlex5 3/17/2018 #48

您可以将部分代码移动到另一个线程中,以卸载并避免获得 ANRNetworkOnMainThreadExceptionIllegalStateException(例如,无法访问主线程上的数据库,因为它可能会长时间锁定 UI)。main thread

您应该根据情况选择一些方法

Java Thread 或 Android HandlerThread

Java 线程只能一次性使用,并在执行其 run 方法后死亡。

HandlerThread 是一个方便的类,用于启动具有循环器的新线程。

AsyncTask(在 API 级别 30 中已弃用

AsyncTask 被设计为围绕 ThreadHandler 的帮助程序类,并不构成通用线程框架。理想情况下,AsyncTasks 应用于短操作(最多几秒钟)。如果需要长时间保持线程运行,强烈建议使用 java.util.concurrent 包提供的各种 API,例如 Executor、ThreadPoolExecutorFutureTask

由于线程垄断了 UI 组件,因此无法访问某些 View,这就是 Handler 来救援的原因

[执行器框架]

ThreadPoolExecutor 类,用于实现 ExecutorService,该类可以对线程池进行精细控制(例如,核心池大小、最大池大小、保持活动时间等)

ScheduledThreadPoolExecutor - 扩展 ThreadPoolExecutor 的类。它可以在给定的延迟后或定期安排任务。

未来任务

FutureTask 执行异步处理,但是,如果结果尚未准备好或处理尚未完成,则调用 get() 将阻塞线程

异步任务加载程序

AsyncTaskLoaders,因为它们解决了 AsyncTask 固有的许多问题

意向服务

这是在 Android 上长时间运行处理的事实上的选择,一个很好的例子是上传或下载大文件。即使用户退出应用程序,上传和下载也可能继续,您当然不希望阻止用户在这些任务进行时使用应用程序。

作业调度程序

实际上,您必须创建一个服务,并使用 JobInfo.Builder 创建一个作业,该作业指定何时运行该服务的条件。

RxJava

用于使用可观察序列编写异步和基于事件的程序的库。

协程 (Kotlin)

它的主要要点是,它使异步代码看起来很像同步代码

阅读更多内容 这里这里, 这里, 和 这里.

-1赞 Ashok Kumar 5/10/2018 #49

不同的选项:

  1. 使用普通的 Java 可运行线程来处理网络任务,并且可以使用 runOnUIThread() 更新 UI

  2. intentservice/ async 任务可用于在获得网络响应后要更新 UI 的情况

1赞 Devix 12/11/2018 #50

如果您在 Kotlin 和 Anko 中工作,您可以添加:

doAsync {
    method()
}

评论

1赞 EpicPandaForce 12/31/2019
Anko 已弃用
0赞 Peter Mortensen 7/23/2021
弃用 Anko 的来源
2赞 Santanu Sur 3/2/2019 #51

您可以使用 Kotlin 协程

 class YoutActivity : AppCompatActivity, CoroutineScope {
      
      override fun onCreate(...) {
         launch {  yourHeavyMethod() }
      }

      suspend fun yourHeavyMethod() {
         with(Dispatchers.IO){ yourNetworkCall() }
         ...
         ...
      }
 } 

您可以按照本指南进行操作。

0赞 Sazzad Hissain Khan 6/1/2019 #52

Kotlin 版本

internal class RetrieveFeedTask : AsyncTask<String, Void, RSSFeed>() {

    override fun doInBackground(vararg urls: String): RSSFeed? {

        try {
             // download
             // prepare RSSFeeds
             return RSSFeeds

         } catch (e: Exception) {

            //handle exception
            return null
        }
    }

    override fun onPostExecute(feed: RSSFeed) {
        // TODO: check this.exception
        // TODO: do something with the feed
    }
}

调用示例,

RetrieveFeedTask().execute(url)
4赞 Vipul Prajapati 6/10/2019 #53

在后台线程中使用AsycTask

爪哇岛

class NetworkThread extends AsyncTask<String, Void, String> {

    protected Void doInBackground(String... arg0) {
        //Your implementation
    }

    protected void onPostExecute(String result) {
        // TODO: do something with the feed
    }
}

随时随地拨打电话

new NetworkThread().execute("Your URL here");

Kotlin

internal class MyNetworkTask : AsyncTask<String, Void, RSSFeed>() {

    override fun doInBackground(vararg urls: String): RSSFeed? {
        try {
             // download
             // prepare RSSFeeds
             return RSSFeeds
         } catch (e: Exception) {
            //handle exception
            return null
        }
    }

    override fun onPostExecute(feed: RSSFeed) {
        // TODO: check this.exception
        // TODO: do something with the feed
    }
}

调用 kotlin

MyNetworkTask().execute(url)
3赞 Richard Kamere 8/19/2019 #54

我也有类似的问题。我刚刚在您的活动的 oncreate 方法中使用了以下内容。

// Allow strict mode
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);

而且效果很好。

需要注意的是,将其用于耗时超过 100 毫秒的网络请求将导致明显的 UI 冻结,并可能导致 ANR(应用程序无响应),因此请记住这一点。

评论

1赞 Shubham Suryavanshi 9/13/2019
@RichardKamere官方文档中,您可以检查“StrictMode 是一种开发人员工具,它可以检测您可能意外执行的操作并引起您的注意,以便您可以修复它们”和“您永远不应该在 Google Play 上分发的应用程序中启用 StrictMode”。点击这里StrictMode
1赞 Jorgesys 10/29/2019
这将有助于测试目的,但不能被视为解决方案,请改用 asynctask。
0赞 shareef 3/10/2020
@Zun 如果我想检查 API 服务器上的互联网连接怎么办?
3赞 majurageerthan 1/1/2020 #55

来自 developer-android

理想情况下,AsyncTasks 应用于短操作(最多几秒钟)。

使用是好的。您也可以考虑其他选项,例如,newCachedThreadPoolnewSingleThreadExecutornewFixedThreadPool

    ExecutorService myExecutor = Executors.newCachedThreadPool();
    myExecutor.execute(new Runnable() {
        @Override
        public void run() {
            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);
        }
    });

ThreadPoolExecutor 是一个帮助程序类,可简化此过程。这 类管理一组线程的创建,设置它们的 优先级,并管理工作在这些线程之间的分配方式。 随着工作负载的增加或减少,类会启动或销毁 更多线程需要根据工作负载进行调整。

有关 Android 线程的更多信息,请参阅此处

5赞 Rahul 2/11/2020 #56

在 Android 上,网络操作不能在主线程上运行。您可以使用 、(短期运行任务)、(长时间运行的任务)来执行网络操作。 当应用程序尝试在其主线程上执行网络操作时引发。如果您的任务花费的时间超过 5 秒,则需要强制关闭。ThreadAsyncTaskServiceandroid.os.NetworkOnMainThreadException

在以下位置运行代码:AsyncTask

class FeedTask extends AsyncTask<String, Void, Boolean> {

    protected RSSFeed doInBackground(String... urls) {
       // TODO: Connect
    }

    protected void onPostExecute(RSSFeed feed) {
        // TODO: Check this.exception
        // TODO: Do something with the feed
    }
}

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

不建议这样做。

但出于调试目的,您也可以使用以下代码禁用严格模式:

if (android.os.Build.VERSION.SDK_INT > 9) {
    StrictMode.ThreadPolicy policy =
        new StrictMode.ThreadPolicy.Builder().permitAll().build();
    StrictMode.setThreadPolicy(policy);
}
1赞 Oleg Gryb 3/14/2020 #57

这些答案需要更新,以使用更现代的方式连接到 Internet 上的服务器并处理一般的异步任务。

例如,您可以在 Google Drive API 示例中找到使用任务的示例。在这种情况下,也应使用相同的方法。我将使用 OP 的原始代码来演示此方法。

首先,您需要定义一个 off-main 线程执行器,并且只需执行一次:

private val mExecutor: Executor = Executors.newSingleThreadExecutor()

然后在该执行器中处理您的逻辑,该执行器将在主线程上运行

Tasks.call (mExecutor, Callable<String> {

        val url = URL(urlToRssFeed)
        val factory = SAXParserFactory.newInstance()
        val parser = factory.newSAXParser()
        val xmlreader = parser.getXMLReader()
        val theRSSHandler = RssHandler()
        xmlreader.setContentHandler(theRSSHandler)
        val is = InputSource(url.openStream())
        xmlreader.parse(is)
        theRSSHandler.getFeed()

        // Complete processing and return a String or other object.
        // E.g., you could return Boolean indicating a success or failure.
        return@Callable someResult
}).continueWith{
    // it.result here is what your asynchronous task has returned
    processResult(it.result)
}

continueWith 子句将在异步任务完成后执行,您将有权访问任务通过 it.result 返回的值。

14赞 El Sushiboi 8/14/2020 #58

Kotlin

如果您使用的是 Kotlin,则可以使用协程

fun doSomeNetworkStuff() {
    GlobalScope.launch(Dispatchers.IO) {
        // ...
    }
}
6赞 Shay Ribera 10/15/2020 #59

Google 在 Android 11 中弃用了 Android AsyncTask API。

即使您在 main 活动之外创建了一个线程类,只需在 main 中调用它,您也会得到相同的错误。这些调用必须位于可运行的线程中,但如果您需要一些异步代码在后台执行或之后在发布时执行一些代码,您可以查看 Kotlin 和 Java 的一些替代方案:

*https://stackoverflow.com/questions/58767733/android-asynctask-api-deprecating-in-android-11-what-are-the-alternatives*

特别适合我的是 mayank1513 对上述链接中可运行线程的 Java 8 实现的回答。代码如下:

new Thread(() -> {
    // do background stuff here
    runOnUiThread(() -> {
        // OnPostExecute stuff here
    });
}).start();

但是,您可以先在代码的某些部分定义线程,然后在其他位置启动它,如下所示:

线程定义

Thread thread = new Thread(() -> {
    // do background stuff here
    runOnUiThread(() -> {
        // OnPostExecute stuff here
    });
});

线程调用

thread.start();

我希望这能使某人免于看到被弃用的头痛.AsyncTask

0赞 neu242 1/25/2021 #60

下面是使用 、 和 的简单解决方案:OkHttpFutureExecutorServiceCallable

final OkHttpClient httpClient = new OkHttpClient();
final ExecutorService executor = newFixedThreadPool(3);

final Request request = new Request.Builder().url("http://example.com").build();

Response response = executor.submit(new Callable<Response>() {
   public Response call() throws IOException {
      return httpClient.newCall(request).execute();
   }
}).get();

评论

0赞 Arpan Saini 2/6/2021
出现错误:System.err:java.util.concurrent.ExecutionException:javax.net.ssl.SSLException:无法解析TLS数据包标头
0赞 neu242 2/8/2021
@ArpanSaini 您的确切 URL 是否适用于 curl 或浏览器?也许从 https 切换到 http(反之亦然),看看它是否有效?
9赞 Sunny Gupta 2/21/2021 #61

Android 不允许在主线程上运行长时间运行的操作。

因此,只需使用不同的线程并在需要时将结果发布到主线程即可。

new Thread(new Runnable() {
        @Override
        public void run() {
            /*
            // Run operation here
            */
            // After getting the result
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    // Post the result to the main thread
                }
            });
        }
    }).start();
0赞 nyxee 4/5/2021 #62

我将返回值的网络访问函数转换为挂起函数,如下所示:


suspend fun isInternetReachable(): Boolean {
  ...
  ...
  return result
}

然后我修改了我使用该函数的位置以适应它:

...
Globalscope.async{
  ...
  result = isInternetReachable()
  ...
}
...
-2赞 Sourav Kumar Verma 5/1/2022 #63
Executors.newFixedThreadPool(3).execute(() -> {
      //DO Task;        
 });

评论

0赞 Joseph 5/3/2022
你需要在你的答案中添加一些解释
1赞 programandoconro 5/10/2022 #64

我在 Kotlin 中使用解决了。有很多使用 Java 的例子,所以我想添加一个在 Kotlin 中对我有用的解决方案。Thread

 Thread {
     println("NEW THREAD")
     callAPI() // add your own task
 }.start()

因此,正如许多其他人所解释的那样,您不能通过调用来阻止主线程,因此有必要创建一个新线程。