วิธีแก้ไข 'android.os.NetworkOnMainThreadException'

Jun 14 2011

ฉันได้รับข้อผิดพลาดขณะเรียกใช้โครงการ Android สำหรับ RssReader

รหัส:

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

ฉันจะแก้ไขปัญหานี้ได้อย่างไร?

คำตอบ

2598 MichaelSpector Jun 14 2011 at 19:15

หมายเหตุ: AsyncTask เลิกใช้แล้วใน API ระดับ 30
https://developer.android.com/reference/android/os/AsyncTask

ข้อยกเว้นนี้เกิดขึ้นเมื่อแอปพลิเคชันพยายามดำเนินการเครือข่ายบนเธรดหลัก เรียกใช้รหัสของคุณใน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"/>
708 user1169115 Feb 15 2012 at 13:59

คุณควรรันการดำเนินการเครือข่ายบนเธรดหรือเป็นงานอะซิงโครนัสเกือบตลอดเวลา

แต่เป็นไปได้ที่จะลบข้อ จำกัด นี้และคุณจะลบล้างพฤติกรรมเริ่มต้นหากคุณยินดีที่จะยอมรับผลที่ตามมา

เพิ่ม:

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

StrictMode.setThreadPolicy(policy); 

ในชั้นเรียนของคุณ

และ

เพิ่มสิทธิ์นี้ในไฟล์ manifest.xml ของ android:    

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

ผลที่ตามมา:

แอปของคุณจะ (ในพื้นที่ที่มีการเชื่อมต่ออินเทอร์เน็ตขาด ๆ หาย ๆ ) ไม่ตอบสนองและถูกล็อกผู้ใช้รับรู้ว่าช้าและต้องบังคับฆ่าและคุณเสี่ยงที่ผู้จัดการกิจกรรมจะฆ่าแอปของคุณและแจ้งให้ผู้ใช้ทราบว่าแอปหยุดทำงาน

Android มีเคล็ดลับดีๆเกี่ยวกับแนวทางการเขียนโปรแกรมที่ดีในการออกแบบเพื่อการตอบสนอง: http://developer.android.com/reference/android/os/NetworkOnMainThreadException.html

443 Dr.Luiji Jan 21 2013 at 23:31

ฉันแก้ไขปัญหานี้โดยใช้ไฟล์Thread.

Thread thread = new Thread(new Runnable() {

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

thread.start(); 
178 Stevie Jan 22 2014 at 20:17

คำตอบที่ได้รับการยอมรับมีข้อเสียที่สำคัญบางประการ ไม่แนะนำให้ใช้ AsyncTask สำหรับระบบเครือข่ายเว้นแต่คุณจะรู้จริงๆว่าคุณกำลังทำอะไรอยู่ ด้านล่างบางส่วน ได้แก่ :

  • AsyncTask ที่สร้างขึ้นเป็นคลาสภายในที่ไม่คงที่มีการอ้างอิงโดยนัยไปยังอ็อบเจ็กต์กิจกรรมที่ปิดล้อมบริบทและลำดับชั้นมุมมองทั้งหมดที่สร้างโดยกิจกรรมนั้น การอ้างอิงนี้ป้องกันไม่ให้กิจกรรมถูกรวบรวมขยะจนกว่าการทำงานเบื้องหลังของ AsyncTask จะเสร็จสิ้น หากการเชื่อมต่อของผู้ใช้ช้าและ / หรือการดาวน์โหลดมีขนาดใหญ่การรั่วไหลของหน่วยความจำระยะสั้นเหล่านี้อาจกลายเป็นปัญหาได้ตัวอย่างเช่นหากการวางแนวเปลี่ยนไปหลายครั้ง (และคุณไม่ได้ยกเลิกการดำเนินการ) หรือผู้ใช้ ออกจากกิจกรรม
  • AsyncTask มีลักษณะการดำเนินการที่แตกต่างกันขึ้นอยู่กับแพลตฟอร์มที่ดำเนินการ: ก่อนที่ API ระดับ 4 AsyncTasks จะดำเนินการแบบอนุกรมบนเธรดพื้นหลังเดียว จาก API ระดับ 4 ถึง API ระดับ 10 AsyncTasks ดำเนินการบนพูลสูงสุด 128 เธรด ตั้งแต่ API ระดับ 11 เป็นต้นไป AsyncTask รันบนเธรดพื้นหลังเดียวแบบอนุกรม (เว้นแต่คุณจะใช้executeOnExecutorเมธอดโอเวอร์โหลดและจัดหาตัวดำเนินการสำรอง) โค้ดที่ใช้งานได้ดีเมื่อรันบน ICS แบบอนุกรมอาจพังเมื่อดำเนินการพร้อมกันบน Gingerbread กล่าวว่าคุณมีการขึ้นต่อกันของคำสั่งในการดำเนินการโดยไม่ได้ตั้งใจหรือไม่

หากคุณต้องการหลีกเลี่ยงการรั่วไหลของหน่วยความจำระยะสั้นมีลักษณะการดำเนินการที่กำหนดไว้อย่างดีในทุกแพลตฟอร์มและมีพื้นฐานในการสร้างการจัดการเครือข่ายที่มีประสิทธิภาพจริงๆคุณอาจต้องพิจารณา:

  1. การใช้ไลบรารีที่ทำได้ดีสำหรับคุณ - มีการเปรียบเทียบ libs เครือข่ายที่ดีในคำถามนี้หรือ
  2. อาจใช้ a ServiceหรือIntentServiceแทนด้วย a PendingIntentเพื่อส่งคืนผลลัพธ์โดยใช้onActivityResultวิธีการของกิจกรรม

แนวทาง IntentService

ด้านล่าง:

  • รหัสและความซับซ้อนมากกว่าAsyncTaskแม้ว่าจะไม่มากเท่าที่คุณคิด
  • จะจัดคิวคำขอและรันบนเธรดพื้นหลังเดียว คุณสามารถควบคุมได้อย่างง่ายดายนี้โดยการแทนที่IntentServiceด้วยเทียบเท่าServiceการดำเนินการอาจจะเหมือนคนนี้
  • อืมฉันไม่สามารถคิดถึงคนอื่นได้ในตอนนี้

ด้านขึ้น:

  • หลีกเลี่ยงปัญหาการรั่วไหลของหน่วยความจำระยะสั้น
  • หากกิจกรรมของคุณรีสตาร์ทในขณะที่การทำงานของเครือข่ายอยู่ในระหว่างการบินก็ยังสามารถรับผลการดาวน์โหลดผ่านonActivityResultวิธีการ
  • แพลตฟอร์มที่ดีกว่า AsyncTask ในการสร้างและใช้โค้ดเครือข่ายที่แข็งแกร่งอีกครั้ง ตัวอย่าง: หากคุณจำเป็นต้องทำการอัปโหลดที่สำคัญคุณสามารถทำได้จากAsyncTaskในแอActivityป แต่ถ้าบริบทของผู้ใช้เปลี่ยนออกจากแอปเพื่อรับโทรศัพท์ระบบอาจฆ่าแอปก่อนที่การอัปโหลดจะเสร็จสิ้น มันเป็นโอกาสน้อยที่Serviceจะฆ่าแอพลิเคชันที่มีที่ใช้งาน
  • หากคุณใช้เวอร์ชันพร้อมกันของคุณเองIntentService(เช่นเดียวกับที่ฉันเชื่อมโยงไว้ด้านบน) คุณสามารถควบคุมระดับของการทำงานพร้อมกันผ่านทางไฟล์Executor.

สรุปการดำเนินการ

คุณสามารถใช้IntentServiceเพื่อทำการดาวน์โหลดบนเธรดพื้นหลังเดียวได้อย่างง่ายดาย

ขั้นตอนที่ 1: สร้างไฟล์IntentServiceเพื่อทำการดาวน์โหลด คุณสามารถบอกได้ว่าจะดาวน์โหลดอะไรผ่านIntentของพิเศษและส่งต่อPendingIntentเพื่อใช้เพื่อส่งคืนผลลัพธ์ไปยังActivity:

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

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

public class DownloadIntentService extends IntentService {

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

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

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

    private IllustrativeRSSParser parser;

    public DownloadIntentService() {
        super(TAG);

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

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

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

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

ขั้นตอนที่ 2: ลงทะเบียนบริการในรายการ:

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

ขั้นตอนที่ 3: เรียกใช้บริการจากกิจกรรมโดยส่งผ่านออบเจ็กต์ PendingResult ซึ่งบริการจะใช้เพื่อส่งคืนผลลัพธ์:

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

ขั้นตอนที่ 4: จัดการผลลัพธ์ใน onActivityResult:

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

โครงการ Github ที่มีการทำงานที่สมบูรณ์โครงการ Android ที่สตูดิโอ / Gradle สามารถใช้ได้ที่นี่

147 MarkAllison Jun 14 2011 at 19:07

คุณไม่สามารถดำเนินการเครือข่ายI / Oในหัวข้อ UI บนรังผึ้ง ในทางเทคนิคเป็นไปได้ใน Android เวอร์ชันก่อนหน้า แต่เป็นความคิดที่แย่มากเนื่องจากจะทำให้แอปของคุณหยุดตอบสนองและอาจส่งผลให้ระบบปฏิบัติการฆ่าแอปของคุณเนื่องจากมีพฤติกรรมไม่ดี คุณจะต้องเรียกใช้กระบวนการเบื้องหลังหรือใช้ AsyncTask เพื่อทำธุรกรรมเครือข่ายของคุณบนเธรดพื้นหลัง

มีบทความเกี่ยวกับการทำเธรดแบบไม่เจ็บปวดบนไซต์ของนักพัฒนา Android ซึ่งเป็นคำแนะนำที่ดีสำหรับเรื่องนี้และจะให้คำตอบที่เจาะลึกได้ดีกว่าที่สามารถระบุได้ตามความเป็นจริงที่นี่

83 henry4343 Dec 24 2013 at 21:33

ดำเนินการเครือข่ายกับเธรดอื่น

ตัวอย่างเช่น:

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

และเพิ่มสิ่งนี้ลงใน AndroidManifest.xml

<uses-permission android:name="android.permission.INTERNET"/>
79 venergiac Aug 18 2013 at 16:18
  1. อย่าใช้เข้มงวดโหมด (เฉพาะในโหมดดีบัก)
  2. อย่าเปลี่ยนเวอร์ชัน SDK
  3. อย่าใช้ด้ายแยก

ใช้บริการหรือ AsyncTask

ดูคำถาม Stack Overflow:

android.os.NetworkOnMainThreadException ส่งอีเมลจาก Android

71 Piyush-AskAnyDifference Oct 24 2012 at 14:10

คุณปิดโหมดเข้มงวดโดยใช้รหัสต่อไปนี้:

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

ไม่แนะนำให้ใช้AsyncTaskอินเทอร์เฟซ

รหัสเต็มสำหรับทั้งสองวิธี

58 DhruvJindal Jan 14 2014 at 13:14

ไม่สามารถรันการทำงานบนเครือข่ายบนเธรดหลักได้ คุณต้องรันงานบนเครือข่ายทั้งหมดบนเธรดลูกหรือใช้ AsyncTask

นี่คือวิธีที่คุณรันงานในเธรดลูก:

new Thread(new Runnable(){
    @Override
    public void run() {
        try {
            // Your implementation goes here
        } 
        catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}).start();
51 VaishaliSutariya Jul 28 2014 at 17:43

ใส่รหัสของคุณภายใน:

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
    }
}
49 raihanahmed Sep 05 2013 at 15:16

สิ่งนี้เกิดขึ้นใน Android 3.0 ขึ้นไป ตั้งแต่ Android 3.0 ขึ้นไปพวกเขาได้ จำกัด การใช้งานเครือข่าย (ฟังก์ชันที่เข้าถึงอินเทอร์เน็ต) ไม่ให้ทำงานในเธรดหลัก / เธรด UI (สิ่งที่เกิดจากวิธีการสร้างและดำเนินการต่อในกิจกรรม)

นี่เป็นการส่งเสริมให้ใช้เธรดแยกต่างหากสำหรับการทำงานของเครือข่าย ดูAsyncTaskสำหรับรายละเอียดเพิ่มเติมเกี่ยวกับวิธีดำเนินกิจกรรมเครือข่ายอย่างถูกต้อง

48 Oleksiy Jun 12 2014 at 10:10

การใช้คำอธิบายประกอบของ Androidเป็นตัวเลือก จะช่วยให้คุณสามารถเรียกใช้วิธีการใดก็ได้ในเธรดพื้นหลัง:

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

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

โปรดทราบว่าแม้ว่าจะให้ประโยชน์ของความเรียบง่ายและความสามารถในการอ่าน แต่ก็มีข้อเสีย

44 AshwinSAshok Jul 16 2014 at 13:09

ข้อผิดพลาดที่เกิดจากการดำเนินการการดำเนินงานระยะยาวในหัวข้อหลักคุณสามารถแก้ไขปัญหาโดยใช้AsynTaskหรือกระทู้ คุณสามารถชำระเงินAsyncHTTPClientไลบรารีนี้เพื่อการจัดการที่ดีขึ้น

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

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

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

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

    @Override
    public void onRetry(int retryNo) {
        // Called when request is retried
    }
});
43 KapilVats Jan 06 2014 at 01:28

You should not do any time-consuming task on the main thread (UI thread), like any network operation, file I/O, or SQLite database operations. So for this kind of operation, you should create a worker thread, but the problem is that you can not directly perform any UI related operation from your worker thread. For that, you have to use Handler and pass the Message.

To simplify all these things, Android provides various ways, like AsyncTask, AsyncTaskLoader, CursorLoader or IntentService. So you can use any of these according to your requirements.

42 sivag1 Jul 19 2013 at 01:52

The top answer of spektom works perfect.

If you are writing the AsyncTask inline and not extending as a class, and on top of this, if there is a need to get a response out of the AsyncTask, one can use the get() method as below.

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

(From his example.)

32 perry Feb 25 2014 at 13:23

This is only thrown for applications targeting the Honeycomb SDK or higher. Applications targeting earlier SDK versions are allowed to do networking on their main event loop threads.

The error is the SDK warning!

28 rharvey Sep 17 2013 at 19:15

For me it was this:

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

The device I was testing my app on was 4.1.2 which is SDK Version 16!

Make the sure the target version is the same as your Android Target Library. If you are unsure what your target library is, right click your Project -> Build Path -> Android, and it should be the one that is ticked.

Also, as others have mentioned, include the correct permissions to access the Internet:

<uses-permission android:name="android.permission.INTERNET"/>
25 dhirajkakran Mar 01 2014 at 20:43

Use this in Your Activity

    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();
        }
    });
23 Novak Jun 25 2014 at 04:32

Just to spell out something explicitly:

The main thread is basically the UI thread.

So saying that you cannot do networking operations in the main thread means you cannot do networking operations in the UI thread, which means you cannot do networking operations in a *runOnUiThread(new Runnable() { ... }* block inside some other thread, either.

(I just had a long head-scratching moment trying to figure out why I was getting that error somewhere other than my main thread. This was why; this thread helped; and hopefully this comment will help someone else.)

22 amardeep Jul 21 2014 at 18:53

This exception occurs due to any heavy task performed on the main thread if that performing task takes too much time.

To avoid this, we can handle it using threads or executers

Executors.newSingleThreadExecutor().submit(new Runnable() {
    @Override
    public void run() {
        // You can perform your task here.
    }
});
19 KG6ZVP Oct 21 2017 at 06:50

There are many great answers already on this question, but a lot of great libraries have come out since those answers were posted. This is intended as a kind of newbie-guide.

I will cover several use cases for performing network operations and a solution or two for each.

ReST over HTTP

Typically Json, can be XML or something else

Full API Access

Let's say you are writing an app that lets users track stock prices, interest rates and currecy exchange rates. You find an Json API that looks something like this:

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)

Retrofit from Square

This is an excellent choice for an API with multiple endpoints and allows you to declare the ReST endpoints instead of having to code them individually as with other libraries like ion or Volley. (website: http://square.github.io/retrofit/)

How do you use it with the finances API?

build.gradle

Add these lines to your Module level 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 snippet

FinancesApi api = FinancesApiBuilder.build("http://api.example.com/"); //trailing '/' required for predictable behavior
api.getStock("INTC").enqueue(new Callback<Stock>(){
    @Override
    public void onResponse(Call<Stock> stockCall, Response<Stock> stockResponse){
        Stock stock = stockCall.body();
        //do something with the stock
    }
    @Override
    public void onResponse(Call<Stock> stockCall, Throwable t){
        //something bad happened
    }
}

If your API requires an API Key or other header like a user token, etc. to be sent, Retrofit makes this easy (see this awesome answer for details: https://stackoverflow.com/a/42899766/1024412).

One off ReST API access

Let's say you're building a "mood weather" app that looks up the users GPS location and checks the current temperature in that area and tells them the mood. This type of app doesn't need to declare API endpoints; it just needs to be able to access one API endpoint.

Ion

This is a great library for this type of access.

Please read msysmilu's great answer (https://stackoverflow.com/a/28559884/1024412)

Load images via HTTP

Volley

Volley can also be used for ReST APIs, but due to the more complicated setup required I prefer to use Retrofit from Square as above (http://square.github.io/retrofit/)

Let's say you are building a social networking app and want to load profile pictures of friends.

build.gradle

Add this line to your Module level buid.gradle:

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

ImageFetch.java

Volley requires more setup than Retrofit. You will need to create a class like this to setup a RequestQueue, an ImageLoader and an ImageCache, but it's not too bad:

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

Add the following to your layout xml file to add an image:

<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

Add the following code to the onCreate method (Fragment, Activity) or the constructor (Dialog):

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

Picasso

Another excellent library from Square. Please see the site for some great examples: http://square.github.io/picasso/

16 Nabin Jul 22 2015 at 09:05

In simple words,

DO NOT DO NETWORK WORK IN THE UI THREAD

For example, if you do an HTTP request, that is a network action.

Solution:

  1. You have to create a new Thread
  2. Or use AsyncTask class

Way:

Put all your works inside

  1. run() method of new thread
  2. Or doInBackground() method of AsyncTask class.

But:

When you get something from Network response and want to show it on your view (like display response message in TextView), you need to return back to the UI thread.

If you don't do it, you will get ViewRootImpl$CalledFromWrongThreadException.

How to?

  1. While using AsyncTask, update view from onPostExecute() method
  2. Or call runOnUiThread() method and update view inside the run() method.
12 yoAlex5 Mar 17 2018 at 22:58

You are able to move a part of your code into another thread to offload the main thread and avoid getting ANR, NetworkOnMainThreadException, IllegalStateException(e.g. Cannot access database on the main thread since it may potentially lock the UI for a long period of time).

There are some approaches that you should choose depends on the situation

Java Thread or Android HandlerThread

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

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.

Since Main thread monopolizes UI components it is not possible to access to some View, that is why Handler is on the rescue

Thread pool implementation 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

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

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

IntentService

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.

JobScheduler

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.

Coroutines (Kotlin)

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

Read more here, here, here, here

11 Ravindrababu May 06 2017 at 03:11

New Thread and AsyncTask solutions have been explained already.

AsyncTask should ideally be used for short operations. Normal Thread is not preferable for Android.

Have a look at alternate solution using HandlerThread and Handler

HandlerThread

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.

Handler:

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.

Solution:

  1. Create HandlerThread

  2. Call start() on HandlerThread

  3. Create Handler by getting Looper from HanlerThread

  4. Embed your Network operation related code in Runnable object

  5. Submit Runnable task to Handler

Sample code snippet, which address 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);

Pros of using this approach:

  1. Creating new Thread/AsyncTask for each network operation is expensive. The Thread/AsyncTask will be destroyed and re-created for next Network operations. But with Handler and HandlerThread approach, you can submit many network operations (as Runnable tasks) to single HandlerThread by using Handler.
10 msysmilu Feb 17 2015 at 17:31

Although above there is a huge solution pool, no one mentioned com.koushikdutta.ion: https://github.com/koush/ion

It's also asynchronous and very simple to use:

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
    }
});
9 AlexShutov Jun 24 2016 at 05:00

There is another very convenient way for tackling this issue - use rxJava's concurrency capabilities. You can execute any task in background and post results to main thread in a very convenient way, so these results will be handed to processing chain.

The first verified answer advice is to use AsynTask. Yes, this is a solution, but it is obsolete nowadays, because there are new tools around.

String getUrl() {
    return "SomeUrl";
}

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

private void processResponse(Object o) {

}

The getUrl method provides the URL address, and it will be executed on the main thread.

makeCallParseResponse(..) - does actual work

processResponse(..) - will handle result on main thread.

The code for asynchronous execution will look like:

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

Compared to AsyncTask, this method allow to switch schedulers an arbitrary number of times (say, fetch data on one scheduler and process those data on another (say, Scheduler.computation()). You can also define you own schedulers.

In order to use this library, include following lines into you build.gradle file:

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

The last dependency includes support for the .mainThread() scheduler.

There is an excellent ebook for rx-java.

9 ShinooGoyal Jun 09 2017 at 12:57

RxAndroid is another better alternative to this problem and it saves us from hassles of creating threads and then posting results on Android UI thread. We just need to specify threads on which tasks need to be executed and everything is handled internally.

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. By specifiying (Schedulers.io()),RxAndroid will run getFavoriteMusicShows() on a different thread.

  2. By using AndroidSchedulers.mainThread() we want to observe this Observable on the UI thread, i.e. we want our onNext() callback to be called on the UI thread

9 Sharathkumar Sep 04 2017 at 17:43

The main thread is the UI thread, and you cannot do an operation in the main thread which may block the user interaction. You can solve this in two ways:

Force to do the task in the main thread like this

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

Or create a simple handler and update the main thread if you want.

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

And to stop the thread use:

newHandler.removeCallbacks(runnable);

For more information check this out: Painless threading

8 Kacy Feb 14 2015 at 06:10

This works. Just made Dr.Luiji's answer a little simpler.

new Thread() {
    @Override
    public void run() {
        try {
            //Your code goes here
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}.start();
7 PonsuyambuVelladurai Jun 25 2015 at 14:42

On Android, network operations cannot be run on the main thread. You can use Thread, AsyncTask (short-running tasks), Service (long-running tasks) to do network operations.