关于java:如何修复android.os.NetworkOnMainThreadException?

How do I fix android.os.NetworkOnMainThreadException?

我在为rssreader运行android项目时出错。

代码:

1
2
3
4
5
6
7
8
9
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();

显示以下错误:

1
android.os.NetworkOnMainThreadException

如何解决此问题?


当应用程序尝试在其主线程上执行网络操作时,会引发此异常。在AsyncTask中运行代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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()方法中添加此行。

1
new RetrieveFeedTask().execute(urlToRssFeed);

别忘了把这个添加到AndroidManifest.xml文件中:

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


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

但是,如果您愿意接受后果的话,可以取消这个限制并覆盖默认行为。

添加:

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

StrictMode.setThreadPolicy(policy);

在你们班,

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

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

后果:

你的应用程序将(在不稳定的互联网连接区域)变得无响应和锁定,用户感觉缓慢,必须强制杀死,你冒着活动管理器杀死你的应用程序并告诉用户应用程序已经停止的风险。

Android在设计响应性的良好编程实践方面有一些很好的建议:http://developer.android.com/reference/android/os/networkonmainthreadexception.html


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

1
2
3
4
5
6
7
8
9
10
11
12
13
Thread thread = new Thread(new Runnable() {

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

thread.start();


公认的答案有一些明显的负面影响。不建议使用AsyncTask进行网络连接,除非您真正知道自己在做什么。下面的部分包括:

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

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

  • 使用一个为您做了很好工作的库-在这个问题中有一个很好的网络libs比较,或者
  • 使用ServiceIntentService,或者使用PendingIntent,通过活动的onActivityResult方法返回结果。
  • 使用方法

    下边:

    • AsyncTask更复杂的代码,尽管没有你想象的那么多
    • 将请求排队并在单个后台线程上运行它们。通过用等效的Service实现替换IntentService,您可以很容易地控制这一点,可能与此类似。
    • 嗯,我现在真的想不出其他人了

    上边:

    • 避免短期内存泄漏问题
    • 如果您的活动在网络运行期间重新启动,它仍然可以通过其onActivityResult方法接收下载结果。
    • 比AsyncTask更好的平台来构建和重用健壮的网络代码。例如:如果你需要做一个重要的上传,你可以在一个Activity中从AsyncTask进行上传,但是如果用户上下文切换到应用外接电话,系统可能会在上传完成之前终止应用。使用活动的Service不太可能终止应用程序。
    • 如果您使用自己的并发版本的IntentService(如我上面链接的版本),则可以通过Executor控制并发级别。

    实施总结

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

    第一步:创建一个IntentService来执行下载。您可以告诉它通过Intentextra下载什么,并通过PendingIntent将结果返回给Activity

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    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:在清单中注册服务:

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

    步骤3:从活动调用服务,传递服务将用于返回结果的挂起的结果对象:

    1
    2
    3
    4
    5
    6
    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中处理结果:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == RSS_DOWNLOAD_REQUEST_CODE) {
            switch (resultCode) {
                case DownloadIntentService.INVALID_URL_CODE:
                    handleInvalidURL();
                    break;
                case DownloadIntentService.ERROR_CODE:
                    handleError(data);
                    break;
                case DownloadIntentService.RESULT_CODE:
                    handleRSS(data);
                    break;
            }
            handleRSS(data);
        }
        super.onActivityResult(requestCode, resultCode, data);
    }

    这里有一个包含完整工作的Android Studio/Gradle项目的Github项目。


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

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


  • 不使用严格模式(仅在调试模式下)
  • 不更改SDK版本
  • 不要使用单独的螺纹
  • 使用服务或异步任务

    另请参见堆栈溢出问题:

    android.os.networkonmainthreadexception从android发送电子邮件


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

    For Example:

    1
    2
    3
    4
    5
    6
    new Thread(new Runnable(){
        @Override
        public void run() {
            // Do network action in this function
        }
    }).start();

    并将其添加到androidmanifest.xml

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


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

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

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

    两种方法的完整代码


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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    new Thread(new Runnable(){
        @Override
        public void run() {
            try {
                // Your implementation goes here
            }
            catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    }).start();


    将代码放入:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    new Thread(new Runnable(){
        @Override
        public void run() {
            try {
                // Your implementation
            }
            catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    }).start();

    或:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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注释是一种选择。它允许您简单地在后台线程中运行任何方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // normal method
    private void normal() {
        doSomething(); // do something in background
    }

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

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


    这在Android 3.0及更高版本中发生。在android 3.0及更高版本中,他们限制使用网络操作(访问互联网的功能)在主线程/ui线程中运行(由活动中的on-create和on-resume方法生成)。

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


    错误是由于在主线程中执行长时间运行的操作,您可以使用asyntask或thread轻松地纠正问题。您可以签出此库AsynchTtpClient以获得更好的处理。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    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而不是作为类进行扩展,并且在此基础上,如果需要从AsyncTask得到响应,可以使用下面的get()方法。

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

    (从他的例子中。)


    这只适用于针对蜂窝式SDK或更高版本的应用程序。针对早期SDK版本的应用程序可以在其主事件循环线程上进行网络连接。

    错误是SDK警告!


    对我来说是这样的:

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

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

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

    另外,正如其他人提到的,包括访问互联网的正确权限:

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


    只是想明确地说:

    主线程基本上是UI线程。

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

    (我只是有一个很长的抓耳挠腮的时刻,试图弄明白为什么我会在主线程之外的其他地方得到这个错误。这就是原因;这个线索有帮助;希望这个评论能帮助其他人。)


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

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

    1
    2
    3
    4
    5
    6
    Executors.newSingleThreadExecutor().submit(new Runnable() {
        @Override
        public void run() {
            // You can perform your task here.
        }
    });

    在你的活动中使用这个

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
        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();
            }
        });

    关于这个问题,已经有很多很好的答案了,但是自从这些答案被公布之后,已经有很多很棒的图书馆出现了。这是一种新手指南。

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

    搁置HTTP

    通常是JSON,可以是XML或其他

    完全API访问

    假设您正在编写一个应用程序,它允许用户跟踪股票价格、利率和货币汇率。您会发现一个JSON API,看起来像这样:

    1
    2
    3
    4
    5
    6
    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来说,这是一个很好的选择,它允许您声明其余的端点,而不必像使用其他库(如ion或volley)那样单独对它们进行编码。(网址:http://square.github.io/remodify/)

    如何将其与财务API结合使用?

    平地

    将这些行添加到模块级buid.gradle:

    1
    2
    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版

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    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);
    }

    财务顾问

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public class FinancesApiBuilder {
        public static FinancesApi build(String baseUrl){
            return new Retrofit.Builder()
                        .baseUrl(baseUrl)
                        .addConverterFactory(GsonConverterFactory.create())
                        .build()
                        .create(FinancesApi.class);
        }
    }

    财务片段片段

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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加载图像截击

    Volley也可以用于RESTAPI,但是由于需要更复杂的设置,我更喜欢使用上面的square的revention(http://square.github.io/revent/)

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

    平地

    将此行添加到模块级buid.gradle:

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

    爪哇

    凌空抽射比改装需要更多的设置。您需要创建一个这样的类来设置一个请求队列、一个ImageLoader和一个ImageCache,但这并不太糟糕:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    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;
        }
    }

    用户视图对话.xml

    将以下内容添加到布局XML文件以添加图像:

    1
    2
    3
    4
    5
    6
    7
    <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"/>

    用户视图对话框.java

    将以下代码添加到onCreate方法(片段、活动)或构造函数(对话框)中:

    1
    2
    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/


    简单来说,

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

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

    解决方案:

  • 你必须创建一个新线程
  • 或使用AsyncTask类
  • 方式:

    把你所有的作品都放进去

  • 新螺纹的run()
  • 或异步任务类的doInBackground()方法。
  • 但是:

    当您从网络响应中得到一些东西并希望在视图中显示它时(如在textview中显示响应消息),您需要返回到UI线程。

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

    如何?

  • 使用AsyncTask时,从onPostExecute()方法更新视图
  • 或者调用runOnUiThread()方法,在run()方法内部更新视图。

  • 虽然上面有一个巨大的解决方案池,但没有人提到com.koushikdutta.ion:https://github.com/koush/ion

    它也是异步的,而且使用非常简单:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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
        }
    });

    新的Thread和异步任务解决方案已经解释过了。

    AsyncTask最好用于短期作战。普通的Thread不适合安卓系统。

    使用handlerThread和handler查看其他解决方案

    手螺纹

    Handy class for starting a new thread that has a looper. The looper can then be used to create handler classes. Note that start() must still be called.

    汉德勒:

    A Handler allows you to send and process Message and Runnable objects associated with a thread's MessageQueue. Each Handler instance is associated with a single thread and that thread's message queue. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it -- from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.

    解决方案:

  • 创建HandlerThread

  • HandlerThread上呼叫start()

  • HanlerThread获取Looper创建Handler

  • Runnable对象中嵌入网络操作相关代码

  • Handler提交Runnable任务

  • 示例代码段,地址为NetworkOnMainThreadException

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    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 +"
    "
    );
                }
                Log.d("Ravi","After IO call");
                Log.d("Ravi",text.toString());

            }catch( Exception err){
                err.printStackTrace();
            }
        }
    };
    mainHandler.post(myRunnable);

    使用这种方法的优点:

  • 为每个网络操作创建新的Thread/AsyncTask是昂贵的。Thread/AsyncTask将被销毁,并为下一个网络操作重新创建。但是使用HandlerHandlerThread方法,可以通过使用Handler向单个HandlerThread提交许多网络操作(作为可运行任务)。

  • RxAndroid是这个问题的另一个更好的选择,它可以避免创建线程和在Android UI线程上发布结果的麻烦。我们只需要指定需要在哪些线程上执行任务,并且在内部处理所有事情。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    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);
        }
    });
  • 通过指定(Schedulers.io()),rxandroid将在不同的线程上运行getFavoriteMusicShows()

  • 通过使用AndroidSchedulers.mainThread(),我们希望在UI线程上观察到这种可观察的情况,也就是说,我们希望在UI线程上调用onNext()回调


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

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

    Java线程或Android手线程

    Java threads are one-time use only and die after executing its run method.

    HandlerThread is a handy class for starting a new thread that has a looper.

    异步任务

    AsyncTask is designed to be a helper class around Thread and Handler and does not constitute a generic threading framework. AsyncTasks should ideally be used for short operations (a few seconds at the most.) If you need to keep threads running for long periods of time, it is highly recommended you use the various APIs provided by the java.util.concurrent package such as Executor, ThreadPoolExecutor and FutureTask.

    线程池实现threadpoolExecutor,scheduledthreadpoolExecutor…

    ThreadPoolExecutor class that implements ExecutorService which gives fine control on the thread pool (Eg, core pool size, max pool size, keep alive time, etc.)

    ScheduledThreadPoolExecutor - a class that extends ThreadPoolExecutor. It can schedule tasks after a given delay or periodically.

    未来任务

    FutureTask performs asynchronous processing, however, if the result is not ready yet or processing has not complete, calling get() will be block the thread

    异步任务加载器

    AsyncTaskLoaders as they solve a lot of problems that are inherent to AsyncTask

    智能服务

    This is the defacto choice for long running processing on Android, a good example would be to upload or download large files. The upload and download may continue even if the user exits the app and you certainly do not want to block the user from being able to use the app while these tasks are going on.

    作业调度程序

    Effectively, you have to create a Service and create a job using JobInfo.Builder that specifies your criteria for when to run the service.

    RXJava

    Library for composing asynchronous and event-based programs by using observable sequences.

    共济会(Kotlin)

    The main gist of it is, it makes asynchronous code looks so much like synchronous

    阅读更多信息:
    Android异步处理和计数的8种方法< BR>Android网络接入的发展在Android中使用线程池
    管理线程和自定义服务


    这是可行的。只是让Luiji医生的回答简单了一点。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    new Thread() {
        @Override
        public void run() {
            try {
                //Your code goes here
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }.start();

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


    在Android上,网络操作不能在主线程上运行。您可以使用线程、异步任务(短运行任务)、服务(长运行任务)来执行网络操作。


    解决这个问题还有另一个非常方便的方法——使用RxJava的并发功能。您可以在后台执行任何任务,并以非常方便的方式将结果发布到主线程,因此这些结果将被传递到处理链。

    第一个经过验证的回答建议是使用asyntask。是的,这是一个解决方案,但现在已经过时了,因为周围有新的工具。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    String getUrl() {
        return"SomeUrl";
    }

    private Object makeCallParseResponse(String url) {
        return null;
        //
    }

    private void processResponse(Object o) {

    }

    geturl方法提供了url地址,它将在主线程上执行。

    makeCallParseResponse(..)-实际工作

    processResponse(..)-将处理主线程上的结果。

    异步执行的代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    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文件中包含以下行:

    1
    2
       compile 'io.reactivex:rxjava:1.1.5'
       compile 'io.reactivex:rxandroid:1.2.0'

    最后一个依赖项包括对.mainthread()调度程序的支持。

    有一个优秀的电子书为RX Java。


    您可以使用KOTLINANKO

    Kotlin是Android的新官方语言,更多信息请参见此处。https://kotlinlang.org/docs/tutorials/kotlin-android.html网站

    Anko支持Android中Kotlin的库,这里是一些文档网址:https://github.com/kotlin/anko

    这个解决方案非常有用,只有@antonioleiva编写的几行代码https://antonioleiva.com/anko-background-kotlin-android/安卓/

    1
    2
    3
    4
    5
    6
    doAsync {
        var result = runLongTask()
        uiThread {
            toast(result)
        }
    }

    简单地说,NetworkOnMainThread发生在你在UI Thread上运行后台作业时,所以你要做的一件事就是在后台运行longTask job。你可以在你的Android应用程序中使用这个方法和Kotlin和Anko。


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

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

    使用异步任务的解决方案:分别是。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
        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权限。它会很有魅力。:)


    主线程是UI线程,您不能在主线程中执行可能会阻止用户交互的操作。你可以用两种方法来解决这个问题:

    强制在主线程中执行这样的任务

    1
    2
    StrictMode.ThreadPolicy threadPolicy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
    StrictMode.setThreadPolicy(threadPolicy);

    或者创建一个简单的处理程序并根据需要更新主线程。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    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);

    要停止线程,请使用:

    1
    newHandler.removeCallbacks(runnable);

    有关更多信息,请查看:无痛线程


    您还可以使用下面的代码使用严格模式来解决此问题。这也是解决这个问题的另一种选择。

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

    但最好的做法是使用AsyncTask。


    如何修复android.os.networkonmainthreadexception

    什么是网络维护例外:

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

    如何修复:

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


    我用一种简单的方法解决了这个问题…

    我在oncreateStrictMode.enableDefaults();之后加了一句,解决了这个问题。

    ServiceAsyncTask来解决这个问题。

    注:

    1
    2
    Do not change SDK version
    Do not use a separate thread

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


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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
        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();
    }

    当应用程序尝试在其主线程上执行网络操作时,会引发此异常。如果你的任务超过5秒,就需要一个接近的部队。

    AsyncTask中运行代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    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
        }
    }

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

    1
    2
    3
    4
    5
    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在一个线程上工作,所以您不应该在主线程上执行任何网络操作。有多种方法可以避免这种情况。

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

    • asysnctask:对于不需要太多时间的小型操作。
    • 意向服务:用于需要大量时间的网络运营。
    • 使用自定义库(如截击和翻新)进行处理复杂的网络操作

    永远不要使用strictmode.setthreadpolicy(policy),因为它会冻结您的用户界面,一点也不好。


    不能在主线程或UI线程上调用网络。在Android上,如果你想呼叫网络,有两种选择-

  • 调用AsyncTask,它将运行一个后台线程来处理网络操作。
  • 您可以创建自己的可运行线程来处理网络操作。
  • 我个人更喜欢AsyncTask。有关更多信息,请参阅此链接。


    你可以开始一个新的线程,我以前有过这个问题,用这种方法解决了它。


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

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


    如果你在科特林和安科工作,你可以添加

    1
    2
    3
     doAsync {
       method()
    }


    您可以使用Kotlin-coroutines

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     class YoutActivity : AppCompatActivity, CoroutineScope {

          override fun onCreate(...) {
             launch {  yourHeavyMethod() }
          }

          suspend fun yourHeavyMethod() {
             async{ yourNetworkCall() }.await()
             ...
             ...
          }

     }

    You can follow this guide.


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

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

    1
    2
    3
    4
    StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
                .detectAll()
                .penaltyLog()
                .build());

    你可以设定不同的惩罚-

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

    有很多关于https://developer.android.com/reference/android/os/strictmode.html的内容


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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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)
        }


    不同的选项:

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

  • IntentService/Async任务可用于获取网络响应后要更新UI的情况。


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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    // 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中的manifest标记后添加以下行

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

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

    1
    2
    3
    4
    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服务器上可以禁用Wi-Fi ping,因此在这种情况下,可以使用HTTP连接检查请求的状态。

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

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

    1
    2
    StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitNetwork().build();
    StrictMode.setThreadPolicy(policy);