簡體   English   中英

如何修復“android.os.NetworkOnMainThreadException”?

[英]How can I fix 'android.os.NetworkOnMainThreadException'?

為 RssReader 運行 Android 項目時出現錯誤。

代碼:

URL url = new URL(urlToRssFeed);
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser();
XMLReader xmlreader = parser.getXMLReader();
RssHandler theRSSHandler = new RssHandler();
xmlreader.setContentHandler(theRSSHandler);
InputSource is = new InputSource(url.openStream());
xmlreader.parse(is);
return theRSSHandler.getFeed();

它顯示以下錯誤:

android.os.NetworkOnMainThreadException

我該如何解決這個問題?

注意: AsyncTask 在 API 級別 30 中已棄用。
異步任務 | Android 開發人員

當應用程序嘗試在其主線程上執行網絡操作時,將引發此異常。 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"/>

您應該幾乎總是在線程上或作為異步任務運行網絡操作。

但是,如果您願意接受后果,可以刪除此限制並覆蓋默認行為。

添加:

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

StrictMode.setThreadPolicy(policy);

在您的 class 中,

在 Android manifest.xml文件中添加此權限:

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

結果:

您的應用程序將(在互聯網連接不穩定的區域)變得無響應並被鎖定,用戶認為速度很慢並且必須強制終止,並且您冒着活動管理器殺死您的應用程序並告訴用戶應用程序已停止的風險。

Android 有一些關於設計響應性的良好編程實踐的好技巧: NetworkOnMainThreadException | Android 開發人員

我使用新的Thread解決了這個問題。

Thread thread = new Thread(new Runnable() {

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

thread.start(); 

公認的答案有一些明顯的缺點。 除非您真的知道自己在做什么,否則不建議使用 AsyncTask 進行聯網。 一些缺點包括:

  • 作為非靜態內部類創建的 AsyncTask 具有對封閉 Activity object、其上下文以及由該 Activity 創建的整個視圖層次結構的隱式引用。 此引用可防止在 AsyncTask 的后台工作完成之前對 Activity 進行垃圾收集。 如果用戶的連接速度很慢,和/或下載量很大,這些短期的 memory 泄漏可能會成為問題 - 例如,如果方向改變了幾次(並且您沒有取消正在執行的任務),或者用戶導航離開活動。
  • AsyncTask 有不同的執行特性,具體取決於它執行的平台:在 API 4 級之前,AsyncTask 在單個后台線程上串行執行; 從 API 級別 4 到 API 級別 10,AsyncTasks 在多達 128 個線程的池上執行; 從 API 級別 11 開始,AsyncTask 在單個后台線程上串行執行(除非您使用重載的executeOnExecutor方法並提供替代執行器)。 在 ICS 上串行運行時運行良好的代碼在 Gingerbread 上並發執行時可能會中斷,例如,如果您有無意的執行順序依賴項。

如果您想避免短期 memory 泄漏,在所有平台上具有明確定義的執行特征,並有基礎構建真正強大的網絡處理,您可能需要考慮:

  1. 使用可以為您完成這項工作的庫 - 在這個問題中有一個很好的網絡庫比較,或者
  2. 使用ServiceIntentService代替,可能使用PendingIntent通過 Activity 的onActivityResult方法返回結果。

IntentService 方法

缺點:

  • AsyncTask更多的代碼和復雜性,雖然沒有你想象的那么多
  • 將請求排隊並在單個后台線程上運行它們。 您可以通過將IntentService替換為等效的Service實現來輕松控制這一點,也許就像這樣
  • 嗯,我現在真的想不出其他人了

優點:

  • 避免短期 memory 泄漏問題
  • 如果您的活動在網絡操作正在進行時重新啟動,它仍然可以通過其onActivityResult方法接收下載結果
  • 一個比 AsyncTask 更好的平台來構建和重用強大的網絡代碼。 示例:如果您需要進行重要的上傳,您可以從Activity中的AsyncTask進行,但如果用戶上下文切換到應用程序之外接聽電話,系統可能會在上傳完成之前終止應用程序。 殺死具有活動Service的應用程序的可能性較小
  • 如果您使用自己的IntentService並發版本(如我上面鏈接的那個),您可以通過Executor控制並發級別。

實施總結

您可以很容易地實現一個IntentService在單個后台線程上執行下載。

第 1 步:創建一個IntentService來執行下載。 您可以通過Intent extras 告訴它要下載什么,並傳遞一個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 reuse, in the case where more than one intent is queued
        parser = new IllustrativeRSSParser();
    }

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

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

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

第 2 步:在清單中注冊服務:

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

第 3 步:從 Activity 調用服務,傳遞一個 PendingResult object,服務將使用它返回結果:

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

第四步:在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 Studio/ Gradle項目可 在此處獲得。

您不能在Honeycomb的 UI 線程上執行網絡I/O 從技術上講,在早期版本的 Android 上可能的,但這是一個非常糟糕的主意,因為它會導致您的應用程序停止響應,並可能導致操作系統因您的應用程序表現不佳而殺死您的應用程序。 您需要運行后台進程或使用 AsyncTask 在后台線程上執行網絡事務。

Android 開發人員網站上有一篇關於無痛線程的文章,這是對此的一個很好的介紹,它將為您提供比此處實際提供的更深入的答案。

這個問題有兩種解決方案。

  1. 不要在主 UI 線程中使用網絡調用。 為此使用異步任務。

  2. setContentView(R.layout.activity_main); 之后將以下代碼寫入您的 MainActivity 文件中;

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

並將以下導入語句放入您的 Java 文件中。

import android.os.StrictMode;

在另一個線程上執行網絡操作。

例如:

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

並將其添加到文件AndroidManifest.xml

<uses-permission android:name="android.permission.INTERNET"/>
  1. 不要使用 strictMode(僅在調試模式下)
  2. 不改SDK版本
  3. 不要使用單獨的線程

使用 Service 或 AsyncTask

另請參閱堆棧溢出問題:

android.os.NetworkOnMainThreadException 從 Android 發送 email

您可以使用以下代碼禁用嚴格模式:

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

不推薦這樣做:使用AsyncTask接口。

兩種方法的完整代碼

基於網絡的操作不能在主線程上運行。 您需要在子線程上運行所有基於網絡的任務或實現 AsyncTask。

這是在子線程中運行任務的方式:

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

將您的代碼放入:

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

這發生在 Android 3.0 及更高版本中。 從 Android 3.0 及更高版本開始,它們已限制使用網絡操作(訪問 Internet 的函數)在主線程/UI 線程中運行(從活動中的 on create 和 on resume 方法產生的內容)。

這是為了鼓勵使用單獨的線程進行網絡操作。 有關如何以正確方式執行網絡活動的更多詳細信息,請參閱AsyncTask

使用Android 注釋是一個選項。 它將允許您簡單地在后台線程中運行任何方法:

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

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

請注意,盡管它提供了簡單性和可讀性的優點,但也有其缺點。

該錯誤是由於在主線程中執行了長時間運行的操作,您可以使用AsynTaskThread輕松糾正該問題。 您可以簽出此庫AsyncHTTPClient以獲得更好的處理。

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

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

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

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

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

您不應在主線程(UI 線程)上執行任何耗時的任務,例如任何網絡操作、文件 I/O 或 SQLite 數據庫操作。 所以對於這種操作,你應該創建一個工作線程,但問題是你不能直接從你的工作線程執行任何 UI 相關的操作。 為此,您必須使用Handler並傳遞Message

為了簡化所有這些事情,Android 提供了各種方式,如AsyncTaskAsyncTaskLoaderCursorLoaderIntentService 因此,您可以根據自己的要求使用其中任何一種。

spektom 的最佳答案非常完美。

如果您正在編寫AsyncTask內聯而不是擴展為 class,除此之外,如果需要從AsyncTask中獲取響應,可以使用get()方法,如下所示。

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

(來自他的例子。)

這僅適用於針對Honeycomb SDK 或更高版本的應用程序。 允許針對早期 SDK 版本的應用程序在其主事件循環線程上進行聯網。

錯誤是 SDK 警告!

對我來說是這樣的:

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

我正在測試我的應用程序的設備是 4.1.2,即 SDK 版本 16!

確保目標版本與您的 Android 目標庫相同。 如果您不確定您的目標庫是什么,請右鍵單擊您的項目 ->構建路徑-> Android ,它應該是勾選的那個。

此外,正如其他人所提到的,包括訪問 Internet 的正確權限:

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

在您的活動中使用它

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

只是為了明確地說明一些事情:

主線程基本上是 UI 線程。

所以說你不能在主線程中進行網絡操作意味着你不能在 UI 線程中進行網絡操作,這意味着你不能在*runOnUiThread(new Runnable() {... }*內的某個其他線程中進行網絡操作, 任何一個。

(我花了很長時間試圖弄清楚為什么我在主線程以外的地方遇到了這個錯誤。這就是原因;這個線程有幫助;希望這個評論能幫助其他人。)

如果執行任務花費太多時間,則由於在主線程上執行的任何繁重任務會發生此異常。

為了避免這種情況,我們可以使用線程執行器來處理它

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

這個問題已經有很多很棒的答案,但是自從這些答案發布以來,已經出現了很多很棒的庫。 這是一種新手指南。

我將介紹幾個用於執行網絡操作的用例,並為每個用例提供兩個解決方案。

REST超過 HTTP

通常是 JSON,但也可以是 XML 或其他。

完整的 API 訪問

假設您正在編寫一個應用程序,讓用戶可以跟蹤股票價格、利率和貨幣匯率。 你會發現一個 JSON API 看起來像這樣:

http://api.example.com/stocks                       // ResponseWrapper<String> object containing a
                                                    // list of strings 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 來自 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 Amazon Ion Java or Volley (website: Retrofit ).

您如何將其與財務 API 一起使用?

文件build.gradle

將這些行添加到您的模塊級別build.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

文件財務Api.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);
}

Class FinancesApiBuilder

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

Class FinancesFragment片段

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 to Add Header Parameter in Retrofit for details).

一次性 REST API 接入

假設您正在構建一個“心情天氣”應用程序,它會查找用戶的 GPS 位置並檢查該區域的當前溫度並告訴他們心情。 此類應用無需聲明 API 端點; 它只需要能夠訪問一個 API 端點。

離子

對於此類訪問,這是一個很棒的庫。

請閱讀msysmiluHow can I fix 'android.os.NetworkOnMainThreadException' 的精彩回答? .

通過 HTTP 加載圖像

排球

Volley 也可用於 REST API,但由於需要更復雜的設置,我更喜歡使用上述 Square 的Retrofit

假設您正在構建一個社交網絡應用程序並想要加載朋友的個人資料圖片。

文件build.gradle

將此行添加到您的模塊級別build.gradle文件:

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

文件ImageFetch.java

Volley 需要比 Retrofit 更多的設置。 您需要像這樣創建 class 來設置 RequestQueue、ImageLoader 和 ImageCache,但這還不錯:

public class ImageFetch {
    private static ImageLoader imageLoader = null;
    private static RequestQueue imageQueue = null;

    public static ImageLoader getImageLoader(Context ctx){
        if(imageLoader == null){
            if(imageQueue == null){
                imageQueue = Volley.newRequestQueue(ctx.getApplicationContext());
            }
            imageLoader = new ImageLoader(imageQueue, new ImageLoader.ImageCache() {
                Map<String, Bitmap> cache = new HashMap<String, Bitmap>();
                @Override
                public Bitmap getBitmap(String url) {
                    return cache.get(url);
                }
                @Override
                public void putBitmap(String url, Bitmap bitmap) {
                    cache.put(url, bitmap);
                }
            });
        }
        return imageLoader;
    }
}

文件user_view_dialog.xml

將以下內容添加到您的布局 XML 文件以添加圖像:

<com.android.volley.toolbox.NetworkImageView
    android:id="@+id/profile_picture"
    android:layout_width="32dp"
    android:layout_height="32dp"
    android:layout_alignParentTop="true"
    android:layout_centerHorizontal="true"
    app:srcCompat="@android:drawable/spinner_background"/>

文件UserViewDialog.java

將以下代碼添加到 onCreate 方法(Fragment、Activity)或構造函數(Dialog)中:

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

畢加索

Picasso是 Square 的另一個優秀圖書館。 請參閱網站以獲取一些很好的示例。

簡單來說,

不要在 UI 線程中做網絡工作

例如,如果您執行 HTTP 請求,則這是一個網絡操作。

解決方案:

  1. 您必須創建一個新線程
  2. 使用AsyncTask class

方法:

把你所有的作品都放進去

  1. 新線程的run()方法
  2. 或者AsyncTask class 的doInBackground()方法。

但:

當您從網絡響應中獲取某些內容並希望將其顯示在您的視圖中時(例如在 TextView 中顯示響應消息),您需要返回到 UI線程。

如果你不這樣做,你會得到ViewRootImpl$CalledFromWrongThreadException

如何

  1. 使用 AsyncTask 時,從onPostExecute()方法更新視圖
  2. 或者調用runOnUiThread()方法並更新run()方法中的視圖。

您可以將部分代碼移動到另一個線程以卸載main thread並避免出現ANRNetworkOnMainThreadExceptionIllegalStateException (例如,無法訪問主線程上的數據庫,因為它可能會長時間鎖定 UI) .

您應該根據情況選擇一些方法

Java 線程或 Android HandlerThread

Java 線程是一次性使用的,並且在執行其運行方法后死亡。

HandlerThread 是一個方便的 class ,用於啟動具有彎針的新線程。

AsyncTask (在 API 級別 30 中已棄用

AsyncTask被設計為圍繞ThreadHandler的 helper class,並不構成通用線程框架。 AsyncTasks 最好用於短操作(最多幾秒鍾)。如果您需要保持線程長時間運行,強烈建議您使用 java.util.concurrent package 提供的各種 API,例如ExecutorThreadPoolExecutorFutureTask

由於線程獨占了 UI 組件,所以無法訪問某些 View,這就是 Handler 來救場的原因

【執行器框架】

實現 ExecutorService 的 ThreadPoolExecutor class 可以對線程池進行精細控制(例如,核心池大小、最大池大小、保持活動時間等)

ScheduledThreadPoolExecutor - 擴展 ThreadPoolExecutor 的 class。 它可以在給定的延遲后或定期安排任務。

未來任務

FutureTask 執行異步處理,但是如果結果尚未准備好或處理尚未完成,調用 get() 將阻塞線程

異步任務加載器

AsyncTaskLoaders 因為它們解決了 AsyncTask 固有的許多問題

意向服務

這是在 Android 上長時間運行處理的實際選擇,一個很好的例子是上傳或下載大文件。 即使用戶退出應用程序,上傳和下載仍可能繼續,並且您當然不希望在這些任務進行時阻止用戶使用應用程序。

作業調度器

實際上,您必須創建一個服務並使用 JobInfo.Builder 創建一個作業,它指定何時運行該服務的標准。

RxJava

使用可觀察序列組成異步和基於事件的程序的庫。

協程(Kotlin)

它的主要要點是,它使異步代碼看起來很像同步

在此處此處此處此處閱讀更多信息。

已經解釋了新的ThreadAsyncTask解決方案。

理想情況下, AsyncTask應該用於短操作。 普通Thread不適用於 Android。

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

處理線程

方便的 class 用於啟動具有彎針的新線程。 然后可以使用 looper 來創建處理程序類。 請注意,仍然必須調用start()

處理程序:

Handler 允許您發送和處理與線程的 MessageQueue 關聯的 Message 和 Runnable 對象。 每個 Handler 實例都與單個線程和該線程的消息隊列相關聯。 當您創建一個新的處理程序時,它會綁定到創建它的線程的線程/消息隊列——從那時起,它會將消息和可運行對象傳遞到該消息隊列並在它們從消息中出來時執行它們隊列。

解決方案:

  1. 創建HandlerThread

  2. HandlerThread上調用start()

  3. 通過從HanlerThread獲取Looper來創建Handler

  4. Runnable object 中嵌入您的網絡操作相關代碼

  5. Runnable任務提交給Handler

示例代碼片段,地址NetworkOnMainThreadException

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

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

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

使用這種方法的優點:

  1. 為每個網絡操作創建新的Thread/AsyncTask任務是昂貴的。 Thread/AsyncTask將被銷毀並為下一次網絡操作重新創建。 但是使用HandlerHandlerThread方法,您可以使用Handler將許多網絡操作(作為 Runnable 任務)提交給單個HandlerThread

Kotlin

如果您使用的是 Kotlin,則可以使用協程

fun doSomeNetworkStuff() {
    GlobalScope.launch(Dispatchers.IO) {
        // ...
    }
}

這行得通。 我只是讓Luiji博士的回答簡單一點。

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

雖然上面有一個巨大的解決方案池,但沒有人提到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
    }
});

解決這個問題還有另一種非常方便的方法——使用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() 調度程序的支持。

一本優秀的 RxJava 電子書

RxAndroid是解決這個問題的另一個更好的選擇,它讓我們免於創建線程然后在 Android UI 線程上發布結果的麻煩。

我們只需要指定需要執行哪些任務的線程,並且一切都在內部處理。

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

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

});

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

    @Override
    public void onCompleted() { }

    @Override
    public void onError(Throwable e) { }

    @Override
    public void onNext(List<String> musicShows) {
        listMusicShows(musicShows);
    }
});
  1. 通過指定(Schedulers.io()) ,RxAndroid 將在不同的線程上運行getFavoriteMusicShows()

  2. 通過使用AndroidSchedulers.mainThread() ,我們希望在 UI 線程上觀察這個 Observable,即,我們希望在 UI 線程上調用我們的onNext()回調。

主線程是 UI 線程,不能在主線程中進行可能會阻塞用戶交互的操作。 您可以通過兩種方式解決此問題:

像這樣強制在主線程中執行任務

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

或者創建一個簡單的處理程序並根據需要更新主線程。

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

並停止線程使用:

newHandler.removeCallbacks(runnable);

有關更多信息,請查看: Painless threading

在 Android 上,網絡操作無法在主線程上運行。 您可以使用 Thread、AsyncTask(短期運行任務)、Service(長期運行任務)來進行網絡操作。

從主 (UI) 線程訪問網絡資源會導致此異常。 使用單獨的線程或 AsyncTask 訪問網絡資源以避免此問題。

您可以使用KotlinAnko

KotlinAndroid的新官方語言。 您可以在此處找到更多信息: Kotlin for Android

Anko是 Android 中 Kotlin 的受支持庫。 GitHub 頁面上有一些文檔。

真正有用的解決方案,只有@AntonioLeiva 編寫的幾行代碼: Using Anko to run background tasks with Kotlin in Android (KAD 09)

doAsync {
    var result = runLongTask()
    uiThread {
        toast(result)
    }
}

雖然很簡單,當您在 UI 線程上運行后台作業時會發生NetworkOnMainThread ,因此您必須做的一件事就是在后台運行longTask 作業 您可以在 Android 應用程序中使用此方法和帶有 AnkoKotlin來執行此操作。

Android 不允許在主線程上運行長時間運行的操作。

所以只需使用不同的線程並在需要時將結果發布到主線程。

new Thread(new Runnable() {
        @Override
        public void run() {
            /*
            // Run operation here
            */
            // After getting the result
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    // Post the result to the main thread
                }
            });
        }
    }).start();

不允許在 Android 上的 UI 線程上實現網絡操作。 You will have to use AsyncTask class to perform network related operations like sending API request, downloading image from a URL, etc. and using callback methods of AsyncTask, you can get you result in onPostExecute menthod and you will be in the UI thread and you可以使用來自 web 服務或類似服務的數據填充 UI。

示例:假設您要從 URL 下載圖像: https://www.samplewebsite.com/sampleimage.jpg

使用 AsyncTask 的解決方案:分別是。

    public class MyDownloader extends AsyncTask<String,Void,Bitmap>
    {
        @Override
        protected void onPreExecute() {
            // Show progress dialog
            super.onPreExecute();
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            //Populate Ui
            super.onPostExecute(bitmap);
        }

        @Override
        protected Bitmap doInBackground(String... params) {
            // Open URL connection read bitmaps and return form here
            return result;
        }

        @Override
        protected void onProgressUpdate(Void... values) {
            // Show progress update
            super.onProgressUpdate(values);
        }


    }
}

注意:不要忘記在 Android 清單文件中添加 Internet 權限。 它會像魅力一樣工作。 :)

我們還可以使用RxJava將網絡操作移至后台線程。 它也相當簡單。

webService.doSomething(someData)
          .subscribeOn(Schedulers.newThread())-- This for background thread
          .observeOn(AndroidSchedulers.mainThread()) -- for callback on UI
          .subscribe(result -> resultText.setText("It worked!"),
              e -> handleError(e));

你可以用 RxJava 做更多的事情。 以下是 RxJava 的一些鏈接。 隨意挖掘。

Android 中的 RxJava 異步任務

http://blog.stablekernel.com/replace-asynctask-asynctaskloader-rx-observable-rxjava-android-patterns/

在 Android 上,網絡操作無法在主線程上運行。 您可以使用ThreadAsyncTask (短期運行任務)、 Service (長期運行任務)來進行網絡操作。 當應用程序嘗試在其主線程上執行網絡操作時,會引發android.os.NetworkOnMainThreadException 如果您的任務耗時超過 5 秒,則需要強制關閉。

AsyncTask中運行您的代碼:

class FeedTask extends AsyncTask<String, Void, Boolean> {

    protected RSSFeed doInBackground(String... urls) {
       // TODO: Connect
    }

    protected void onPostExecute(RSSFeed feed) {
        // TODO: Check this.exception
        // TODO: Do something with the feed
    }
}

或者

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

不建議這樣做。

但出於調試目的,您也可以使用以下代碼禁用嚴格模式:

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

Google 在 Android 11 中棄用了 Android AsyncTask API。

即使你在 main Activity 之外創建了一個線程 class,只要在 main 中調用它,你也會得到同樣的錯誤。 調用必須在可運行線程內,但如果您需要一些異步代碼在后台執行或稍后在此處發布,您可以查看 Kotlin 和 Java 的一些替代方案:

*https://stackoverflow.com/questions/58767733/android-asynctask-api-deprecating-in-android-11-what-are-the-alternatives*

對我特別有用的是 mayank1513 對上述鏈接中可運行線程的Java 8實現的回答。 代碼如下:

new Thread(() -> {
        // do background stuff here
        runOnUiThread(()->{
            // OnPostExecute stuff here
        });
    }).start();

但是,您可以先在代碼的某些部分定義線程,然后在其他地方啟動它,如下所示:

線程定義

Thread thread = new Thread(() -> {
            // do background stuff here
            runOnUiThread(()->{
                // OnPostExecute stuff here
            });
        });

線程調用

thread.start();

我希望這可以避免人們看到已棄用的 AsyncTask 的頭痛。

當應用程序嘗試在其主線程上執行網絡操作時,將引發此異常。 如果您的任務耗時超過 5 秒,則需要強制關閉。

AsyncTask中運行您的代碼:

class RetrieveFeedTask extends AsyncTask<String, Void, Boolean> {

    protected RSSFeed doInBackground(String... urls) {
       // TODO: Connect
    }

    protected void onPostExecute(RSSFeed feed) {
        // TODO: Check this.exception
        // TODO: Do something with the feed
    }
}

您還可以使用以下代碼使用嚴格模式來解決此問題。 它也是解決此問題的替代方法。

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

但最佳實踐是使用 AsyncTask。

如何修復 android.os.NetworkOnMainThreadException

什么是 NetworkOnMainThreadException:

在 Android 中,我們必須在 UI 線程(主線程)上進行的所有 UI 操作。 如果我們在主線程上執行后台操作或一些網絡操作,那么我們就有可能發生這個異常並且應用程序不會響應。

如何修復它:

為了避免這個問題,您必須使用另一個線程進行后台操作或網絡操作,例如使用 asyncTask 並使用一些庫進行網絡操作,例如 Volley、AsyncHttp 等。

android.os.NetworkOnMainThreadException 在主線程進行網絡操作時拋出。 您最好在 AsyncTask 中執行此操作以刪除此異常。 這樣寫:

    new AsyncTask<Void,String,String>(){

        @Override
        protected Void doInBackground(Void... params) {
            // Perform your network operation.
            // Get JSON or XML string from the server.
            // Store in a local variable (say response) and return.
            return response;
        }

        protected void onPostExecute(String results){
            // Response returned by doInBackGround() will be received
            // by onPostExecute(String results).
            // Now manipulate your jason/xml String(results).
        }

    }.execute();
}

使用AsycTask在后台線程中執行此操作

Java

class NetworkThread extends AsyncTask<String, Void, String> {

    protected Void doInBackground(String... arg0) {
        //Your implementation
    }

    protected void onPostExecute(String result) {
        // TODO: do something with the feed
    }
}

隨時隨地撥打電話

new NetworkThread().execute("Your URL here");

Kotlin

internal class MyNetworkTask : AsyncTask<String, Void, RSSFeed>() {

    override fun doInBackground(vararg urls: String): RSSFeed? {
        try {
             // download
             // prepare RSSFeeds
             return RSSFeeds
         } catch (e: Exception) {
            //handle exception
            return null
        }
    }

    override fun onPostExecute(feed: RSSFeed) {
        // TODO: check this.exception
        // TODO: do something with the feed
    }
}

致電 kotlin

MyNetworkTask().execute(url)

我有一個類似的問題。 我剛剛在您的活動的oncreate方法中使用了以下內容。

// Allow strict mode
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);

而且效果很好。

需要注意的是,將其用於超過 100 毫秒的網絡請求會導致明顯的 UI 凍結和潛在的 ANR(應用程序無響應),因此請記住這一點。

來自開發者安卓

理想情況下,AsyncTasks 應該用於短操作(最多幾秒鍾。)

使用newCachedThreadPool是一個不錯的選擇。 您也可以考慮其他選項,例如newSingleThreadExecutornewFixedThreadPool

    ExecutorService myExecutor = Executors.newCachedThreadPool();
    myExecutor.execute(new Runnable() {
        @Override
        public void run() {
            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);
        }
    });

ThreadPoolExecutor是一個幫助器 class 使這個過程更容易。 這個 class 管理一組線程的創建,設置它們的優先級,並管理工作在這些線程之間的分配方式。 隨着工作負載的增加或減少,class 會啟動或銷毀更多線程以適應工作負載。

有關 Android 線程的更多信息,請參閱內容。

我以一種簡單的方式解決了這個問題......

我在oncreate StrictMode.enableDefaults();之后添加並解決了這個問題。

或者

使用ServiceAsyncTask來解決這個問題

筆記:

Do not change SDK version
Do not use a separate thread

有關更多信息, 請查看此

由於 Android 是在單線程上工作,所以你不應該在主線程上做任何網絡操作。 有多種方法可以避免這種情況。

使用以下方式進行網絡操作

  • Asysnctask :用於不需要太多時間的小型操作。
  • Intent Service : 用於需要大量時間的網絡操作。
  • 使用VolleyRetrofit等自定義庫來處理復雜的網絡操作

永遠不要使用 StrictMode.setThreadPolicy(policy) ,因為它會凍結你的 UI 並且根本不是一個好主意。

您不能在主線程或 UI 線程上調用網絡。 在 Android 上,如果你想調用網絡,有兩個選項 -

  1. 調用 asynctask,它會運行一個后台線程來處理網絡操作。
  2. 您可以創建自己的可運行線程來處理網絡操作。

我個人更喜歡異步任務。 有關更多信息,您可以參考此鏈接

如果您在 Kotlin 和Anko工作,您可以添加:

doAsync {
    method()
}

您可以使用 Kotlin協程

 class YoutActivity : AppCompatActivity, CoroutineScope {
      
      override fun onCreate(...) {
         launch {  yourHeavyMethod() }
      }

      suspend fun yourHeavyMethod() {
         with(Dispatchers.IO){ yourNetworkCall() }
         ...
         ...
      }
 } 

您可以按照本指南進行操作。

這些答案需要更新以使用更現代的方式連接到 Internet 上的服務器並處理一般的異步任務。

例如,您可以找到在Google Drive API 示例中使用任務的示例。 在這種情況下應該使用相同的方法。 我將使用 OP 的原始代碼來演示這種方法。

首先,您需要定義一個非主線程執行器,並且只需要執行一次:

private val mExecutor: Executor = Executors.newSingleThreadExecutor()

然后在該執行程序中處理您的邏輯,該執行程序將從主線程運行

Tasks.call (mExecutor, Callable<String> {

        val url = URL(urlToRssFeed)
        val factory = SAXParserFactory.newInstance()
        val parser = factory.newSAXParser()
        val xmlreader = parser.getXMLReader()
        val theRSSHandler = RssHandler()
        xmlreader.setContentHandler(theRSSHandler)
        val is = InputSource(url.openStream())
        xmlreader.parse(is)
        theRSSHandler.getFeed()

        // Complete processing and return a String or other object.
        // E.g., you could return Boolean indicating a success or failure.
        return@Callable someResult
}).continueWith{
    // it.result here is what your asynchronous task has returned
    processResult(it.result)
}

continueWith子句將在您的異步任務完成后執行,您將可以訪問任務通過it.result返回的值。

您實際上可以啟動一個新線程。 我之前遇到過這個問題,就通過這種方式解決了。

發生 NetworkOnMainThread 異常是因為您在默認線程,即 UI 線程上調用了一些網絡操作。 根據不允許的Android版本Android 3 (Honeycomb),您應該在主線程外調用網絡操作。

您可以使用 AsyncTask、IntentService 或創建自己的線程並在 run 方法中調用。 有關更多信息,請訪問連接到網絡

Android Jetpack引入WorkManager ,修復了Android 8.1 (Oreo) 后台服務限制問題, Android 5.0以下使用Alarm Manager和JobScheduler 5.0以上)

請使用WorkManager在后台線程上運行任務,即使在用戶關閉應用后它也會繼續運行。

我將網絡訪問 function 返回值轉換為暫停 function,如下所示:


suspend fun isInternetReachable(): Boolean {
  ...
  ...
  return result
}

然后我修改了我使用 function 的位置以適應這個:

...
Globalscope.async{
  ...
  result = isInternetReachable()
  ...
}
...

永遠不要在 UI 線程上做任何長時間運行的工作。 長時間運行的工作可以是與服務器的通信、文件的讀/寫等。這些任務應該在后台線程上。 這就是創建ServiceAsyncTaskThreads的原因。 您可以禁用StrictMode ,這將防止崩潰。 但是,從不建議這樣做。

我建議您至少在調試模式下利用StrictMode 使用下面的代碼來獲取在主線程上減慢您的應用程序的任何問題的日志。

StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
            .detectAll()
            .penaltyLog()
            .build());

您可以設置不同的懲罰:

penaltyLog() // to print log
penaltyDeath() // This will crash you App(so costly penalty)
penaltyDialog() // Show alert when something went lazy on Main thread

這里有更多關於StrictMode的信息: StrictMode | Android 開發人員

截至 2018 年,我建議在 Kotlin 中使用RxJava進行網絡獲取。 下面是一個簡單的例子。

Single.fromCallable {
        // Your Network Fetching Code
        Network.fetchHttp(url) 
    }
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe {
        // What you need to do with your result on the view 
        result -> view.updateScreen(result) 
    }

不同的選擇:

  1. 使用普通的 Java 可運行線程來處理網絡任務,並且可以使用 runOnUIThread() 來更新 UI

  2. 如果您想在獲得網絡響應后更新 UI,可以使用 intentservice/ async 任務

Kotlin版

internal class RetrieveFeedTask : AsyncTask<String, Void, RSSFeed>() {

    override fun doInBackground(vararg urls: String): RSSFeed? {

        try {
             // download
             // prepare RSSFeeds
             return RSSFeeds

         } catch (e: Exception) {

            //handle exception
            return null
        }
    }

    override fun onPostExecute(feed: RSSFeed) {
        // TODO: check this.exception
        // TODO: do something with the feed
    }
}

調用示例,

RetrieveFeedTask().execute(url)

這是一個使用OkHttpFutureExecutorServiceCallable的簡單解決方案:

final OkHttpClient httpClient = new OkHttpClient();
final ExecutorService executor = newFixedThreadPool(3);

final Request request = new Request.Builder().url("http://example.com").build();

Response response = executor.submit(new Callable<Response>() {
   public Response call() throws IOException {
      return httpClient.newCall(request).execute();
   }
}).get();

我在 Kotlin 中使用Thread解決了。 有很多使用 Java 的示例,所以我想在 Kotlin 中添加一個對我有用的解決方案。

 Thread {
     println("NEW THREAD")
     callAPI() // add your own task
 }.start()

因此,正如許多其他人所解釋的那樣,您不能在調用時阻塞主線程,因此有必要創建一個新線程。

Executors.newFixedThreadPool(3).execute(() -> {
      //DO Task;        
 });

使用以下代碼執行繁重的任務。

// Your package here


import java.util.List;
import org.apache.http.NameValuePair;

import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.os.AsyncTask;
import android.view.View.OnSystemUiVisibilityChangeListener;

public class AsyncRequest extends AsyncTask<String, Integer, String> {

    Context context;
    ProgressDialog pDialog;

    // Three Constructors
    public AsyncRequest(Activity a, String m, List<NameValuePair> p) {
        context = a;
        method = m;
        parameters = p;
    }

    public AsyncRequest(Activity a) {
        this.caller = (OnAsyncRequestComplete) a;
        context = a;
    }

    public String doInBackground(String... urls) {

        //Perform your task here
        return result;
    }

    public void onPreExecute() {
        pDialog = new ProgressDialog(context);
        pDialog.setMessage("Please wait..");
        pDialog.setCancelable(false);
        pDialog.show();
    }

    public void onProgressUpdate(Integer... progress) {
        // You can implement some progressBar and update it in this record.
        //   setProgressPercent(progress[0]);
    }

    public void onPostExecute(String response) {
        if (pDialog != null && pDialog.isShowing()) {
            pDialog.dismiss();
        }
        // Get the result here
    }

    protected void onCancelled(String response) {

        if (pDialog != null && pDialog.isShowing()) {
            pDialog.dismiss();
        }
    }
}

您只需在清單標記后的文件manifest.xml中添加以下行

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

並在活動文件中,在綁定語句后添加如下代碼:

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

Android不允許單獨的進程進入主活動線程,HTTP連接在這里是一個獨立的線程。 這就是您收到“ android.os.NetworkOnMainThreadException ”的原因。

可能需要在向用戶顯示 webview 之前檢查實際的 Internet 連接,因為如果沒有 Internet,web 視圖將向用戶顯示頁面未找到錯誤,通常您不會顯示什么.

要檢查 Internet 可用性,可以使用 ping 命令,但如果 Wi-Fi ping 可以在 Wi-Fi 服務器上禁用,因此在這種情況下,您使用 HTTP 連接來檢查請求的狀態。

如果您在向用戶顯示 webview 之前檢查自己的 webview URL 鏈接,這可能是正確的方法。 在這種情況下,您可以使用 Android 的嚴格模式,但不要允許所有策略,因為您不需要它。

您應該只為嚴格模式提供網絡允許策略。 只需將以下行添加到您的代碼中,您就不會收到此錯誤。

StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitNetwork().build();
StrictMode.setThreadPolicy(policy);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM