如何解决“android.os.NetworkOnMainThreadException”?(How

2019-10-20 11:35发布

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

我怎样才能解决这个问题?

Answer 1:

当应用程序试图在其主线程进行联网操作,抛出此异常。 运行在你的代码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"/>


Answer 2:

你应该总是在一个线程或异步任务运行的网络操作。

但是有可能取消这一限制,并覆盖了默认的行为,如果你愿意接受的后果。

加:

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

StrictMode.setThreadPolicy(policy); 

在你的类,

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

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

后果:

你的应用程序将(在参差不齐的互联网连接的区域)变得反应迟钝和锁定,用户感知缓慢,并有做力杀了,你的风险行为管理杀死你的应用程序,并告诉该应用程序已停止用户。

Android有良好的编程习惯一些好的建议以设计为响应: http://developer.android.com/reference/android/os/NetworkOnMainThreadException.html



Answer 3:

我采用了全新的解决了这个问题Thread

Thread thread = new Thread(new Runnable() {

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

thread.start(); 


Answer 4:

该接受的答案有一些显著下跌的侧部。 这是不建议使用的AsyncTask联网,除非你真的知道自己在做什么。 一些向下的侧部包括:

  • 的AsyncTask的创建为非静态内部类必须封闭活动对象,它的上下文,并且由该活动创建的整个视图层级的隐式引用。 该参考防止活动被当作垃圾收集,直到的AsyncTask的后台工作完成。 如果定向多次改变,例如(和你不取消执行任务),或用户导航 - 如果用户的连接速度很慢,和/或下载大,这些短期内存泄漏会成为一个问题离活动。
  • 的AsyncTask具有取决于平台不同的执行特性它执行上:前API级别4 AsyncTasks在单个后台线程串行执行; 从通过API级10 API级别4,AsyncTasks执行多达128个线程的池; 从API级11起的AsyncTask串行执行在单个后台线程(除非使用重载executeOnExecutor方法,并提供一种替代执行人)。 上连续运行ICS时,在姜饼并发执行可能会破坏时正常工作守则,说,如果有订单的执行中无意依赖。

如果你想避免短期内存泄漏,拥有明确的执行特性在所有平台上,并有一个基地建立真正强大的网络处理,你可能要考虑:

  1. 使用该做的这个给你一个很好的工作,一个图书馆-有在网络库的一个很好的比较这个问题 ,或
  2. 使用ServiceIntentService代替,也许用PendingIntent通过活动的返回结果onActivityResult方法。

IntentService方法

下行方面:

  • 更多的代码和复杂性比AsyncTask ,但并不像你想象
  • 将排队的请求,并在单个后台线程运行它们。 您可以轻松地通过更换控制这个IntentService具有同等Service的实现,或许像这一个 。
  • 嗯,我想不出任何别人的,现在居然

向上方面:

  • 避免了短期内存泄漏问题
  • 如果在网络操作您的活动重新启动在飞行中它仍然可以通过它接收下载的结果onActivityResult方法
  • 更好的平台比的AsyncTask建立和再利用强大的网络代码。 例如:如果你需要做一个重要的上传,你可以从做它AsyncTaskActivity ,但如果用户的上下文切换出的应用可以采取电话呼叫,上传完成前系统可以终止该应用。 这是不太可能杀死活跃的应用程序Service
  • 如果你用你自己的并发版本IntentService (像我上面链接),你可以控制通过并发的水平Executor

执行摘要

你可以实现一个IntentService对单个后台线程进行下载很容易。

第1步:创建一个IntentService进行下载。 你可以告诉它通过下载Intent额外的,并通过它PendingIntent使用,让结果返回到Activity

import android.app.IntentService;
import android.app.PendingIntent;
import android.content.Intent;
import android.util.Log;

import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;

public class DownloadIntentService extends IntentService {

    private static final String TAG = DownloadIntentService.class.getSimpleName();

    public static final String PENDING_RESULT_EXTRA = "pending_result";
    public static final String URL_EXTRA = "url";
    public static final String RSS_RESULT_EXTRA = "url";

    public static final int RESULT_CODE = 0;
    public static final int INVALID_URL_CODE = 1;
    public static final int ERROR_CODE = 2;

    private IllustrativeRSSParser parser;

    public DownloadIntentService() {
        super(TAG);

        // make one and re-use, in the case where more than one intent is queued
        parser = new IllustrativeRSSParser();
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        PendingIntent reply = intent.getParcelableExtra(PENDING_RESULT_EXTRA);
        InputStream in = null;
        try {
            try {
                URL url = new URL(intent.getStringExtra(URL_EXTRA));
                IllustrativeRSS rss = parser.parse(in = url.openStream());

                Intent result = new Intent();
                result.putExtra(RSS_RESULT_EXTRA, rss);

                reply.send(this, RESULT_CODE, result);
            } catch (MalformedURLException exc) {
                reply.send(INVALID_URL_CODE);
            } catch (Exception exc) {
                // could do better by treating the different sax/xml exceptions individually
                reply.send(ERROR_CODE);
            }
        } catch (PendingIntent.CanceledException exc) {
            Log.i(TAG, "reply cancelled", exc);
        }
    }
}

第2步:注册清单中的服务:

<service
        android:name=".DownloadIntentService"
        android:exported="false"/>

步骤3:从活动调用服务,传递该服务将使用返回结果的PendingResult对象:

PendingIntent pendingResult = createPendingResult(
    RSS_DOWNLOAD_REQUEST_CODE, new Intent(), 0);
Intent intent = new Intent(getApplicationContext(), DownloadIntentService.class);
intent.putExtra(DownloadIntentService.URL_EXTRA, URL);
intent.putExtra(DownloadIntentService.PENDING_RESULT_EXTRA, pendingResult);
startService(intent);

步骤4:处理结果在onActivityResult:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == RSS_DOWNLOAD_REQUEST_CODE) {
        switch (resultCode) {
            case DownloadIntentService.INVALID_URL_CODE:
                handleInvalidURL();
                break;
            case DownloadIntentService.ERROR_CODE:
                handleError(data);
                break;
            case DownloadIntentService.RESULT_CODE:
                handleRSS(data);
                break;
        }
        handleRSS(data);
    }
    super.onActivityResult(requestCode, resultCode, data);
}

包含一个完整的工作Android的工作室/ gradle这个项目一个项目的GitHub可以在这里 。



Answer 5:

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

有一个关于文章无痛线程在Android开发者网站是一个很好的介绍这一点,这将为您提供比这里可以逼真地给出了一个答案的一个更好的深度。



Answer 6:

  1. 不要使用strictMode(仅在调试模式下)
  2. 不要更改SDK版本
  3. 不要使用一个单独的线程

使用服务或的AsyncTask

另请参见堆栈溢出问题:

android.os.NetworkOnMainThreadException从安卓发送电子邮件



Answer 7:

请在另一个线程的网络行为

例如:

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

而这增加的AndroidManifest.xml

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


Answer 8:

您禁用使用下面的代码严格模式:

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

这是不推荐 :使用AsyncTask接口。

对于这两种方法的完整代码



Answer 9:

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

这是你如何运行在一个子线程任务:

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


Answer 10:

把你的代码中:

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


Answer 11:

使用Android的注解是一种选择。 它可以让你简单地运行在后台线程的任何方法:

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

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

需要注意的是,虽然它提供了简单性和可读性的优点,它有它的缺点。



Answer 12:

这发生在安卓3.0及以上。 在Android 3.0及以上,他们已经利用从主线程/ UI线程中运行的网络操作(即上网功能)的限制(从什么滋生创建并在活动中恢复的方法)。

这是使用单独的线程进行网络操作,以鼓励。 见的AsyncTask有关如何执行网络活动的正确方式的更多细节。



Answer 13:

该错误是由于在主线程中执行长时间运行的操作,您可以轻松地通过使用纠正问题AsynTask或螺纹 。 您可以检出该库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
    }
});


Answer 14:

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

为了简化这些东西,Android提供了多种方式,像AsyncTaskAsyncTaskLoaderCursorLoaderIntentService 。 所以,你可以使用根据您的要求任何这些。



Answer 15:

顶部spektom的答案可以完美运行。

如果你正在写的AsyncTask内联,而不是扩展为一类,并在此之上,如果有需要走出的响应AsyncTask ,一个可以使用get()方法如下。

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

(从他的例子。)



Answer 16:

这只是抛出应用针对蜂窝 SDK或更高。 针对早期SDK版本的应用程序被允许做自己的主事件循环线程联网。

该错误是SDK的警告!



Answer 17:

对我来说是这样的:

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

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

使确保目标的版本是一样的Android目标库。 如果你不能确定你的目标库是什么,右键点击你的项目- > 构建路径 - > Android的 ,它应该是被选中的人。

此外,如其他人所说,包括正确的权限来访问互联网:

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


Answer 18:

只是为了明确地拼出了一句:

主线程是基本的UI线程。

所以说,你不能在主线程中做网络操作意味着你不能做的UI线程,这意味着你不能在一个网络操作的网络操作*runOnUiThread(new Runnable() { ... }*块中的一些其他线程内,无论是。

(我只是有一个长头刮时刻努力,为什么我得到这个错误的地方比我的主线程其他弄清楚这是为什么。这个线程帮助;并希望此评论将帮助其他人)



Answer 19:

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

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

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


Answer 20:

在你的活动中使用此

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


Answer 21:

当地有很多的答案已经在这个问题上,但很多伟大的图书馆已经出来了,因为这些问题的答案被张贴。 这是为了作为一种新手指导的。

我将涵盖执行网络操作解决方案或双方各持两份几个用例。

REST通过HTTP

通常JSON,可以是XML或别的东西,

完整的API访问

比方说,你正在编写一个应用程序,允许用户跟踪股票价格,利率和currecy汇率。 你会发现一个Json的API,它看起来是这样的:

http://api.example.com/stocks                       //ResponseWrapper<String> object containing a list of Srings 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)

从广场改造

这对于具有多个端点的API一个很好的选择,并允许你声明,而不必个别地它们与等离子或排球其他库代码的其余部分的端点。 (网址: http://square.github.io/retrofit/ )

你如何与财政API使用它呢?

的build.gradle

这些行添加到您的模块级buid.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

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

FinancesApiBuilder

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密钥或其他类似标题用户令牌等发送,改造,使这个容易(详见本真棒答案: https://stackoverflow.com/a/42899766/1024412 )。

一次性REST API访问

比方说,你正在构建一个“情绪天气”应用中查找用户的GPS位置,在这方面检查当前温度,并告诉他们的心情。 这种类型的应用程序并不需要声明API端点; 它只是需要能够访问一个API端点。

离子

这是这种类型的访问一个伟大的图书馆。

请阅读msysmilu的伟大答案( https://stackoverflow.com/a/28559884/1024412 )

通过HTTP加载图片

齐射

凌空也可用于REST API的,但由于更复杂的设置需要我更喜欢使用改造从广场如上( http://square.github.io/retrofit/ )

比方说,你正在建设一个社交网络应用程序,并希望加载好友的个人资料照片。

的build.gradle

该行添加到您的模块级buid.gradle:

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

ImageFetch.java

凌空需要比改造更设置。 您需要创建这样一个类来建立一个请求队列,一个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方法(片段,活动)或构造(对话框):

NetworkImageView profilePicture = view.findViewById(R.id.profile_picture);
profilePicture.setImageUrl("http://example.com/users/images/profile.jpg", ImageFetch.getImageLoader(getContext());

毕加索

从广场另一优库。 请参阅网站的一些很好的例子: http://square.github.io/picasso/



Answer 22:

简单地说,

DO NOT DO网络工作在UI线程

例如,如果你做一个HTTP请求,这是一个网络行动。

解:

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

方式:

把你所有的作品中

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

但:

当你从网络反应的东西,要显示它您的视图(如在TextView中显示响应消息),你需要要返回到UI线程。

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

如何?

  1. 同时使用的AsyncTask,从更新视图onPostExecute()方法
  2. 致电runOnUiThread()内的方法和更新视图run()方法。


Answer 23:

尽管上面有一个巨大的解决方案库,没有人提到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
    }
});


Answer 24:

新的Thread和的AsyncTask解决方案已经进行了说明。

AsyncTask应该理想地用于短操作。 普通Thread是不可取的Android。

看看使用替代解决方案HandlerThread和处理程序

HandlerThread

手持类开始,有一个弯一个新的线程。 然后,将弯针可以用来创建处理程序类。 需要注意的是start()仍必须调用。

处理器:

一个处理程序允许您发送和处理信息,并与一个线程的MessageQueue关联Runnable对象。 每个处理程序实例与单个线程和线程的消息队列相关联。 当你创建一个新的处理程序,它被绑定到线程的线程/消息队列是创造它 - 从这一点上来说,这将提供信息和可运行到消息队列,并执行它们,因为他们出来的消息队列。

解:

  1. 创建HandlerThread

  2. 调用start()HandlerThread

  3. 创建Handler通过获得LooperHanlerThread

  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将被销毁,下一次网络运营重新创建。 但随着HandlerHandlerThread方法,您可以提交许多网络操作(如Runnable的任务),以单HandlerThreadHandler


Answer 25:

你可以在你的代码的一部分转移到另一个线程来卸载main thread和避免让ANR , NetworkOnMainThreadException , IllegalStateException异常 (例如,在主线程中无法访问数据库,因为它可能会锁定用户界面进行的很长一段时间)。

有一些,你应该选择的方法视情况而定

Java的线程或Android HandlerThread

Java线程只能一次性使用和执行它的run方法后死亡。

HandlerThread是开始,有一个弯一个新的线程,一个方便的类。

的AsyncTask

的AsyncTask的设计是围绕线程处理程序一个辅助类,并不构成通用线程框架。 AsyncTasks应该理想地(最多几秒钟。)可用于短期操作。如果您需要保持对长时间运行的线程,强烈建议您使用java.util.concurrent包提供,如各种API 遗嘱执行人 ,并ThreadPoolExecutor的 FutureTask。

线程池实现的ThreadPoolExecutor , 的ScheduledThreadPoolExecutor ...

实现的ExecutorService这给线程池的精细控制的ThreadPoolExecutor类(例如,核心池大小,最大池大小,保持活动时间等)

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

FutureTask

FutureTask进行异步处理,但是,如果结果是还没有准备好或者处理未完成,调用get()会阻塞线程

AsyncTaskLoaders

AsyncTaskLoaders为他们解决了很多所固有的问题的AsyncTask

IntentService

这是在Android上长时间运行的处理事实上的选择,一个很好的例子是上传或下载大型文件。 上传和下载可能会继续即使在用户退出应用程序,你肯定不希望从能够使用的应用程序,而这些任务会在阻止该用户。

作业调度

实际上,你必须创建一个服务,并创建一个使用工作JobInfo.Builder指定你为当运行的服务标准。

RxJava

图书馆通过使用观察序列构成异步和基于事件的程序。

协程 (科特林)

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

更多在这里 , 这里 , 这里 , 这里



Answer 26:

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

  2. 通过使用AndroidSchedulers.mainThread()我们要观察这个可观测的UI线程上,即我们希望我们的onNext()回调被称为UI线程上



Answer 27:

这工作。 刚才Dr.Luiji的回答更简单一点。

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


Answer 28:

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



Answer 29:

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



Answer 30:

没有为解决此问题的另一个非常方便的方法 - 使用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()调度的支持。

有对RX-java的一个很好的电子书 。



文章来源: How do I fix 'android.os.NetworkOnMainThreadException'?