如何修复“ 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

如何解决此问题?


#1楼

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

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

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

两种方法的完整代码


#2楼

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

Thread thread = new Thread(new Runnable() {

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

thread.start(); 

#3楼

spektom的最高答案很完美。

如果您正在编写内联而不是作为类扩展的AsyncTask ,并且最重要的是,如果需要从AsyncTask获取响应,则可以使用如下的get()方法。

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

(以他的例子为例。)


#4楼

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

使用服务或AsyncTask

另请参阅堆栈溢出问题:

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


#5楼

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

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


#6楼

对我来说是这样的:

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

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

确保目标版本与您的Android目标库相同。 如果不确定目标库是什么,请右键单击Project-> Build Path- > Android ,然后将其选中。

另外,正如其他人提到的那样,请包括访问Internet的正确权限:

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

#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楼

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

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


#9楼

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

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

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

#10楼

公认的答案有一些明显的缺点。 除非您真的知道自己在做什么,否则不建议使用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通过Activity的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);
}

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


#11楼

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

错误是SDK警告!


#12楼

在您的活动中使用它

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

#13楼

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

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

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

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


#14楼

只是要明确说明一些内容:

主线程基本上是UI线程。

因此,说您无法在主线程中执行联网操作意味着您无法在UI线程中执行联网操作,这意味着您无法在其他线程内*runOnUiThread(new Runnable() { ... }*块中进行联网操作。 ,或者。

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


#15楼

该错误是由于在主线程中执行了长时间运行的操作引起的,您可以使用AsynTaskThread轻松纠正该问题。 您可以检出此库AsyncHTTPClient以获得更好的处理。

AsyncHttpClient client = new AsyncHttpClient();
client.get("http://www.google.com", new AsyncHttpResponseHandler() {

    @Override
    public void onStart() {
        // Called before a request is started
    }

    @Override
    public void onSuccess(int statusCode, Header[] headers, byte[] response) {
        // Called when response HTTP status is "200 OK"
    }

    @Override
    public void onFailure(int statusCode, Header[] headers, byte[] errorResponse, Throwable e) {
        // Called when response HTTP status is "4XX" (for example, 401, 403, 404)
    }

    @Override
    public void onRetry(int retryNo) {
        // Called when request is retried
    }
});

#16楼

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

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

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

#17楼

将您的代码放入其中:

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

#18楼

这可行。 只是使Luiji博士的答案简单了一点。

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

#19楼

尽管上面有一个巨大的解决方案池,但没有人提到com.koushikdutta.ionhttps : //github.com/koush/ion

它也是异步的 ,使用起来非常简单

Ion.with(context)
.load("http://example.com/thing.json")
.asJsonObject()
.setCallback(new FutureCallback<JsonObject>() {
   @Override
    public void onCompleted(Exception e, JsonObject result) {
        // do stuff with the result or error
    }
});

#20楼

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


#21楼

简单来说

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

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

解:

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

道路:

把你所有的作品放进去

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

但:

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

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

如何?

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

#22楼

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


#23楼

解决此问题的另一种非常方便的方法-使用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一本非常出色的电子书


#24楼

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

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

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

处理器线程

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

处理程序:

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

解:

  1. 创建HandlerThread

  2. HandlerThread上调用start()

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

  4. 将与网络操作相关的代码嵌入到Runnable对象中

  5. 提交Runnable任务Handler

示例代码段,解决了NetworkOnMainThreadException

HandlerThread handlerThread = new HandlerThread("URLConnection");
handlerThread.start();
handler mainHandler = new Handler(handlerThread.getLooper());

Runnable myRunnable = new Runnable() {
    @Override
    public void run() {
        try {
            Log.d("Ravi", "Before IO call");
            URL page = new URL("http://www.google.com");
            StringBuffer text = new StringBuffer();
            HttpURLConnection conn = (HttpURLConnection) page.openConnection();
            conn.connect();
            InputStreamReader in = new InputStreamReader((InputStream) conn.getContent());
            BufferedReader buff = new BufferedReader(in);
            String line;
            while ( (line =  buff.readLine()) != null) {
                text.append(line + "\n");
            }
            Log.d("Ravi", "After IO call");
            Log.d("Ravi",text.toString());

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

使用这种方法的优点:

  1. 为每个网络操作创建新的Thread/AsyncTask非常昂贵。 Thread/AsyncTask将被销毁,并为下一个网络操作重新创建。 但随着HandlerHandlerThread方法,您可以提交许多网络操作(如Runnable的任务),以单HandlerThreadHandler

#25楼

RxAndroid是解决此问题的另一个更好的选择,它使我们免于创建线程然后将结果发布到Android UI线程上的麻烦。 我们只需要指定需要在其上执行任务并且内部处理所有事情的线程即可。

Observable<List<String>> musicShowsObservable = Observable.fromCallable(new Callable<List<String>>() { 

  @Override 
  public List<String> call() { 
    return mRestClient.getFavoriteMusicShows(); 
  }
});

mMusicShowSubscription = musicShowsObservable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<List<String>>() {

    @Override 
    public void onCompleted() { }

    @Override 
    public void onError(Throwable e) { }

    @Override 
    public void onNext(List<String> musicShows){
        listMusicShows(musicShows);
    }
});
  1. 通过指定(Schedulers.io()) ,RxAndroid将在其他线程上运行getFavoriteMusicShows()

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


#26楼

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

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

通过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)

广场改造

对于具有多个端点的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);
}

财务ApiBuilder

public class FinancesApiBuilder {
    public static FinancesApi build(String baseUrl){
        return new Retrofit.Builder()
                    .baseUrl(baseUrl)
                    .addConverterFactory(GsonConverterFactory.create())
                    .build()
                    .create(FinancesApi.class);
    }
}

财务片段

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/


#27楼

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

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

Java 线程或Android HandlerThread

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

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

异步任务

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

线程池实现ThreadPoolExecutorScheduledThreadPoolExecutor ...

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

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

未来任务

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

AsyncTaskLoaders

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

意图服务

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

作业调度程序

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

RxJava的

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

协程科特琳

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

在这里这里这里这里阅读更多


#28楼

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

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


#29楼

当应用程序尝试在其主线程上执行联网操作时,将引发此异常。 在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"/>

#30楼

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

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

加:

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

StrictMode.setThreadPolicy(policy); 

在你班上

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

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

后果:

您的应用程序(在Internet连接不正常的区域)将变得无响应并被锁定,用户会感觉很慢,必须强行杀死它,并且冒险活动管理器会杀死您的应用程序并告诉用户该应用程序已停止。

Android提供了一些有关设计良好的编程实践以提高响应能力的好技巧: http : //developer.android.com/reference/android/os/NetworkOnMainThreadException.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值