java - كيف أقوم بإصلاح android.os.NetworkOnMainThreadException؟




thread-exceptions (20)

  1. لا تستخدم strictMode (فقط في وضع التصحيح)
  2. لا تقم بتغيير إصدار SDK
  3. لا تستخدم موضوع منفصل

استخدام الخدمة أو AsyncTask

راجع أيضًا Stack Overflow question:

android.os.NetworkOnMainThreadException إرسال بريد إلكتروني من Android

تلقيت خطأ أثناء تشغيل مشروع 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

كيف يمكنني حل هذه المشكلة؟


أنا حل هذه المشكلة باستخدام Thread جديد.

Thread thread = new Thread(new Runnable() {

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

thread.start(); 

الخطأ بسبب تنفيذ عمليات تشغيل طويلة في مؤشر الترابط الرئيسي ، يمكنك بسهولة حل المشكلة باستخدام AsynTask أو AsynTask Thread . يمكنك الخروج من هذه المكتبة AsyncHTTPClient للتعامل بشكل أفضل.

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

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

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

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

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

بالنسبة لي كان هذا:

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

كان الجهاز الذي أختبره تطبيقي على الإصدار 4.1.2 وهو إصدار SDK 16!

تأكد من أن الإصدار المستهدف هو نفسه مكتبة استهداف Android. إذا لم تكن متأكدًا من المكتبة المستهدفة ، فانقر بزر الماوس الأيمن فوق Project -> Build Path -> Android ، ويجب أن يكون هو الذي تم تحديده.

أيضًا ، كما ذكر آخرون ، قم بتضمين الأذونات الصحيحة للوصول إلى الإنترنت:

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

ضع شفرتك في الداخل:

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

فقط لتوضيح شيء صريح:

الموضوع الرئيسي هو في الأساس مؤشر ترابط واجهة المستخدم.

لذلك لا يعني أنك لا تستطيع القيام بعمليات الشبكة في سلسلة *runOnUiThread(new Runnable() { ... }* الرئيسية أنه لا يمكنك القيام بعمليات شبكة في مؤشر ترابط واجهة المستخدم ، مما يعني أنه لا يمكنك القيام بعمليات شبكة في *runOnUiThread(new Runnable() { ... }* كتلة داخل بعض الصفحات الأخرى ، إما.

(لقد كان لدي لحظة خدش طويلة لمحاولة معرفة السبب في أنني كنت أتلقى هذا الخطأ في مكان آخر غير موضوعي الرئيسي. هذا هو السبب ؛ ساعد هذا الموضوع ؛ ونأمل أن يساعد هذا التعليق شخصًا آخر.)


لا يمكنك تنفيذ I/O الشبكة على مؤشر ترابط واجهة المستخدم على Honeycomb . من الناحية الفنية ، من الممكن في الإصدارات السابقة من Android ، ولكنها فكرة سيئة حقًا لأن ذلك سيتسبب في توقف تطبيقك عن الاستجابة ، ويمكن أن يؤدي إلى مقتل نظام التشغيل لديك بسبب سوء تصرفه. ستحتاج إلى تشغيل عملية الخلفية أو استخدام AsyncTask لإجراء معاملة الشبكة على مؤشر ترابط الخلفية.

هناك مقال حول " خيوط غير مؤلم" على موقع مطور Android وهو مقدمة جيدة لهذا ، وسيوفر لك عمقًا أفضل بكثير من الإجابة التي يمكن تقديمها بشكل واقعي هنا.


هل إجراءات الشبكة على موضوع آخر

فمثلا:

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

وأضف هذا إلى AndroidManifest.xml

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

يتم طرح هذا الاستثناء عندما يحاول تطبيق تنفيذ عملية شبكة على مؤشر الترابط الرئيسي الخاص به. قم بتشغيل التعليمات البرمجية في 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"/>


يحدث هذا الاستثناء بسبب أي مهمة ثقيلة يتم تنفيذها في مؤشر الترابط الرئيسي إذا كانت تلك المهمة تستغرق وقتًا طويلاً .

لتجنب هذا ، يمكننا التعامل معها باستخدام مؤشرات الترابط أو المنفذين

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

يحدث هذا في Android 3.0 والإصدارات الأحدث. من Android 3.0 والإصدارات الأحدث ، تم تقييد استخدام عمليات الشبكة (الوظائف التي تصل إلى الإنترنت) من التشغيل في مؤشر ترابط / مؤشر ترابط واجهة المستخدم الرئيسي (ما ينتج عن طرق الإنشاء والإستئناف في النشاط).

هذا هو لتشجيع استخدام سلاسل منفصلة لعمليات الشبكة. راجع AsyncTask للحصول على مزيد من التفاصيل حول كيفية تنفيذ أنشطة الشبكة بالطريقة الصحيحة.


يمكنك تعطيل الوضع المقيد باستخدام التعليمة البرمجية التالية:

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

هذا غير مستحسن : استخدم واجهة AsyncTask .

رمز كامل لكلتا الطريقتين


الجواب الأعلى من spektom يعمل الكمال.

إذا كنت تكتب AsyncTask inline ولا تمدد AsyncTask ، وفوق هذا ، إذا كانت هناك حاجة للحصول على استجابة من AsyncTask ، فيمكن للمرء استخدام أسلوب get() كما هو موضح أدناه.

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

(من مثاله.)


يجب أن تقوم دائمًا بتشغيل عمليات الشبكة على مؤشر ترابط أو كمهمة غير متزامنة.

ولكن من الممكن إزالة هذا التقييد وتجاوز السلوك الافتراضي ، إذا كنت ترغب في قبول العواقب.

إضافة:

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


الوصول إلى موارد الشبكة من مؤشر ترابط الرئيسي (UI) يسبب هذا الاستثناء. استخدم مؤشر ترابط منفصل أو AsyncTask للوصول إلى مورد شبكة لتجنب هذه المشكلة.


على الرغم من أن هناك بركة حل ضخمة ، لا أحد ذكر com.koushikdutta.ion: https://github.com/koush/ion

كما انها غير متزامن و بسيط جدا لاستخدام:

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

على نظام التشغيل Android ، لا يمكن تشغيل عمليات الشبكة على سلسلة المحادثات الرئيسية. يمكنك استخدام مؤشر الترابط ، AsyncTask (المهام على المدى القصير) ، خدمة (مهام تشغيل طويلة) للقيام بعمليات الشبكة.


هذا يعمل. فقط جعل الجواب Dr.Luiji أبسط قليلا.

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

هناك طريقة أخرى ملائمة للغاية للتعامل مع هذه المشكلة - استخدام إمكانيات التزامن rxJava. يمكنك تنفيذ أي مهمة في الخلفية ونشر النتائج إلى موضوع رئيسي بطريقة مريحة للغاية ، بحيث يتم تسليم هذه النتائج إلى سلسلة المعالجة.

أول نصيحة الإجابة التحقق هي استخدام AsynTask. نعم ، هذا هو الحل ، ولكنه عفا عليه الزمن في الوقت الحاضر ، لأن هناك أدوات جديدة حولها.

String getUrl() {
    return "SomeUrl";
}

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

private void processResponse(Object o) {

}

يوفر أسلوب getUrl عنوان URL ، وسيتم تنفيذه على مؤشر الترابط الرئيسي.

makeCallParseResponse (..) - هل العمل الفعلي

processResponse (..) - سوف تعالج النتيجة على الموضوع الرئيسي.

سيبدو رمز التنفيذ غير المتزامن كما يلي:

rx.Observable.defer(new Func0<rx.Observable<String>>() {
    @Override
    public rx.Observable<String> call() {
        return rx.Observable.just(getUrl());
    }
})
    .subscribeOn(Schedulers.io())
    .observeOn(Schedulers.io())
    .map(new Func1<String, Object>() {
        @Override
        public Object call(final String s) {
            return makeCallParseResponse(s);
        }
    })
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(new Action1<Object>() {
        @Override
        public void call(Object o) {
             processResponse(o);
        }
    },
    new Action1<Throwable>() {
        @Override
        public void call(Throwable throwable) {
            // Process error here, it will be posted on
            // the main thread
        }
    });

بالمقارنة مع AsyncTask ، تسمح هذه الطريقة لتبديل المجدولين عددًا عشوائيًا من المرات (على سبيل المثال ، إحضار البيانات على جدولة واحدة ومعالجة تلك البيانات على آخر (على سبيل المثال ، Scheduler.computation ()) .يمكنك أيضًا تعريف المجدلين الخاصين بك.

لاستخدام هذه المكتبة ، ضمّن السطور التالية في ملف build.gradle:

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

تتضمن التبعية الأخيرة دعم مجدول .mainThread ().

هناك كتاب إلكتروني ممتاز عن rx-java .







thread-exceptions