Soru Android.os.NetworkOnMainThreadException nasıl düzeltilir?


Android projemi RssReader için çalıştırırken bir hatam var.

Kod:

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

Ve aşağıdaki hatayı gösterir:

android.os.NetworkOnMainThreadException

Bu sorunu nasıl giderebilirim?


1982
2018-06-14 12:02


Menşei


Bu blog gönderisini oku Daha fazla bilgi için NetworkOnMainThreadException üzerinde. Bunun neden Android 3.0 ve üstü sürümlerde gerçekleştiğini açıklıyor. - Adrian Monk
Rite pistte olmak için önce androidde Network Requests hakkında daha sonra okumak için "Volley" çalışmasını tavsiye ederim. - Anuj Sharma
Bu sorunu çözen birçok alternatif kütüphane var. Birçok listeleniyor bu sayfanın alt kısmında. Daha fazlasına sahipseniz, onları alırız :) - Snicolas
İnternet aktivitelerini ana (UI) diziden ayrı bir iş parçacığı üzerinde çalıştırmanız gerekir. - Naveed Ahmad
stackoverflow.com/questions/16439587/...    yukarıdaki bağlantı iyidir - nguyenvangiangbn


Cevaplar:


Bir uygulama, ana iş parçacığı üzerinde bir ağ işlemi gerçekleştirmeye çalıştığında bu istisna atılır. Kodunuzu çalıştırın 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
    }
}

Görevi nasıl yürütülür?

İçinde MainActivity.java dosya bu satırı sizin için ekleyebilirsiniz oncreate() yöntem

new RetrieveFeedTask().execute(urlToRssFeed);

Bunu eklemeyi unutma AndroidManifest.xml dosya:

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

2240
2018-06-14 12:15



Yukarıdaki kod snippet'inin tercihen özel bir alt sınıf (iç sınıf) olması gerektiğine dikkat etmek istiyorum. Bu şekilde, AsyncTask bittiğinde, hala sınıfınızın gizemlerini idare edebilirsiniz. - dyslexicanaboko
Aslında yukarıda bahsettiğim gibi aynı şeyi yaptım ama bu hatayla yüzleştim java.lang.RuntimeException: Looper.prepare () olarak adlandırılmamış iş parçacığı içinde işleyici oluşturulamıyor - Dhruv Tyagi
Bu tam olarak yanlış cevaptır. Bunu her zaman halkların kodunda görüyorum ve her zaman onu düzeltmek zorunda kalması can sıkıcı. AsyncTask ağ etkinliği için kullanılmamalıdır, çünkü etkinlikle bağlantılıdır, ancak etkinlik yaşam döngüsü değildir. Aygıtı bu görevle birlikte döndürmek koşturuyor, bir istisna oluşturuyor ve uygulamanıza çarpıyor. Verileri sqlite veritabanında yerine bırakan bir IntentService kullanın. - Brill Pappin
Dikkat, AsyncTask çoğu zaman etkinlik ağı işlemleri için kullanılmaması gerektiğinde kullanılır. Yaşam döngüsü, etkinlikle eşleşmiyor. Veri almak için bir IntentService ve görünümün arkasındaki veritabanı kullanmalısınız. - Brill Pappin
Bu tür durumlarda AsyncTask Loader'ı şiddetle tavsiye ediyorum. - Roman Gherta


Neredeyse her zaman ağ işlerini bir iş parçacığında veya eşzamansız bir görev olarak çalıştırmalısınız.

Ama o olduğu Bu kısıtlamayı kaldırmak mümkündür ve sonuçları kabul etmeye istekli iseniz, varsayılan davranışı geçersiz kılın.

Eklemek:

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

StrictMode.setThreadPolicy(policy); 

Sınıfında,

ve

Bu izni android manifest.xml dosyasında ekle:

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

sonuçlar:

Uygulamanız (sivilceli internet bağlantısında) tepkisiz hale gelir ve kullanıcı kilitlenir, kullanıcı yavaşlık algılar ve bir zorla öldürme yapmak zorundadır. Ayrıca, uygulama yöneticisini uygulamanızı öldürerek kullanıcıya uygulamayı durdurduğunu bildirirsiniz.

Android, yanıtlama için tasarlamada iyi programlama uygulamalarına ilişkin bazı iyi ipuçlarına sahiptir: http://developer.android.com/reference/android/os/NetworkOnMainThreadException.html


557
2018-02-15 06:59



Bu çok kötü bir fikir. Çözüm, ana iş parçasındaki ağ IO'yu engellemektir (kabul edilen yanıtın gösterdiği gibi). - MByD
Bununla sadece gerçek problemini saklıyorsun. - Alex
@TwistedUmbrella AsyncTask bir kod sayfası eklemez, 6 satır ekler (sınıf bildirimi, not geçersiz kılma, doInBackground deklarasyon, 2 kapanış parantezi ve bir çağrı execute()). Diğer yandan, bahsettiğiniz bir yerden tek bir getirme bile UI yanıtında önemli bir gecikme getirir. Tembel olmayın. - Zoltán
@TwistedUmbrella Aptal olma. Kötü uygulamalar bu şekilde yapılır. Bir metin dosyası veya bir resim galerisi indiriyor olsun, en iyi uygulamaları izlemelisiniz. Ve ana iş parçasındaki ağ IO'nun çalışması en iyi uygulama değildir. - States
Upvoted. Bu cevap doğrudur ve ne naif ne de aptal olan pek çok programcı için, sadece SYNCHRONOUS gerektiren (yani: uygulamayı engellemeli) çağrı, tam olarak ihtiyaç duyulan şey budur. Android'in istisnayı varsayılan olarak attığından çok daha fazlasıyım (IMHO bu çok "yardımcı" bir şey!) - ama "teşekkürler, ama - aslında bu benim niyet ettiğim şey" diyerek eşit derecede mutluyum. o. - Adam


Bu sorunu yeni kullanarak çözdüm Thread.

Thread thread = new Thread(new Runnable() {

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

thread.start(); 

346
2018-01-21 16:31



Her zaman bir ağ işlemi gerçekleştirmek istediğinizde yeni bir iş parçacığı oluşturmak yerine, tek bir iş parçacığı yürütme hizmetini de kullanabilirsiniz. - Alex Lockwood
Basit ama tehlikeli. Anonim Runnable, kapama sınıfına (örn. Aktiviteniz veya Fragmanınız), iş parçacığı tamamlanıncaya kadar çöp toplanmasını önleyen dolaylı bir başvuruya sahiptir. En azından önceliği Process.BACKGROUND olarak ayarlamalısınız, aksi takdirde bu iş parçacığı ana / ui iş parçacığıyla aynı önceliğe sahip olacak ve yaşam döngüsü yöntemleri ve kullanıcı kare hızlarıyla çakışacaktır (koreograftan günlük olarak uyarılara dikkat edin). - Stevie
@ Öncelik nasıl ayarlanır? ne runnble ne de executorService böyle bir belirleyici metodu var - J. K.
@ J.K. Özel bir ThreadFactory ile ExecutorService verin ve iş parçacığı döndürmeden önce Thread.setPriority öğesini çağırın. - Stevie
Bir ağ çağrısı yapmak için tasarlanan bir şey için önceliğin ayarlanması (engellenecek) aşırı hızlı görünüyor. - john16384


Ağı gerçekleştiremezsiniz I / O kullanıcı arayüzü iş parçacığında Bal peteği. Teknik olarak olduğu Android'in önceki sürümlerinde mümkün olsa da, uygulamanızın yanıt vermeyi durdurması ve işletim sisteminin kötü davrandığı için uygulamanızı öldürmesiyle sonuçlanabileceği için gerçekten kötü bir fikirdir. Bir arka plan işlemini çalıştırmanız veya ağ işleminizi arka plan iş parçacığında gerçekleştirmek için AsyncTask kullanmanız gerekir.

Hakkında bir makale var Ağrısız Diş Android geliştirici sitesinde bunun iyi bir tanıtımı vardır ve bu, size burada sağlanabilecek olandan çok daha iyi bir cevap derinliği sağlayacaktır.


124
2018-06-14 12:07





Kabul edilen cevabın bazı önemli düşüşleri var. Ağınız için AsyncTask'ı kullanmanız tavsiye edilmez. Gerçekten mi ne yaptığını biliyorsun. Aşağı tarafların bazıları şunlardır:

  • AsyncTask'ın statik olmayan iç sınıflar olarak oluşturması, kapsayıcı Etkinlik nesnesine, içeriğine ve bu etkinlik tarafından oluşturulan tüm Görünüm hiyerarşisine örtük bir başvurusu vardır. Bu referans, AsyncTask'ın arka plan çalışması tamamlanana kadar Etkinlik'in çöp toplanmasını önler. Kullanıcının bağlantısı yavaşsa ve / veya indirme işlemi büyükse, bu kısa süreli bellek sızıntıları bir sorun haline gelebilir - örneğin yönlendirme birkaç kez değişiyorsa (ve yürütme görevlerini iptal etmiyorsanız) veya kullanıcı geziniyorsa Faaliyetten uzak.
  • AsyncTask, yürütmekte olduğu platforma bağlı olarak farklı yürütme özelliklerine sahiptir: API seviyesi 4'ten önce AsyncTasks, tek bir arka plan iş parçacığı üzerinde seri olarak yürütülür; API düzey 4'ten API seviye 10'a kadar, AsyncTasks 128 iş parçacığı kadar bir havuz üzerinde yürütülür; API düzey 11'den itibaren AsyncTask tek bir arka plan iş parçacığı üzerinde seri olarak yürütülür (aşırı yüklenenleri kullanmazsanız) executeOnExecutoryöntem ve alternatif bir icracı kaynağı). İKS'de seri olarak çalıştırıldığında iyi çalışan kod, eşzamanlı olarak Gingerbread üzerinde yürütüldüğünde kırılabiliyor, örneğin, istem dışı yürütme bağımlılıkları varsa.

Kısa süreli bellek sızıntılarından kaçınmak istiyorsanız, tüm platformlarda iyi tanımlanmış yürütme özelliklerine sahip olun ve gerçekten güçlü bir ağ yönetimi oluşturmak için bir temele sahip olun, şunları düşünebilirsiniz:

  1. Bunun için iyi bir iş çıkarmış bir kütüphane kullanmak - ağdaki lib'lerin güzel bir karşılaştırması var. bu soruveya
  2. Kullanma Service veya IntentService bunun yerine, belki de PendingIntent Sonucun Aktivite ile geri döndürülmesi onActivityResult yöntem.

IntentService yaklaşımı

Aşağı taraf:

  • Daha fazla kod ve karmaşıklık AsyncTaskDüşündüğün kadar olmasa da
  • Sıra istekleri olacak ve bunları tek arka plan iş parçacığı. Bunu değiştirerek kolayca kontrol edebilirsiniz IntentService eşdeğer Service belki de uygulama Bu.
  • Şu an başkalarını düşünemiyorum.

Yukarı tarafta:

  • Kısa süreli bellek sızıntısı sorununu önler
  • Ağ operasyonları uçuş sırasındayken etkinliğiniz yeniden başlarsa, indirme işleminin sonucunu yine de onActivityResult yöntem
  • Güçlü ağ kodunu oluşturmak ve yeniden kullanmak için AsyncTask'tan daha iyi bir platform. Örnek: Önemli bir yükleme yapmanız gerekiyorsa bunu AsyncTask içinde ActivityAncak, kullanıcı bağlamı bir telefon çağrısı almak için uygulamadan çıkarsa, sistem Mayıs ayı Yükleme tamamlanmadan önce uygulamayı öldür. Bu daha az ihtimalle Bir uygulamayı aktif olarak öldürmek Service.
  • Eğer kendi eşzamanlı versiyonunu kullanırsanız IntentService (Yukarıda bağladığım gibi) eşzamanlılık seviyesini Executor.

Uygulama özeti

Bir uygulayabilirsiniz IntentService tek bir arka plan iş parçacığı üzerinde indirme yapmak oldukça kolay.

1. Adım: Bir IntentService indirmek için. Buradan ne indirileceğini anlatabilirsin Intent Ekstralar, ve bunu geçmek PendingIntent sonuca dönmek için kullanmak 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. Adım: Hizmeti bildirime kaydedin:

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

3. Adım: Hizmeti, sonucu döndürmek için Hizmetin kullanacağı bir PendingResult nesnesini geçirerek Etkinliğinden çağırın:

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. Adım: Sonucu onActivityResult uygulamasında ele alın:

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

Tam bir çalışan Android-Studio / gradle projesi içeren bir github projesi var İşte.


118
2018-01-22 13:17



AsyncTask tam olarak bunu yapmamak için olduğu için, IntentService bunu yapmak için doğru yoldur. - Brill Pappin
@BrillPappin AsyncTask'ın dezavantajlarını vurgulamak için neredeyse tamamen katılıyorum ve yeniden yazdım. (Hala bir tane olduğunu düşünüyorum. çok küçük vakaların sayısı - ne yaptığınızı gerçekten biliyorsanız - bu belki AsyncTask kullanmak için Tamam, ama kabul edilen cevap herhangi bir dezavantajları işaret etmiyor ve Android iyilik için çok popülerdir). - Stevie
Gerçekten ihtiyacın var mı IllustrativeRSS? RSS ile çalışmıyorsan ne yapmalı? - Cullub


  1. StrictMode kullanmayın (yalnızca hata ayıklama modunda)
  2. SDK sürümünü değiştirmeyin
  3. Ayrı bir iplik kullanmayın

Hizmet veya AsyncTask'ı Kullan

Ayrıca Yığın Taşması sorusuna bakın:

android.os.NetworkOnMainThreadException Android'den bir e-posta gönderiyor


59
2017-08-18 09:18



Belki de bir Servis kullandığınızda hala ayrı bir iş parçacığı oluşturmanız gerektiğini vurgulamaya değer - Ana iş parçacığında servis geri bildirimleri çalışır. Diğer taraftan, bir IntentService, bir arka plan iş parçacığı üzerinde onHandleIntent yöntemini çalıştırır. - Stevie
Uzun süreli operasyonlar için AsyncTask kullanmamalısınız! Yönergeler maksimum 2 ila 3 saniye arasıdır. - Dage


Başka bir iş parçacığında ağ eylemleri yapın

Örneğin:

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

Ve bunu AndroidManifest.xml dosyasına ekleyin

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

51
2017-12-24 14:33



Ancak, iş parçacığı sona erdiğinde nasıl bulabiliriz, böylece kullanıcı arabirimindeki sonraki görevlerimizi gerçekleştirebiliriz? AsyncTask bunu yapabilmeyi sağlıyor. Runnable konuları kullanarak aynı şeyi yapmak için bir yolu var mı? - Piyush Soni
Kodunuzu adım adım işleyecek, böylece kodun bitiminde işleyici UI iş parçacığına geri dönmeniz gerekir. - henry4343
mobileorchard.com/... - henry4343
Çalışan iş parçacığı üzerinde çalıştırıldığı için, uyumsuz görev veya amaç hizmeti kullanabilirsiniz. - Chetan Chaudhari