2614

为 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

我该如何解决这个问题?

4

64 回答 64

2647

注意: AsyncTask在 API 级别 30 中已被弃用。
安卓开发者

当应用程序尝试在其主线程上执行网络操作时,将引发此异常。在以下位置运行您的代码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"/>
于 2011-06-14T12:15:05.223 回答
748

You should almost always run network operations on a thread or as an asynchronous task.

But it is possible to remove this restriction and you override the default behavior, if you are willing to accept the consequences.

Add:

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

StrictMode.setThreadPolicy(policy);

In your class,

and

Add this permission in the Android manifest.xml file:

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

Consequences:

Your app will (in areas of spotty Internet connection) become unresponsive and lock up, the user perceives slowness and has to do a force kill, and you risk the activity manager killing your app and telling the user that the app has stopped.

Android has some good tips on good programming practices to design for responsiveness: NetworkOnMainThreadException | Android Developers

于 2012-02-15T06:59:50.027 回答
492

我使用新的Thread.

Thread thread = new Thread(new Runnable() {

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

thread.start(); 
于 2013-01-21T16:31:35.090 回答
187

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

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

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

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

IntentService 方法

缺点:

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

优点:

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

实施总结

您可以IntentService很容易地实现在单个后台线程上执行下载。

第 1 步:创建一个IntentService以执行下载。Intent您可以通过extras告诉它要下载什么,并将其传递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 步:从 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);

第四步:在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);
}

此处提供了一个 GitHub 项目,其中包含一个完整的 Android Studio/ Gradle项目。

于 2014-01-22T13:17:32.277 回答
150

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

Android 开发者网站上有一篇关于Painless Threading的文章很好地介绍了这一点,它将为您提供比此处实际提供的更深入的答案。

于 2011-06-14T12:07:51.613 回答
128

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

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

  2. 在setContentView(R.layout.activity_main);之后将以下代码写入您的 MainActivity 文件中;:

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

并将下面的 import 语句放入您的 Java 文件中。

import android.os.StrictMode;
于 2012-10-30T10:06:32.587 回答
90

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

例如:

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

并将其添加到文件AndroidManifest.xml

<uses-permission android:name="android.permission.INTERNET"/>
于 2013-12-24T14:33:08.510 回答
81
  1. 不要使用 strictMode(仅在调试模式下)
  2. 不要更改 SDK 版本
  3. 不要使用单独的线程

使用 Service 或 AsyncTask

另请参阅堆栈溢出问题:

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

于 2013-08-18T09:18:48.467 回答
74

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

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

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

两种方法的完整代码

于 2012-10-24T07:10:03.367 回答
62

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

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

new Thread(new Runnable(){
    @Override
    public void run() {
        try {
            // Your implementation goes here
        } 
        catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}).start();
于 2014-01-14T06:14:13.900 回答
52

将您的代码放入:

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
    }
}
于 2014-07-28T10:43:13.117 回答
50

这发生在 Android 3.0 及更高版本中。从 Android 3.0 及更高版本开始,它们已限制使用网络操作(访问 Internet 的函数)在主线程/UI 线程中运行(从您的 on create 和 on resume 方法在活动中产生的内容)。

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

于 2013-09-05T08:16:34.440 回答
49

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

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

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

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

于 2014-06-12T03:10:54.360 回答
45

该错误是由于在主线程中执行了长时间运行的操作,您可以使用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
    }
});
于 2014-07-16T06:09:37.833 回答
44

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

为了简化所有这些事情,Android 提供了各种方式,例如AsyncTask、或。因此,您可以根据自己的要求使用其中任何一种。AsyncTaskLoaderCursorLoaderIntentService

于 2014-01-05T18:28:38.127 回答
43

spektom的最佳答案非常完美。

如果您正在编写AsyncTask内联而不是作为一个类扩展,并且除此之外,如果需要从 中获得响应AsyncTask,可以使用以下get()方法。

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

(来自他的例子。)

于 2013-07-18T18:52:22.977 回答
32

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

错误是SDK警告!

于 2014-02-25T06:23:51.290 回答
28

对我来说是这样的:

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

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

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

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

<uses-permission android:name="android.permission.INTERNET"/>
于 2013-09-17T12:15:21.133 回答
26

在您的活动中使用它

    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();
        }
    });
于 2014-03-01T13:43:59.780 回答
24

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

主线程基本上是 UI 线程。

所以说你不能在主线程中进行网络操作意味着你不能在 UI 线程中进行网络操作,这意味着你也不能*runOnUiThread(new Runnable() { ... }*在某个其他线程内的块中进行网络操作。

(我花了很长的时间想弄清楚为什么我在主线程以外的地方遇到了这个错误。这就是原因;这个线程有帮助;希望这个评论可以帮助其他人。)

于 2014-06-24T21:32:53.947 回答
23

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

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

Executors.newSingleThreadExecutor().submit(new Runnable() {
    @Override
    public void run() {
        // You can perform your task here.
    }
});
于 2014-07-21T11:53:20.923 回答
20

这个问题已经有很多很棒的答案,但是自从这些答案发布以来,已经出现了很多很棒的库。这是一种新手指南。

我将介绍几个用于执行网络操作的用例,并为每个用例提供两个解决方案。

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

文件FinanceApi.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);
    }
}

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

离子

对于此类访问,这是一个很棒的库。

请阅读msysmiluHow can I fix 'android.os.NetworkOnMainThreadException' 的精彩回答?.

通过 HTTP 加载图像

排球

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

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

文件build.gradle

将此行添加到您的模块build.gradle文件中:

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

文件ImageFetch.java

Volley 比改造需要更多的设置。您将需要创建一个这样的类来设置 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());

毕加索

Picasso是 Square 的另一个优秀图书馆。请参阅网站以获取一些很好的示例。

于 2017-10-20T23:50:07.767 回答
16

简单来说,

不要在 UI 线程中做网络工作

例如,如果您执行 HTTP 请求,那就是网络操作。

解决方案:

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

方法:

把你所有的作品都放进去

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

但:

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

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

如何

  1. 使用 AsyncTask 时,从onPostExecute()方法更新视图
  2. 或者调用runOnUiThread()方法并更新方法内的视图run()
于 2015-07-22T02:05:44.033 回答
14

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

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

Java线程或 Android HandlerThread

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

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

AsyncTask(在 API 级别 30 中已弃用)

AsyncTask被设计为围绕ThreadHandler的辅助类,并不构成通用线程框架。AsyncTasks 最适合用于短时间的操作(最多几秒钟)。如果您需要保持线程长时间运行,强烈建议您使用 java.util.concurrent 包提供的各种 API,例如ExecutorThreadPoolExecutorFutureTask

由于线程独占了 UI 组件,所以无法访问某些 View,这就是 Handler 来救场的原因

【执行器框架】

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

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

未来任务

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

异步任务加载器

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

意向服务

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

作业调度器

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

RxJava

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

协程(Kotlin)

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

在此处此处此处此处阅读更多信息。

于 2018-03-17T15:58:49.810 回答
11

新的ThreadAsyncTask解决方案已经解释过了。

AsyncTask理想情况下应用于短期操作。NormalThread不适用于 Android。

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

处理线程

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

处理程序:

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

解决方案:

  1. 创造HandlerThread

  2. 打电话start()_HandlerThread

  3. Handler通过获取Looper来创建HanlerThread

  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被销毁并为下一次网络操作重新创建。但是使用HandlerandHandlerThread方法,您可以HandlerThread使用Handler.
于 2017-05-05T20:11:56.267 回答
10

虽然上面有一个巨大的解决方案池,但没有人提到com.koushikdutta.ionhttps ://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
    }
});
于 2015-02-17T10:31:31.067 回答
9

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

new Thread() {
    @Override
    public void run() {
        try {
            //Your code goes here
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}.start();
于 2015-02-13T23:10:17.853 回答
9

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

有关更多信息,请查看:Painless threading

于 2017-09-04T10:43:19.090 回答
9

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

于 2016-06-23T22:00:02.920 回答
9

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,即我们希望我们的onNext()回调在 UI 线程上被调用。

于 2017-06-09T05:57:25.223 回答
8

科特林

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

fun doSomeNetworkStuff() {
    GlobalScope.launch(Dispatchers.IO) {
        // ...
    }
}
于 2020-08-14T14:19:43.133 回答
7

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

于 2015-06-25T07:42:59.583 回答
7

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

于 2015-08-13T08:26:14.747 回答
7

您可以使用KotlinAnko

Kotlin是一种新的Android官方语言。您可以在此处找到更多相关信息: Android 版 Kotlin

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

真正有用的解决方案,只有@AntonioLeiva 编写的几行代码: Using Anko to run background tasks with Kotlin in Android (KAD 09)

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

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

于 2017-07-10T09:06:55.807 回答
6

在 Android 上,网络操作不能在主线程上运行。您可以使用Thread, AsyncTask(短期任务)、Service(长期任务)进行网络操作。android.os.NetworkOnMainThreadException当应用程序尝试在其主线程上执行网络操作时抛出。如果您的任务耗时超过 5 秒,则需要强制关闭。

在以下位置运行您的代码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);
}
于 2020-02-11T10:28:02.603 回答
6

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

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

使用 AsyncTask 的解决方案:分别是。

    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 权限。它会像魅力一样工作。:)

于 2015-12-18T09:23:21.713 回答
6

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();
于 2021-02-21T08:54:49.910 回答
5

当应用程序尝试在其主线程上执行网络操作时,将引发此异常。如果您的任务耗时超过 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
    }
}
于 2016-01-15T16:47:54.673 回答
5

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

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

但最佳实践是使用 AsyncTask。

于 2017-02-19T11:31:11.097 回答
5

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

即使你在 main Activity 之外创建了一个线程类,只要在 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 的头痛。

于 2020-10-15T14:56:34.990 回答
5

我们还可以使用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/

于 2016-05-23T09:32:15.610 回答
4

如何修复 android.os.NetworkOnMainThreadException

什么是 NetworkOnMainThreadException:

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

如何修复它:

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

于 2016-10-04T08:00:51.103 回答
4

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

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

而且效果很好。

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

于 2019-08-18T17:27:01.277 回答
4

Do this in Background Thread using AsycTask

Java

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

Call wherever you need

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

Call in kotlin

MyNetworkTask().execute(url)
于 2019-06-10T04:50:55.837 回答
4

在主线程上执行网络操作时会抛出 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();
}
于 2017-01-16T04:56:58.680 回答
3

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

我在之后添加oncreate StrictMode.enableDefaults();并解决了这个问题。

或者

使用ServiceAsyncTask解决这个问题

笔记:

Do not change SDK version
Do not use a separate thread

有关更多信息,请查看此

于 2014-12-21T08:17:47.360 回答
3

来自开发者安卓

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

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

    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 线程的更多信息,请参阅此内容。

于 2020-01-01T14:52:10.283 回答
2

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

doAsync {
    method()
}
于 2018-12-10T22:09:37.817 回答
2

您不能在主线程或 UI 线程上调用网络。在 Android 上,如果你想调用网络,有两个选项 -

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

我个人更喜欢异步任务。有关更多信息,您可以参考此链接

于 2017-03-17T06:00:44.263 回答
2

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

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

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

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返回的值。

于 2020-03-14T00:11:57.440 回答
2

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

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

  • Asysnctask:用于不需要太多时间的小型操作。
  • Intent Service : 用于需要大量时间的网络操作。
  • 使用VolleyRetrofit等自定义库来处理复杂的网络操作

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

于 2017-03-02T16:16:14.040 回答
2

您可以使用 Kotlin协程

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

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

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

于 2019-03-01T17:37:35.380 回答
1

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

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

于 2016-01-19T09:52:56.550 回答
1

Android Jetpack推出WorkManager,修复Android 8.1 (Oreo)后台服务限制问题, Android 5.0 (Lollipop) 以下的AlarmManager 和Lolipop 以上的JobScheduler 的使用问题。

请使用WorkManager在后台线程上运行任务,即使在用户关闭应用程序后它也会继续运行。

于 2019-11-06T04:52:44.607 回答
1

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


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

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

...
Globalscope.async{
  ...
  result = isInternetReachable()
  ...
}
...
于 2021-04-04T23:52:04.760 回答
1

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

于 2015-08-17T01:47:28.940 回答
0

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)
于 2019-06-01T07:41:03.440 回答
0

不同的选择:

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

  2. 如果您想在获得网络响应后更新 UI,可以使用 intentservice/ async 任务

于 2018-05-10T09:24:39.887 回答
0

截至 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) 
    }
于 2018-01-17T21:54:56.417 回答
0

OkHttp这是一个使用、FutureExecutorService的简单解决方案Callable

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();
于 2021-01-25T10:12:47.793 回答
-1

永远不要在 UI 线程上做任何长时间运行的工作。长时间运行的工作可以是与服务器的通信、文件的读/写等。这些任务应该在后台线程上。这就是创建Service,AsyncTask的原因Threads。您可以禁用StrictMode,这将防止崩溃。但是,从不建议这样做。

我建议您StrictMode至少在调试模式下利用 . 使用下面的代码来获取在主线程上减慢您的应用程序的任何问题的日志。

StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
            .detectAll()
            .penaltyLog()
            .build());

您可以设置不同的惩罚:

penaltyLog() // to print log
penaltyDeath() // This will crash you App(so costly penalty)
penaltyDialog() // Show alert when something went lazy on Main thread

这里有更多信息StrictModeStrictMode | 安卓开发者

于 2017-08-01T17:31:34.680 回答
-3

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

// 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();
        }
    }
}
于 2015-01-27T09:56:43.643 回答
-4

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

<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);
}
于 2015-02-26T05:44:37.343 回答
-5

Android不允许单独的进程进入主Activity线程,这里的HTTP连接是一个独立的线程。这就是您获得“ android.os.NetworkOnMainThreadException ”的原因。

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

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

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

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

StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitNetwork().build();
StrictMode.setThreadPolicy(policy);
于 2014-12-12T04:12:21.547 回答