Bagaimana cara memperbaiki 'android.os.NetworkOnMainThreadException'?

2394

Saya mendapat kesalahan saat menjalankan proyek Android saya untuk RssReader.

Kode:

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

Dan itu menunjukkan kesalahan di bawah ini:

android.os.NetworkOnMainThreadException

Bagaimana saya bisa memperbaiki masalah ini?

bejoy george
sumber
131
Baca posting blog ini di NetworkOnMainThreadException untuk informasi lebih lanjut. Ini menjelaskan mengapa ini terjadi pada Android 3.0 dan di atasnya.
Adrian Monk
6
Untuk berada di jalur ritus pertama baca tentang Permintaan Jaringan di android maka saya akan merekomendasikan untuk belajar "Volley".
Anuj Sharma
3
Ada banyak pustaka alternatif yang memecahkan masalah ini. Banyak yang terdaftar di bagian bawah halaman ini . Jika Anda mendapat lebih banyak, kami bawa mereka :)
Snicolas
Anda perlu menjalankan aktivitas internet di utas terpisah dari utas utama (UI)
Naveed Ahmad
"Karena bug pada versi Android sebelumnya, sistem tidak menandai tulisan ke soket TCP pada utas utama sebagai pelanggaran mode ketat. Android 7.0 memperbaiki bug ini. Aplikasi yang menunjukkan perilaku ini sekarang melempar android.os. NetworkOnMainThreadException. " - Jadi beberapa dari kita belum mencapai ini sampai saat ini! developer.android.com/about/versions/nougat/…
Jay

Jawaban:

2547

Pengecualian ini dilemparkan ketika suatu aplikasi mencoba untuk melakukan operasi jaringan pada utas utamanya. Jalankan kode Anda di 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
    }
}

Cara menjalankan tugas:

Dalam MainActivity.javafile Anda dapat menambahkan baris ini dalam oncreate()metode Anda

new RetrieveFeedTask().execute(urlToRssFeed);

Jangan lupa untuk menambahkan ini ke AndroidManifest.xmlfile:

<uses-permission android:name="android.permission.INTERNET"/>
Michael Spector
sumber
37
Saya pikir perlu dicatat di sini bahwa potongan kode di atas seharusnya menjadi subkelas (kelas dalam), lebih disukai pribadi. Dengan begitu ketika AsyncTask selesai, Anda masih bisa memanipulasi jeroan kelas Anda.
dyslexicanaboko
4
Sebenarnya saya melakukan hal yang sama seperti yang Anda sebutkan di atas tetapi saya menghadapi kesalahan ini java.lang.RuntimeException: Tidak dapat membuat handler di dalam utas yang belum disebut Looper.prepare ()
Dhruv Tyagi
68
Ini adalah jawaban yang salah. Saya menemukan ini sepanjang waktu dalam kode orang, dan itu menjengkelkan harus memperbaikinya sepanjang waktu. AsyncTask tidak boleh digunakan untuk aktivitas jaringan, karena itu terkait dengan aktivitas, tetapi bukan siklus hidup aktivitas. Memutar perangkat dengan tugas ini sedang berjalan akan menyebabkan pengecualian dan merusak aplikasi Anda. Gunakan IntentService yang menjatuhkan data dalam database sqlite.
Brill Pappin
5
Perhatian, AsyncTask sering digunakan untuk operasi jaringan per aktivitas padahal seharusnya tidak. siklus hidupnya tidak selaras dengan aktivitas. Untuk mengambil data, Anda harus menggunakan IntentService dan database di belakang tampilan.
Brill Pappin
1
@BrillPappin, FWIW, Panduan Pengembang Android ini menggunakan AsyncTask, dan menangani perubahan konfigurasi.
HeyJude
677

Anda hampir selalu harus menjalankan operasi jaringan pada utas atau sebagai tugas tidak sinkron.

Tapi itu benar kemungkinan untuk menghapus batasan ini dan Anda menimpa perilaku default, jika Anda bersedia menerima konsekuensinya.

Menambahkan:

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

StrictMode.setThreadPolicy(policy); 

Di kelasmu,

dan

TAMBAH izin ini dalam file android manifest.xml:    

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

Konsekuensi:

Aplikasi Anda akan (di area koneksi internet yang berbintik-bintik) menjadi tidak responsif dan terkunci, pengguna merasakan kelambatan dan harus melakukan pemaksaan paksa, dan Anda berisiko manajer aktivitas membunuh aplikasi Anda dan memberi tahu pengguna bahwa aplikasi telah berhenti.

Android memiliki beberapa kiat bagus tentang praktik pemrograman yang baik untuk merancang responsif: http://developer.android.com/reference/android/os/NetworkOnMainThreadException.html

pengguna1169115
sumber
456
Ini ide yang sangat buruk. solusinya adalah menghindari IO jaringan pada utas utama (seperti yang ditunjukkan oleh jawaban yang diterima).
MByD
74
Dengan ini, Anda hanya menyembunyikan masalah Anda yang sebenarnya.
Alex
28
@TwistedUmbrella AsyncTask tidak menambahkan halaman kode, ia menambahkan 6 baris (deklarasi kelas, ganti anotasi, doInBackgrounddeklarasi, 2 kurung penutup, dan panggilan ke execute()). Di sisi lain, bahkan satu pengambilan dari sebuah situs seperti yang Anda sebutkan membawa jeda yang signifikan dalam respons UI. Jangan malas.
Zoltán
19
Saya pikir ini adalah solusi sempurna jika Anda ingin menjalankan contoh kode untuk melihat apakah sesuatu berfungsi sebelum mengimplementasikan AsyncTask yang tepat. Itu sebabnya saya memutakhirkan jawaban ini, meskipun seperti semua yang lain katakan, ini tidak boleh dilakukan dalam aplikasi produksi, hanya sebagai solusi cepat untuk tes dev.
hooked82
94
Terpilih. Jawaban ini benar, dan bagi banyak programmer yang tidak naif atau bodoh, tetapi yang hanya membutuhkan panggilan SYNCHRONOUS (yaitu: ini harus memblokir aplikasi ), inilah yang dibutuhkan. Saya lebih dari senang bahwa Android melempar pengecualian secara default (IMHO itu hal yang sangat "membantu" untuk dilakukan!) - tapi saya juga senang mengatakan "terima kasih, tapi - ini sebenarnya yang saya maksudkan" dan untuk mengesampingkan Itu.
Adam
428

Saya memecahkan masalah ini menggunakan yang baru Thread.

Thread thread = new Thread(new Runnable() {

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

thread.start(); 
Dr.Luiji
sumber
7
Alih-alih membuat utas baru setiap kali Anda ingin melakukan operasi jaringan, Anda bisa menggunakan layanan pelaksana utas tunggal juga.
Alex Lockwood
66
Sederhana tapi berbahaya. Runnable anonim memiliki referensi implisit ke kelas terlampir (misalnya Aktivitas atau Fragmen Anda), mencegahnya dari pengumpulan sampah hingga utas selesai. Anda setidaknya harus menetapkan prioritas untuk Proses. LATAR BELAKANG, kalau tidak thread ini akan berjalan pada prioritas yang sama dengan utas utama / ui, bersaing dengan metode siklus hidup dan tingkat bingkai ui (perhatikan peringatan log dari koreografer).
Stevie
1
@Stevie cara mengatur prioritas? runnble maupun executorService tidak memiliki metode setter seperti itu
JK
1
@JK Berikan Layanan ExecutorService Anda dengan ThreadFactory kustom dan panggil Thread.setPriority di utas sebelum mengembalikannya.
Stevie
1
"Menggunakan Threads secara langsung di Android tidak disarankan. Ini menyebabkan lebih banyak masalah daripada menyelesaikannya" Peduli untuk menguraikannya? Sebenarnya AsyncTask tidak digunakan lagi untuk itu ... techyourchance.com/asynctask-deprecated
Fran Marzoa
170

Jawaban yang diterima memiliki beberapa kelemahan. Tidak disarankan menggunakan AsyncTask untuk jaringan kecuali Anda benar - benar tahu apa yang Anda lakukan. Beberapa sisi-bawah meliputi:

  • AsyncTask yang dibuat sebagai kelas dalam non-statis memiliki referensi implisit ke objek Kegiatan yang melampirkan, konteksnya, dan seluruh hierarki Tampilan yang dibuat oleh aktivitas itu. Referensi ini mencegah Kegiatan dari menjadi sampah yang dikumpulkan sampai pekerjaan latar belakang AsyncTask selesai. Jika koneksi pengguna lambat, dan / atau unduhannya besar, kebocoran memori jangka pendek ini dapat menjadi masalah - misalnya, jika orientasinya berubah beberapa kali (dan Anda tidak membatalkan tugas yang dijalankan), atau pengguna menavigasi menjauh dari Aktivitas.
  • AsyncTask memiliki karakteristik eksekusi yang berbeda tergantung pada platform yang dijalankannya: sebelum API level 4, AsyncTasks mengeksekusi secara serial pada utas latar belakang tunggal; dari API level 4 hingga API level 10, AsyncTasks mengeksekusi pada kumpulan hingga 128 utas; dari API level 11 dan seterusnya, AsyncTask dieksekusi secara serial pada utas latar belakang tunggal (kecuali jika Anda menggunakan executeOnExecutormetode kelebihan beban dan menyediakan pelaksana alternatif). Kode yang berfungsi dengan baik saat menjalankan serial pada ICS mungkin rusak ketika dieksekusi bersamaan pada Gingerbread, katakan jika Anda memiliki dependensi eksekusi eksekusi yang tidak disengaja.

Jika Anda ingin menghindari kebocoran memori jangka pendek, memiliki karakteristik eksekusi yang terdefinisi dengan baik di semua platform, dan memiliki dasar untuk membangun penanganan jaringan yang sangat kuat, Anda mungkin ingin mempertimbangkan:

  1. Menggunakan perpustakaan yang melakukan pekerjaan yang bagus untuk Anda - ada perbandingan bagus lib jaringan dalam pertanyaan ini , atau
  2. Menggunakan a Serviceatau IntentServicesebaliknya, mungkin dengan PendingIntentmengembalikan hasil melalui metode Aktivitas onActivityResult.

Pendekatan IntentService

Sisi bawah:

  • Lebih banyak kode dan kompleksitas daripada AsyncTask, meskipun tidak sebanyak yang Anda kira
  • Akan mengantri permintaan dan menjalankannya pada utas latar belakang tunggal . Anda dapat dengan mudah mengontrol ini dengan mengganti IntentServicedengan Serviceimplementasi yang setara , mungkin seperti ini .
  • Um, sebenarnya aku tidak bisa memikirkan yang lain

Sisi atas:

  • Menghindari masalah kebocoran memori jangka pendek
  • Jika aktivitas Anda dimulai kembali saat operasi jaringan sedang dalam penerbangan, ia masih dapat menerima hasil unduhan melalui onActivityResult metodenya
  • Platform yang lebih baik daripada AsyncTask untuk membangun dan menggunakan kembali kode jaringan yang kuat. Contoh: jika Anda perlu melakukan unggahan penting, Anda bisa melakukannya dari AsyncTaskdalam Activity, tetapi jika konteks pengguna-beralih dari aplikasi untuk menerima panggilan telepon, sistem dapat mematikan aplikasi sebelum unggahan selesai. Kecil kemungkinannya untuk mematikan aplikasi dengan aktif Service.
  • Jika Anda menggunakan versi konkuren Anda sendiri IntentService(seperti yang saya tautkan di atas), Anda dapat mengontrol tingkat konkurensi melalui Executor.

Ringkasan implementasi

Anda dapat menerapkan IntentService untuk melakukan unduhan pada utas latar belakang tunggal dengan cukup mudah.

Langkah 1: Buat sebuah IntentServiceuntuk melakukan unduhan. Anda dapat menentukan apa yang harus diunduh melalui Intentekstra, dan meneruskannya PendingIntentuntuk mengembalikan hasilnya ke 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);
        }
    }
}

Langkah 2: Daftarkan layanan di manifes:

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

Langkah 3: Aktifkan layanan dari Aktivitas, dengan meneruskan objek PendingResult yang akan digunakan Layanan untuk mengembalikan hasilnya:

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

Langkah 4: Tangani hasilnya di 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);
}

Proyek Github yang berisi proyek Android-Studio / Gradle yang berfungsi lengkap tersedia di sini .

Stevie
sumber
IntentService adalah cara yang benar untuk melakukan ini, bukan mencabut karena AsyncTask persis cara untuk tidak melakukannya.
Brill Pappin
3
@BrillPappin Saya hampir sepenuhnya setuju dan telah berkata kembali untuk menekankan kelemahan AsyncTask. (Saya masih berpikir ada sejumlah kecil kasus di mana - jika Anda benar-benar tahu apa yang Anda lakukan - mungkin boleh menggunakan AsyncTask, tetapi jawaban yang diterima tidak menunjukkan kekurangan dan terlalu populer untuk kebaikan. Android).
Stevie
1
Apakah Anda benar-benar membutuhkan IllustrativeRSS? Bagaimana jika Anda tidak bekerja dengan barang-barang RSS?
Cullub
Menurut pendapat saya Google harus mengubah implementasi buruk mereka dari pengumpulan sampah daripada menempatkan berdon di sisi programmer. Ini adalah tanggung jawab OS. Jika seorang programmer menggunakan IntentService atau Layanan untuk melakukan pekerjaan itu karena masalah mendasar dalam implementasi Android, setelah beberapa tahun Google akan mengatakan IntentService adalah praktik buruk juga dan menyarankan sesuatu yang lain. Dan cerita ini berlanjut ... Jadi pengembang Google harus menyelesaikan manajemen memori buruk Android bukan programmer.
saeed khalafinejad
Saya hanya akan mengatakannya: Jawaban ini sepertinya lebih merupakan cara untuk membagikan proyek daripada alternatif yang lebih baik AsyncTask. Kesalahan itu dimaksudkan untuk menghentikan pengembang dari tertinggal UI, tidak perlu membujuk mereka untuk mengambil risiko keamanan dengan sekelompok niat / layanan.
Abandoned Cart
144

Anda tidak dapat melakukan I / O jaringan pada utas UI di Honeycomb . Secara teknis, itu adalah dimungkinkan pada versi Android sebelumnya, tetapi itu adalah ide yang sangat buruk karena akan menyebabkan aplikasi Anda berhenti merespons, dan dapat mengakibatkan OS membunuh aplikasi Anda karena berperilaku buruk. Anda harus menjalankan proses latar belakang atau menggunakan AsyncTask untuk melakukan transaksi jaringan Anda di utas latar.

Ada artikel tentang Painless Threading di situs pengembang Android yang merupakan pengantar yang bagus untuk ini, dan itu akan memberi Anda kedalaman jawaban yang jauh lebih baik daripada yang bisa diberikan secara realistis di sini.

Mark Allison
sumber
76
  1. Jangan gunakan strictMode (hanya dalam mode debug)
  2. Jangan mengubah versi SDK
  3. Jangan gunakan utas terpisah

Gunakan Layanan atau AsyncTask

Lihat juga pertanyaan Stack Overflow:

android.os.NetworkOnMainThreadException mengirim email dari Android

venergiac
sumber
8
Mungkin perlu ditekankan bahwa jika Anda menggunakan Layanan, Anda masih perlu membuat utas terpisah - Layanan panggil balik berjalan di utas utama. IntentService, di sisi lain, menjalankan metode onHandleIntent pada utas latar belakang.
Stevie
Anda tidak boleh menggunakan AsyncTask untuk operasi yang berjalan lama! Panduan menentukan maksimal 2 hingga 3 detik.
Dage
76

Lakukan tindakan jaringan pada utas lainnya

Sebagai contoh:

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

Dan tambahkan ini ke AndroidManifest.xml

<uses-permission android:name="android.permission.INTERNET"/>
henry4343
sumber
5
Tetapi bagaimana kita bisa mengetahui kapan utas selesai dalam hal ini sehingga kita dapat melakukan serangkaian tugas berikutnya dalam utas UI? AsyncTask menyediakan fasilitas untuk melakukan itu. Apakah ada cara untuk melakukan hal yang sama menggunakan utas runnable?
Piyush Soni
3
Ini akan memproses kode Anda selangkah demi selangkah, sehingga pada akhir kode selesai, Anda harus menggunakan handler kembali ke utas UI
henry4343
1
Anda dapat menggunakan tugas async atau layanan maksud, karena dijalankan pada utas pekerja.
Chetan Chaudhari
63

Anda menonaktifkan mode ketat menggunakan kode berikut:

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

Ini tidak disarankan : gunakan AsyncTaskantarmuka.

Kode lengkap untuk kedua metode

Sandeep
sumber
2
Ya, kesalahan PPA akan datang. berarti App tidak merespons dalam 5 detik.
Muhammad Mubashir
12
Ini jawaban yang sangat buruk. Anda seharusnya tidak mengubah kebijakan utas tetapi untuk menulis kode yang lebih baik: jangan membuat operasi jaringan di utas utama!
shkschneider
@ Jauhkan Anda dan pemirsa lain juga harus membaca ini. stackoverflow.com/a/18335031/3470479
Prakhar1001
53

Operasi berbasis jaringan tidak dapat dijalankan pada utas utama. Anda perlu menjalankan semua tugas berbasis jaringan pada utas anak atau mengimplementasikan AsyncTask.

Ini adalah bagaimana Anda menjalankan tugas di utas anak:

new Thread(new Runnable(){
    @Override
    public void run() {
        try {
            // Your implementation goes here
        } 
        catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}).start();
Dhruv Jindal
sumber
1
Anonymous Runnable BUKAN cara terbaik, karena ia memiliki referensi implisit ke kelas terlampir dan mencegahnya tidak diubah sampai utas selesai! Utas ini juga akan berjalan pada Prioritas yang Sama dengan utas utama / AS, bersaing dengan metode siklus hidup dan rasio bingkai UI!
Yousha Aleayoub
49

Masukkan kode Anda di dalam:

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

Atau:

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
    }
}
Vaishali Sutariya
sumber
Yang kedua akan menjadi yang terbaik dari yang pertama untuk api di atas 11
Rohit Goswami
46

Ini terjadi di Android 3.0 dan di atasnya. Dari Android 3.0 dan di atasnya, mereka telah dibatasi menggunakan operasi jaringan (fungsi yang mengakses Internet) agar tidak berjalan di utas utama / utas UI (apa yang muncul dari metode buat dan pada resume dalam aktivitas).

Ini untuk mendorong penggunaan utas terpisah untuk operasi jaringan. Lihat AsyncTask untuk detail lebih lanjut tentang cara melakukan aktivitas jaringan dengan cara yang benar.

raihan ahmed
sumber
46

Menggunakan Android Annotations adalah opsi. Ini akan memungkinkan Anda untuk hanya menjalankan metode apa pun di utas latar:

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

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

Perhatikan, bahwa meskipun memberikan manfaat kesederhanaan dan keterbacaan, ia memiliki kelemahan.

Oleksiy
sumber
6
@ Gavriel itu membuat duplikat dari semua yang Anda anotasi, apakah itu metode, aktivitas, fragmen, singleton dll, jadi ada dua kali lebih banyak kode dan dibutuhkan waktu lebih lama untuk mengompilasinya. Mungkin juga memiliki beberapa masalah karena bug di perpustakaan. Debugging dan menemukan kesalahan akan menjadi lebih sulit.
Oleksiy
43

Kesalahan ini karena menjalankan operasi yang berjalan lama di utas utama, Anda dapat dengan mudah memperbaiki masalah dengan menggunakan AsynTask atau Utas . Anda dapat checkout perpustakaan ini AsyncHTTPClient untuk penanganan yang lebih baik.

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
    }
});
Ashwin S Ashok
sumber
42

Anda seharusnya tidak melakukan tugas yang memakan waktu pada utas utama (utas UI), seperti operasi jaringan, file I / O, atau operasi database SQLite. Jadi untuk jenis operasi ini, Anda harus membuat utas pekerja, tetapi masalahnya adalah Anda tidak dapat secara langsung melakukan operasi terkait UI dari utas pekerja Anda. Untuk itu, Anda harus menggunakan Handlerdan lulus Message.

Untuk mempermudah semua hal ini, Android menyediakan berbagai cara, seperti AsyncTask, AsyncTaskLoader, CursorLoaderatau IntentService. Jadi Anda dapat menggunakan semua ini sesuai dengan kebutuhan Anda.

Kapil Vats
sumber
40

Jawaban atas spektom bekerja sempurna.

Jika Anda menulis AsyncTaskinline dan tidak memperluas sebagai kelas, dan di atas semua ini, jika ada kebutuhan untuk mendapatkan respons dari AsyncTask, Anda dapat menggunakan get()metode seperti di bawah ini.

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

(Dari contohnya.)

sivag1
sumber
5
menggunakan get()adalah ide yang buruk ... itu membuat AsyncTask "disinkronkan" lagi
Selvin
Apakah ada jalan keluar yang lebih baik? @ Selvin
sivag1
2
Saya pikir Anda bisa memberi info utas utama tentang hasilnya. Misalnya, kirim siaran ke utas utama termasuk hasilnya.
Chine Gary
32

Ini hanya dibuang untuk aplikasi yang menargetkan Honeycomb SDK atau lebih tinggi. Aplikasi yang menargetkan versi SDK sebelumnya diizinkan untuk melakukan jaringan di utas loop acara utama.

Kesalahannya adalah peringatan SDK!

Perry
sumber
28

Bagi saya ini:

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

Perangkat tempat saya menguji aplikasi saya adalah 4.1.2 yang merupakan SDK Versi 16!

Pastikan versi targetnya sama dengan Pustaka Target Android Anda. Jika Anda tidak yakin apa perpustakaan target Anda, klik kanan Proyek Anda -> Build Path -> Android , dan itu harus menjadi salah satu yang dicentang.

Juga, seperti yang disebutkan orang lain, sertakan izin yang benar untuk mengakses Internet:

<uses-permission android:name="android.permission.INTERNET"/>
rharvey
sumber
11
Biarkan saya menjelaskan kepada Anda apa yang Anda lakukan di sini: NetworkOnMainThreadExceptionadalah Guardian yang memberi tahu Anda: jangan menembak di kaki Anda sendiri ... solusinya adalah: mari kita kembali ke masa lalu ketika tidak ada Guardian - sekarang saya bisa menembak pada saya kaki dengan bebas
Selvin
1
Saya mengambil pendekatan ini juga, dan tidak memiliki masalah. Terkadang Guardian terlalu cerewet.
FractalBob
25

Gunakan ini di Aktivitas Anda

    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();
        }
    });
dhiraj kakran
sumber
23

Hanya untuk mengeja sesuatu secara eksplisit:

Utas utama pada dasarnya adalah utas UI.

Jadi mengatakan bahwa Anda tidak dapat melakukan operasi jaringan di utas utama berarti Anda tidak dapat melakukan operasi jaringan di utas UI, yang berarti Anda tidak dapat melakukan operasi jaringan di *runOnUiThread(new Runnable() { ... }*blok di dalam utas lainnya.

(Saya baru saja mengalami saat-saat menggaruk-garuk kepala yang panjang untuk mencari tahu mengapa saya mendapatkan kesalahan di tempat lain selain utas utama saya. Inilah sebabnya; utas ini membantu; dan semoga komentar ini akan membantu orang lain.)

Novak
sumber
22

Pengecualian ini terjadi karena tugas berat yang dilakukan pada utas utama jika tugas tersebut membutuhkan terlalu banyak waktu .

Untuk menghindari ini, kita bisa menanganinya menggunakan utas atau pelaksana

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

Sudah ada banyak jawaban bagus untuk pertanyaan ini, tetapi banyak perpustakaan hebat telah keluar sejak jawaban-jawaban itu diposting. Ini dimaksudkan sebagai semacam panduan pemula.

Saya akan membahas beberapa kasus penggunaan untuk melakukan operasi jaringan dan satu atau dua solusi untuk masing-masingnya.

REST melalui HTTP

Biasanya Json, bisa berupa XML atau yang lainnya

Akses API Penuh

Katakanlah Anda sedang menulis aplikasi yang memungkinkan pengguna melacak harga saham, suku bunga, dan nilai tukar saat ini. Anda menemukan API Json yang terlihat seperti ini:

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 dari Square

Ini adalah pilihan yang sangat baik untuk API dengan banyak titik akhir dan memungkinkan Anda untuk mendeklarasikan titik akhir REST daripada harus kode mereka secara individual seperti dengan perpustakaan lain seperti ion atau Volley. (situs web: http://square.github.io/retrofit/ )

Bagaimana Anda menggunakannya dengan API keuangan?

build.gradle

Tambahkan baris ini ke buid.gradle tingkat Modul Anda:

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

Pembangun Keuangan

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

Cuplikan Keuangan

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

Jika API Anda memerlukan Kunci API atau tajuk lainnya seperti token pengguna, dll. Untuk dikirim, Retrofit membuatnya mudah (lihat jawaban yang luar biasa ini untuk detailnya: https://stackoverflow.com/a/42899766/1024412 ).

Sekali lagi akses API REST

Katakanlah Anda sedang membangun aplikasi "mood mood" yang mencari lokasi pengguna GPS dan memeriksa suhu saat ini di area itu dan memberi tahu mereka mood. Jenis aplikasi ini tidak perlu mendeklarasikan titik akhir API; itu hanya perlu dapat mengakses satu titik akhir API.

Ion

Ini adalah perpustakaan yang bagus untuk jenis akses ini.

Silakan baca jawaban hebat msysmilu ( https://stackoverflow.com/a/28559884/1024412 )

Memuat gambar melalui HTTP

Tembakan

Volley juga dapat digunakan untuk API REST, tetapi karena pengaturan yang lebih rumit diperlukan saya lebih suka menggunakan Retrofit dari Square seperti di atas ( http://square.github.io/retrofit/ )

Katakanlah Anda sedang membangun aplikasi jejaring sosial dan ingin memuat foto profil teman.

build.gradle

Tambahkan baris ini ke buid.gradle tingkat Modul Anda:

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

ImageFetch.java

Volley membutuhkan lebih banyak pengaturan daripada Retrofit. Anda perlu membuat kelas seperti ini untuk menyiapkan RequestQueue, ImageLoader, dan ImageCache, tapi itu tidak terlalu buruk:

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

Tambahkan berikut ini ke file xml tata letak Anda untuk menambahkan gambar:

<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

Tambahkan kode berikut ke metode onCreate (Fragmen, Aktivitas) atau konstruktor (Dialog):

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

Picasso

Perpustakaan lain yang sangat baik dari Square. Silakan lihat situs untuk beberapa contoh bagus: http://square.github.io/picasso/

KG6ZVP
sumber
16

Dengan kata-kata sederhana,

JANGAN LAKUKAN JARINGAN DI THREAD UI

Misalnya, jika Anda melakukan permintaan HTTP, itu adalah tindakan jaringan.

Larutan:

  1. Anda harus membuat utas baru
  2. Atau gunakan kelas AsyncTask

Cara:

Letakkan semua karya Anda di dalamnya

  1. run() metode utas baru
  2. Atau doInBackground() metode kelas AsyncTask.

Tapi:

Ketika Anda mendapatkan sesuatu dari respons Jaringan dan ingin memperlihatkannya pada tampilan Anda (seperti menampilkan pesan respons di TextView), Anda perlu kembali ke utas UI .

Jika Anda tidak melakukannya, Anda akan mendapatkannya ViewRootImpl$CalledFromWrongThreadException.

Bagaimana caranya?

  1. Saat menggunakan AsyncTask, perbarui tampilan dari onPostExecute()metode
  2. Atau panggil runOnUiThread()metode dan perbarui tampilan di dalam run()metode.
Nabin
sumber
12

Anda dapat memindahkan bagian dari kode Anda ke utas lain untuk melepaskan main threaddan menghindari mendapatkan ANR , NetworkOnMainThreadException , IllegalStateException (mis. Tidak dapat mengakses database pada utas utama karena berpotensi mengunci UI untuk jangka waktu yang lama).

Ada beberapa pendekatan yang harus Anda pilih tergantung situasi

Java Thread atau Android HandlerThread

Utas Java hanya sekali pakai dan mati setelah menjalankan metode jalankan.

HandlerThread adalah kelas praktis untuk memulai utas baru yang memiliki looper.

AsyncTask

AsyncTask dirancang untuk menjadi kelas pembantu di sekitar Thread dan Handler dan bukan merupakan kerangka kerja threading umum. AsyncTasks idealnya harus digunakan untuk operasi pendek (paling banyak beberapa detik.) Jika Anda perlu menjaga agar thread tetap berjalan untuk jangka waktu yang lama, sangat disarankan Anda menggunakan berbagai API yang disediakan oleh paket java.util.concurrent seperti Pelaksana , ThreadPoolExecutor dan FutureTask .

Implementasi pool thread ThreadPoolExecutor , DijadwalkanThreadPoolExecutor ...

Kelas ThreadPoolExecutor yang mengimplementasikan ExecutorService yang memberikan kontrol yang baik pada thread pool (Misalnya, ukuran inti kolam, ukuran kumpulan maks, menjaga waktu hidup, dll.)

ScheduledThreadPoolExecutor - kelas yang memperluas ThreadPoolExecutor. Itu dapat menjadwalkan tugas setelah penundaan yang diberikan atau secara berkala.

FutureTask

FutureTask melakukan pemrosesan asinkron, namun, jika hasilnya belum siap atau pemrosesan belum selesai, panggilan get () akan memblokir utas

AsyncTaskLoaders

AsyncTaskLoaders saat mereka memecahkan banyak masalah yang melekat pada AsyncTask

IntentService

Ini adalah pilihan defacto untuk pemrosesan yang berjalan lama di Android, contoh yang baik adalah mengunggah atau mengunduh file besar. Pengunggahan dan unduhan dapat berlanjut bahkan jika pengguna keluar dari aplikasi dan Anda tentu tidak ingin memblokir pengguna untuk dapat menggunakan aplikasi saat tugas-tugas ini sedang berlangsung.

Penjadwal Pekerjaan

Secara efektif, Anda harus membuat Layanan dan membuat pekerjaan menggunakan JobInfo.Builder yang menentukan kriteria Anda kapan harus menjalankan layanan.

RxJava

Perpustakaan untuk menyusun program-program berbasis asinkron dan acara dengan menggunakan urutan yang dapat diamati.

Coroutines (Kotlin)

Inti utama dari itu adalah, itu membuat kode asinkron terlihat sangat mirip dengan sinkron

Baca lebih lanjut di sini , di sini , di sini , di sini

yoAlex5
sumber
Bekerja untuk saya ... Saya telah menggunakan AsyncTask secara ekstensif, tetapi ketika satu tugas sedang berjalan, tugas lain akan menunggu. Saya ingin menyelesaikan ini. Sekarang bekerja dengan executonexecutor. Mari kita lihat bagaimana perilakunya pada perangkat dengan memori rendah.
Suraj Shingade
Silakan lihat metode ini: asyncTask.executeOnExecutor (AsyncTask.THREAD_POOL_EXECUTOR, params); untuk menjalankan tugas Anda secara bersamaan
yoAlex5
10

Meskipun di atas ada kumpulan solusi besar, tidak ada yang disebutkan com.koushikdutta.ion: https://github.com/koush/ion

Ini juga tidak sinkron dan sangat mudah digunakan:

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
    }
});
msysmilu
sumber
10

Solusi baru Threaddan AsyncTask telah dijelaskan.

AsyncTaskidealnya digunakan untuk operasi pendek. Normal Threadtidak disukai untuk Android.

Lihatlah solusi alternatif menggunakan HandlerThread dan Handler

HandlerThread

Kelas praktis untuk memulai utas baru yang memiliki looper. Looper kemudian dapat digunakan untuk membuat kelas handler. Catatan yang start()masih harus dipanggil.

Handler:

Handler memungkinkan Anda mengirim dan memproses objek Message dan Runnable yang terkait dengan ThreadQueue utas. Setiap instance Handler dikaitkan dengan utas tunggal dan antrian pesan utas itu. Ketika Anda membuat Handler baru, ia terikat ke antrian utas / pesan dari utas yang membuatnya - sejak saat itu, ia akan mengirimkan pesan dan runnables ke antrian pesan itu dan menjalankannya saat keluar dari pesan antre.

Larutan:

  1. Membuat HandlerThread

  2. Hubungi start()diHandlerThread

  3. Buat Handlerdengan mendapatkan LooperdariHanlerThread

  4. Cantumkan kode terkait operasi Jaringan Anda di Runnableobjek

  5. Kirim Runnabletugas keHandler

Cuplikan kode contoh, yang alamatnya 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);

Kelebihan menggunakan pendekatan ini:

  1. Membuat baru Thread/AsyncTaskuntuk setiap operasi jaringan itu mahal. Ini Thread/AsyncTaskakan dimusnahkan dan diciptakan kembali untuk operasi Jaringan berikutnya. Tapi dengan Handlerdan HandlerThreadpendekatan, Anda bisa mengirimkan banyak operasi jaringan (sebagai tugas Runnable) untuk tunggal HandlerThreaddengan menggunakan Handler.
Ravindra babu
sumber
8

RxAndroidadalah alternatif lain yang lebih baik untuk masalah ini dan ini menyelamatkan kita dari kerepotan membuat utas dan kemudian memposting hasil pada utas Android UI. Kami hanya perlu menentukan utas yang tugasnya harus dijalankan dan semuanya ditangani secara internal.

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. Dengan menentukan (Schedulers.io()), RxAndroid akan berjalan getFavoriteMusicShows() pada utas yang berbeda.

  2. Dengan menggunakan AndroidSchedulers.mainThread()kami ingin mengamati ini Teramati pada utas UI, yaitu kami ingin onNext()panggilan balik kami dipanggil pada utas UI

Shinoo Goyal
sumber
8

Utas utama adalah utas UI, dan Anda tidak dapat melakukan operasi pada utas utama yang dapat memblokir interaksi pengguna. Anda dapat memecahkan ini dengan dua cara:

Paksa untuk melakukan tugas di utas utama seperti ini

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

Atau buat handler sederhana dan perbarui utas utama jika Anda mau.

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

Dan untuk menghentikan penggunaan utas:

newHandler.removeCallbacks(runnable);

Untuk informasi lebih lanjut lihat ini: threading menyakitkan

Sharath kumar
sumber
Terima kasih. Versi 1 membantu saat menambahkan sebagai Aksi pertama di onCreate.
Ingo
7

Ini bekerja. Baru saja membuat jawaban Dr.Luiji sedikit lebih sederhana.

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

Di Android, operasi jaringan tidak dapat dijalankan di utas utama. Anda dapat menggunakan Utas, AsyncTask (tugas jangka pendek), Layanan (tugas jangka panjang) untuk melakukan operasi jaringan.

Ponsuyambu Velladurai
sumber
7

Mengakses sumber daya jaringan dari utas utama (UI) menyebabkan pengecualian ini. Gunakan utas terpisah atau AsyncTask untuk mengakses sumber daya jaringan untuk menghindari masalah ini.

RevanthKrishnaKumar V.
sumber