问题:如何修复'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

如何解决此问题?

标签:java,android,android-networking,networkonmainthread

回答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"/>

回答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

回答3:

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

Thread thread = new Thread(new Runnable() {

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

thread.start(); 

回答4:

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

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

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

  1. 使用一个可以为您做得很好的库-此问题
  2. 改为使用ServiceIntentService,或者使用PendingIntent通过活动的onActivityResult返回结果方法。

IntentService方法

缺点:

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

优势:

  • 避免短期内存泄漏问题
  • 如果在进行网络操作时您的活动重新开始,它仍然可以通过其onActivityResult方法
  • 接收下载结果
  • 比AsyncTask更好的平台来构建和重复使用强大的网络代码。示例:如果您需要进行重要的上传,则可以通过Activity中的AsyncTask进行,但是如果用户上下文切换出应用程序可以接电话调用后,系统 可能会在上传完成之前杀死该应用。使用有效的Service服务杀死应用程序的可能性较小。
  • 如果您使用自己的并发版本的IntentService(例如我上面链接的版本),则可以通过Executor控制并发级别。

实施摘要

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

步骤1:创建一个IntentService来执行下载。您可以通过Intent extra告诉它要下载的内容,然后将其传递给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步:从Activity调用服务,并传递一个PendingResult对象,服务将使用该对象返回结果:

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

第4步:在onActivityResult中处理结果:

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

可在此处的Github项目中使用完整的Android-Studio / Gradle项目。 a>。

回答5:

您无法在 I / O " https://en.wikipedia.org/wiki/Android_version_history#Android_3.0_Honeycomb_.28API_level_11.29" rel="noreferrer">蜂窝。从技术上讲,它在早期版本的Android上可能是 ,但这是一个非常糟糕的主意,因为它将导致您的应用程序停止响应,并可能导致操作系统因行为不当而杀死您的应用程序。您需要运行后台进程或使用AsyncTask在后台线程上执行网络事务。

有一篇关于无痛线程的文章开发人员站点,对此进行了很好的介绍,它将为您提供比此处实际提供的更好的答案深度。

回答6:

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

使用服务或AsyncTask

另请参阅堆栈溢出问题:

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

回答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"/>

回答8:

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

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

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

这两种方法的完整代码

回答9:

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

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

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

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

回答11:

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

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

回答12:

可以使用 Android注释。它可以让您在后台线程中简单地运行任何方法:

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

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

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

回答13:

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

回答14:

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

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

回答15:

spektom的答案非常完美。< / p>

如果您正在编写内联的AsyncTask而不是作为一个类进行扩展,那么在此之上,如果需要从AsyncTask中获取响应,则可以使用get()方法,如下所示。

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

(以他的例子为例)

回答16:

仅针对定位 Honeycomb SDK的应用程序会抛出此错误或更高。面向早期SDK版本的应用程序可以在其主事件循环线程上进行联网。

错误是SDK警告!

回答17:

对我来说是这样的

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

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

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

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

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

回答18:

在您的活动中使用它

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

回答19:

只需明确地阐明一些内容:

主线程基本上是UI线程。

因此,您不能在主线程中执行网络操作意味着您无法在UI线程中进行网络操作,这意味着您不能在*runOnUiThread(newRunnable(){..} * 在其他线程中。

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

回答20:

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

为避免这种情况,我们可以使用线程执行程序

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

回答21:

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

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

通过HTTP的ReST

Json通常可以是XML或其他

完整的API访问权限

比方说,您正在编写一个可让用户跟踪股价,利率和汇率汇率的应用程序。您会发现一个看起来像这样的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)

从Square改装

对于具有多个端点的API而言,这是一个绝佳的选择,它使您可以声明ReST端点,而不必像对其他库(如ion或Volley)那样单独编码它们。 (网站: http://square.github.io/retrofit/ )

您如何将其与Finances 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密钥或其他标头(例如用户令牌等),则Retrofit可以使此操作变得容易(有关详细信息,请参见以下真棒答案: https://stackoverflow.com/a/42899766/1024412 )。

一次性获得ReST API访问权限

比方说,您正在构建一个"天气"应用程序,该应用程序可查找用户的GPS位置并检查该区域的当前温度并告诉他们心情。这种类型的应用程序无需声明API端点;它只需要能够访问一个API端点即可。

离子

这是用于此类访问的出色库。

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

通过HTTP加载图像

凌空

Volley也可以用于ReST API,但是由于需要更复杂的设置,因此我更喜欢使用Square的Retrofit,如上所述( http://square.github.io/retrofit/ )

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

build.gradle

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

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

ImageFetch.java

排球比翻新需要更多的设置。您将需要创建一个类似这样的类来设置RequestQueue,ImageLoader和ImageCache,但这还不错:

public class ImageFetch {
    private static ImageLoader imageLoader = null;
    private static RequestQueue imageQueue = null;

    public static ImageLoader getImageLoader(Context ctx){
        if(imageLoader == null){
            if(imageQueue == null){
                imageQueue = Volley.newRequestQueue(ctx.getApplicationContext());
            }
            imageLoader = new ImageLoader(imageQueue, new ImageLoader.ImageCache() {
                Map<String, Bitmap> cache = new HashMap<String, Bitmap>();
                @Override
                public Bitmap getBitmap(String url) {
                    return cache.get(url);
                }
                @Override
                public void putBitmap(String url, Bitmap bitmap) {
                    cache.put(url, bitmap);
                }
            });
        }
        return imageLoader;
    }
}

user_view_dialog.xml

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

<com.android.volley.toolbox.NetworkImageView
    android:id="@+id/profile_picture"
    android:layout_width="32dp"
    android:layout_height="32dp"
    android:layout_alignParentTop="true"
    android:layout_centerHorizontal="true"
    app:srcCompat="@android:drawable/spinner_background"/>

UserViewDialog.java

将以下代码添加到onCreate方法(Fragment,Activity)或构造函数(Dialog)中:

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

毕加索

另一个来自Square的优秀图书馆。请参见该网站上的一些示例: http://square.github.io/picasso/ < / p>

回答22:

简单地说,

请勿在用户界面中进行网络连接

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

解决方案:

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

方式:

将您所有的作品放入其中

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

但是:

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

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

如何?

  1. 使用AsyncTask时,通过onPostExecute()方法更新视图
  2. 致电 runOnUiThread() 方法并在run()方法中更新视图。

回答23:

您可以将代码的一部分移到另一个线程中,以卸载主线程,并避免获得 ANR NetworkOnMainThreadException IllegalStateException (例如,无法访问主线程上的数据库因为它可能会长时间锁定用户界面。

根据情况应选择一些方法

Java 线程或Android HandlerThread

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

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

AsyncTask

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

由于主线程垄断了UI组件,因此无法访问某些View,这就是Handler急于求助的原因

线程池实现 ThreadPoolExecutor ScheduledThreadPoolExecutor ...

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

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

FutureTask

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

AsyncTaskLoaders

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

IntentService

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

JobScheduler

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

RxJava

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

协程(科特琳)

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

了解更多信息此处< / a>,此处此处此处

回答24:

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

回答25:

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

理想情况下,

AsyncTask应该用于简短操作。普通的Thread在Android上不是首选。

使用 HandlerThread 处理程序

HandlerThread

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

处理程序:

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

解决方案:

  1. 创建HandlerThread

  2. HandlerThread

  3. 上调用start()
  4. HanlerThread

  5. 中获取Looper,从而创建Handler
  6. 将与网络运营相关的代码嵌入到Runnable对象

  7. 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提交许多网络操作(作为可运行任务)到单个HandlerThread。代码>。

回答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. 通过指定(Schedulers.io()),RxAndroid将在另一个线程上运行getFavoriteMusicShows()

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

回答27:

这有效。只是让路易博士的答案更简单了。

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

回答28:

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

回答29:

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

回答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的优秀电子书

回到顶部