繁体   English   中英

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

[英]How can I fix 'android.os.NetworkOnMainThreadException'?

为 RssReader 运行 Android 项目时出现错误。

代码:

URL url = new URL(urlToRssFeed);
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser();
XMLReader xmlreader = parser.getXMLReader();
RssHandler theRSSHandler = new RssHandler();
xmlreader.setContentHandler(theRSSHandler);
InputSource is = new InputSource(url.openStream());
xmlreader.parse(is);
return theRSSHandler.getFeed();

它显示以下错误:

android.os.NetworkOnMainThreadException

我该如何解决这个问题?

注意: AsyncTask 在 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.java文件中,您可以在oncreate()方法中添加此行

new RetrieveFeedTask().execute(urlToRssFeed);

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

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

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

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

添加:

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

StrictMode.setThreadPolicy(policy);

在您的 class 中,

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

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

结果:

您的应用程序将(在互联网连接不稳定的区域)变得无响应并被锁定,用户认为速度很慢并且必须强制终止,并且您冒着活动管理器杀死您的应用程序并告诉用户应用程序已停止的风险。

Android 有一些关于设计响应性的良好编程实践的好技巧: NetworkOnMainThreadException | Android 开发人员

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

Thread thread = new Thread(new Runnable() {

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

thread.start(); 

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

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

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

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

IntentService 方法

缺点:

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

优点:

  • 避免短期 memory 泄漏问题
  • 如果您的活动在网络操作正在进行时重新启动,它仍然可以通过其onActivityResult方法接收下载结果
  • 一个比 AsyncTask 更好的平台来构建和重用强大的网络代码。 示例:如果您需要进行重要的上传,您可以从Activity中的AsyncTask进行,但如果用户上下文切换到应用程序之外接听电话,系统可能会在上传完成之前终止应用程序。 杀死具有活动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 object,服务将使用它返回结果:

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项目可 在此处获得。

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

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

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

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

并将以下导入语句放入您的 Java 文件中。

import android.os.StrictMode;

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

例如:

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

并将其添加到文件AndroidManifest.xml

<uses-permission android:name="android.permission.INTERNET"/>
  1. 不要使用 strictMode(仅在调试模式下)
  2. 不改SDK版本
  3. 不要使用单独的线程

使用 Service 或 AsyncTask

另请参阅堆栈溢出问题:

android.os.NetworkOnMainThreadException 从 Android 发送 email

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

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

不推荐这样做:使用AsyncTask接口。

两种方法的完整代码

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

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

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

将您的代码放入:

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

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

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

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

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

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

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

该错误是由于在主线程中执行了长时间运行的操作,您可以使用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
    }
});

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

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

spektom 的最佳答案非常完美。

如果您正在编写AsyncTask内联而不是扩展为 class,除此之外,如果需要从AsyncTask中获取响应,可以使用get()方法,如下所示。

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

(来自他的例子。)

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

错误是 SDK 警告!

对我来说是这样的:

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

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

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

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

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

在您的活动中使用它

    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();
        }
    });

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

主线程基本上是 UI 线程。

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

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

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

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

Executors.newSingleThreadExecutor().submit(new Runnable() {
    @Override
    public void run() {
        // You can perform your task here.
    }
});

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

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

REST超过 HTTP

通常是 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)

Retrofit 来自 Square

This is an excellent choice for an API with multiple endpoints and allows you to declare the REST endpoints instead of having to code them individually as with other libraries like Amazon Ion Java or Volley (website: 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);
}

Class FinancesApiBuilder

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

If your API requires an API key or other header, like a user token, etc. to be sent, Retrofit makes this easy (see this awesome answer to Add Header Parameter in Retrofit for details).

一次性 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 需要比 Retrofit 更多的设置。 您需要像这样创建 class 来设置 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 的另一个优秀图书馆。 请参阅网站以获取一些很好的示例。

简单来说,

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

例如,如果您执行 HTTP 请求,则这是一个网络操作。

解决方案:

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

方法:

把你所有的作品都放进去

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

但:

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

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

如何

  1. 使用 AsyncTask 时,从onPostExecute()方法更新视图
  2. 或者调用runOnUiThread()方法并更新run()方法中的视图。

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

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

Java 线程或 Android HandlerThread

Java 线程是一次性使用的,并且在执行其运行方法后死亡。

HandlerThread 是一个方便的 class ,用于启动具有弯针的新线程。

AsyncTask (在 API 级别 30 中已弃用

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

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

【执行器框架】

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

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

未来任务

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

异步任务加载器

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

意向服务

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

作业调度器

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

RxJava

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

协程(Kotlin)

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

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

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

理想情况下, AsyncTask应该用于短操作。 普通Thread不适用于 Android。

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

处理线程

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

处理程序:

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

解决方案:

  1. 创建HandlerThread

  2. HandlerThread上调用start()

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

  4. Runnable object 中嵌入您的网络操作相关代码

  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将被销毁并为下一次网络操作重新创建。 但是使用HandlerHandlerThread方法,您可以使用Handler将许多网络操作(作为 Runnable 任务)提交给单个HandlerThread

Kotlin

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

fun doSomeNetworkStuff() {
    GlobalScope.launch(Dispatchers.IO) {
        // ...
    }
}

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

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

虽然上面有一个巨大的解决方案池,但没有人提到com.koushikdutta.ion : https://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
    }
});

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

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

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

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

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

您可以使用KotlinAnko

KotlinAndroid的新官方语言。 您可以在此处找到更多信息: Kotlin for Android

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

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

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

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

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();

不允许在 Android 上的 UI 线程上实现网络操作。 You will have to use AsyncTask class to perform network related operations like sending API request, downloading image from a URL, etc. and using callback methods of AsyncTask, you can get you result in onPostExecute menthod and you will be in the UI thread and you可以使用来自 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 权限。 它会像魅力一样工作。 :)

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

在 Android 上,网络操作无法在主线程上运行。 您可以使用ThreadAsyncTask (短期运行任务)、 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);
}

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

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

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

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

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

但最佳实践是使用 AsyncTask。

如何修复 android.os.NetworkOnMainThreadException

什么是 NetworkOnMainThreadException:

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

如何修复它:

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

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();
}

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

随时随地拨打电话

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)

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

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

而且效果很好。

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

来自开发者安卓

理想情况下,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是一个帮助器 class 使这个过程更容易。 这个 class 管理一组线程的创建,设置它们的优先级,并管理工作在这些线程之间的分配方式。 随着工作负载的增加或减少,class 会启动或销毁更多线程以适应工作负载。

有关 Android 线程的更多信息,请参阅内容。

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

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

或者

使用ServiceAsyncTask来解决这个问题

笔记:

Do not change SDK version
Do not use a separate thread

有关更多信息, 请查看此

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

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

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

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

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

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

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

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

doAsync {
    method()
}

您可以使用 Kotlin协程

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

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

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

这些答案需要更新以使用更现代的方式连接到 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返回的值。

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

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

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

Android Jetpack引入WorkManager ,修复了Android 8.1 (Oreo) 后台服务限制问题, Android 5.0以下使用Alarm Manager和JobScheduler 5.0以上)

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

我将网络访问 function 返回值转换为暂停 function,如下所示:


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

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

...
Globalscope.async{
  ...
  result = isInternetReachable()
  ...
}
...

永远不要在 UI 线程上做任何长时间运行的工作。 长时间运行的工作可以是与服务器的通信、文件的读/写等。这些任务应该在后台线程上。 这就是创建ServiceAsyncTaskThreads的原因。 您可以禁用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

这里有更多关于StrictMode的信息: StrictMode | Android 开发人员

截至 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) 
    }

不同的选择:

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

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

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)

这是一个使用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();

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

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

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

Executors.newFixedThreadPool(3).execute(() -> {
      //DO Task;        
 });

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

// 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();
        }
    }
}

您只需在清单标记后的文件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);
}

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

可能需要在向用户显示 webview 之前检查实际的 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);

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM