「android.os.NetworkOnMainThreadException」を修正するには?


2394

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

この問題を解決するにはどうすればよいですか?


131
詳細については、NetworkOnMainThreadExceptionに関するこのブログ投稿を参照してください。これがAndroid 3.0以降で発生する理由を説明しています。
エイドリアンモンク

6
儀式に参加するには、まずAndroidのネットワークリクエストについて読んでから、「ボレー」を学ぶことをお勧めします。
Anuj Sharma 2014

3
この問題を解決する多くの代替ライブラリがあります。多くはこのページの下部にリストされています。もっと多くの場合、私たちはそれらを受け取ります:)
Snicolas 14

メイン(UI)スレッドとは別のスレッドでインターネットアクティビティを実行する必要がある
Naveed Ahmad 2014

「以前のバージョンのAndroidのバグのため、システムはメインスレッドのTCPソケットへの書き込みにストリクトモード違反のフラグを立てませんでした。Android7.0はこのバグを修正しています。この動作を示すアプリは、android.osをスローするようになりました。 NetworkOnMainThreadException。」-だから、私たちの一部は最近までこれにぶつかっていません! developer.android.com/about/versions/nougat/...
ジェイ

回答:


2547

この例外は、アプリケーションがメインスレッドでネットワーク操作を実行しようとしたときにスローされます。でコードを実行します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"/>

37
上記のコードスニペットはサブクラス(内部クラス)、できればプライベートであることに注意してください。これにより、AsyncTaskが終了しても、クラスの内部を操作できます。
dyslexicanaboko 2012

4
実際にuは上記のように私は同じことをしたが、mが、このエラー直面 java.lang.RuntimeExceptionを:Looper.prepare(呼び出されていないスレッド内でハンドラを作成することはできません)
Dhruvティアギ(Tyagi)

68
これはまったく間違った答えです。私は人々のコードでいつもこれに遭遇し、それを常に修正しなければならないのは面倒です。AsyncTaskは、アクティビティのライフサイクルではなくアクティビティに関連付けられているため、ネットワークアクティビティには使用しないでください。このタスクが実行されている状態でデバイスを回転させると、例外が発生し、アプリがクラッシュします。代わりに、sqliteデータベースにデータをドロップするIntentServiceを使用してください。
Brill Pappin

5
注意:AsyncTaskは、アクティビティごとのネットワーク操作で使用すべきでない場合によく使用されます。そのライフサイクルはアクティビティと同期していません。データを取得するには、IntentServiceとビューの背後にあるデータベースを使用する必要があります。
Brill Pappin、2016年

1
@ BrillPappin、FWIW、このAndroid開発者ガイドではを使用しAsyncTask、構成の変更を処理します。
HeyJude

677

ほとんどの場合、ネットワーク操作はスレッド上で実行するか、非同期タスクとして実行する必要があります。

ただし、結果を受け入れる場合は、この制限を削除してデフォルトの動作を上書きすることできます。

追加:

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

StrictMode.setThreadPolicy(policy); 

あなたのクラスでは、

そして

この権限をandroid manifest.xmlファイルに追加します。    

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

結果:

アプリは(むらのあるインターネット接続の領域で)応答しなくなり、ロックされます。ユーザーは速度が遅いと感じて強制終了する必要があり、アクティビティマネージャーがアプリを強制終了し、アプリが停止したことをユーザーに知らせるリスクがあります。

Androidには、応答性を考慮して設計するための優れたプログラミング手法に関するヒントがいくつかあります。http//developer.android.com/reference/android/os/NetworkOnMainThreadException.html


456
これは非常に悪い考えです。解決策は、メインスレッドでネットワークIOを回避することです(受け入れられた回答が示すように)。
MByD

74
これであなたはあなたの本当の問題を隠すだけです。
Alex

28
@TwistedUmbrella AsyncTaskはコードのページを追加せず、6行を追加します(クラス宣言、オーバーライドアノテーション、doInBackground宣言、2つの閉じ角括弧、およびへの呼び出しexecute())。一方、サイトからの1回のフェッチでも、UIの応答性に大幅な遅れが生じます。怠惰にしないでください。
ゾルタン

19
適切なAsyncTaskを実装する前に、コードのサンプルチャンクを実行して何かが機能するかどうかを確認する場合は、これが最適なソリューションだと思います。他のすべての人が言ったように、これは本番アプリでは行わないでください。開発テストの迅速な回避策としてのみです。
hooked82

94
賛成。この答えは正解です。そして、世間知らずでも愚かでもないが、単にSYNCHRONOUS(つまり、アプリをブロックする必要がある)呼び出しを必要とする多くのプログラマにとって、これはまさに必要なことです。Androidがデフォルトで例外をスローすることを私はとても嬉しく思います(IMHOこれは非常に「役立つ」ことです!)-「感謝しますが、これは私が実際に意図したことです」とオーバーライドして、それ。
アダム

428

新しいを使用してこの問題を解決しましたThread

Thread thread = new Thread(new Runnable() {

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

thread.start(); 

7
ネットワーク操作を実行するたびに新しいスレッドを作成する代わりに、シングルスレッドエグゼキューターサービスを使用することもできます。
Alex Lockwood 2013年

66
シンプルですが危険です。匿名のRunnableには、包含クラス(例:アクティビティまたはフラグメント)への暗黙的な参照があり、スレッドが完了するまでガベージコレクションされるのを防ぎます。少なくとも優先順位をProcess.BACKGROUNDに設定する必要があります。そうしないと、このスレッドはメイン/ UIスレッドと同じ優先順位で実行され、ライフサイクルメソッドやUIフレームレートと競合します(振付家からのログの警告に注意してください)。
スティービー

1
@Stevie優先度の設定方法は?runnbleにもexecutorServiceにも、このようなセッターメソッドはありません
JK

1
@JK ExecutorServiceにカスタムThreadFactoryを提供し、スレッドを返す前にスレッドでThread.setPriorityを呼び出します。
スティービー

1
「Androidで直接スレッドを使用することはお勧めしません。解決するよりも多くの問題が発生します」詳細については注意してください。実際には、AsyncTaskはそのために非推奨になっています... techyourchance.com/asynctask-deprecated
Fran Marzoa

170

受け入れられた答えには、いくつかの重大な欠点があります。何をしているのか本当に理解していない限り、ネットワークにAsyncTaskを使用することはお勧めできません。いくつかの欠点は次のとおりです。

  • 非静的内部クラスとして作成されたAsyncTaskには、それを囲むActivityオブジェクト、そのコンテキスト、およびそのアクティビティによって作成されたView階層全体への暗黙的な参照があります。この参照は、AsyncTaskのバックグラウンド作業が完了するまで、Activityがガベージコレクションされるのを防ぎます。ユーザーの接続が遅い、またはダウンロードが大きい場合、これらの短期的なメモリリークが問題になる可能性があります。たとえば、向きが数回変更された場合(および実行中のタスクをキャンセルしない場合)、またはユーザーアクティビティから移動します。
  • AsyncTaskは、実行するプラットフォームに応じて実行特性が異なります。APIレベル4より前のAsyncTasksは、単一のバックグラウンドスレッドでシリアルに実行されます。APIレベル4からAPIレベル10まで、AsyncTasksは最大128スレッドのプールで実行されます。APIレベル11以降、AsyncTaskは単一のバックグラウンドスレッドでシリアルに実行されます(オーバーロードされたexecuteOnExecutorメソッドを使用して代替のエグゼキューターを提供しない場合)。ICSでシリアルに実行する場合に正常に機能するコードは、Gingerbreadで同時に実行すると壊れる可能性があります(不注意による実行順序の依存関係がある場合など)。

短期的なメモリリークを回避し、すべてのプラットフォームにわたって明確に定義された実行特性を持ち、本当に堅牢なネットワーク処理を構築するための基盤がある場合は、次のことを検討することをお勧めします。

  1. あなたのためにこれをうまく行うライブラリを使用する- この質問にはネットワークライブラリの素晴らしい比較があります、または
  2. ServiceまたはのIntentService代わりに、おそらくaを使用しPendingIntentて、アクティビティのonActivityResultメソッドを介して結果を返します。

IntentServiceアプローチ

欠点:

  • AsyncTask思ったほどではありませんが、よりもコードが多く複雑です
  • リクエストをキューに入れ、単一のバックグラウンドスレッドで実行します。おそらくこれのようなIntentService同等のService実装に置き換えることで、これを簡単に制御できます。
  • ええと、今は他の人のことは考えられません

メリット:

  • 短期的なメモリリークの問題を回避
  • ネットワーク操作の実行中にアクティビティが再開した場合でも、そのonActivityResultメソッドを介してダウンロードの結果を受け取ることができます
  • 堅牢なネットワークコードを構築して再利用するためのAsyncTaskよりも優れたプラットフォーム。例:あなたは、重要なアップロードを行う必要がある場合、あなたはからそれを行うことAsyncTaskActivityはなく、ユーザーの電話を取るためにAPPのうちコンテキストが-切り替えた場合、システムがあり、アップロードが完了する前にアプリを殺します。アクティブなアプリケーションを強制終了する可能性低くなりますService
  • 独自の並行バージョンIntentService(上記でリンクしたバージョンなど)を使用している場合は、を使用して並行性のレベルを制御できますExecutor

実装の概要

を実装IntentServiceして、単一のバックグラウンドスレッドでダウンロードを非常に簡単に実行できます。

ステップ1:IntentServiceダウンロードを実行するを作成します。Intentエキストラ経由で何をダウンロードするかを伝え、a PendingIntentを渡して結果をに返すことができますActivity

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

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

public class DownloadIntentService extends IntentService {

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

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

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

    private IllustrativeRSSParser parser;

    public DownloadIntentService() {
        super(TAG);

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

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

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

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

ステップ2:マニフェストにサービスを登録します。

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

ステップ3:アクティビティからサービスを呼び出し、サービスが結果を返すために使用するPendingResultオブジェクトを渡します。

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

ステップ4:onActivityResultで結果を処理します。

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

完全に機能するAndroid-Studio / Gradleプロジェクトを含むGithubプロジェクトは、こちらから入手できます


AsyncTaskはこれを行わない方法であるため、IntentServiceは、これを行う正しい方法であり、根こそぎではありません。
Brill Pappin

3
@BrillPappin私はほぼ完全に同意し、AsyncTaskの欠点を強調するために書き直しました。(私はまだ非常に少数のケースがあると思います-あなたが本当にあなたが何をしているのか知っているなら-AsyncTaskを使用しても問題ないかもしれませんが、受け入れられた答えは欠点を指摘せず、あまりにも人気が高いためですAndroidの)。
スティービー

1
実際に必要IllustrativeRSSですか?RSSを使用していない場合はどうなりますか?
カラブ

私の意見では、Googleは、ガードンコレクションの悪い実装をプログラマー側に置くのではなく、変更するべきです。これはOSの責任です。Android実装の根本的な問題が原因でプログラマーがIntentServiceまたはServiceを使用して仕事をする場合、数年後、GoogleはIntentServiceも悪い習慣であり、別のことを提案します。そして、この話は続きます...したがって、Google開発者はプログラマーではなくAndroidの不良メモリ管理を解決する必要があります。
saeed khalafinejad

私はそれを言うつもりです:この答えは、プロジェクトを共有するためのより良い代替手段よりも多くの方法のようAsyncTaskです。このエラーは、開発者がUIを遅らせるのを防ぐためのものであり、必ずしも一連のインテント/サービスによってセキュリティを危険にさらすように誘惑するものではありませんでした。
放棄されたカート

144

Honeycombの UIスレッドでネットワークI / Oを実行することはできません。技術的に、以前のバージョンのAndroidでも可能ですが、アプリが応答しなくなり、OSが不適切な動作をしてアプリを強制終了する可能性があるため、これは非常に悪い考えです。バックグラウンドスレッドでネットワークトランザクションを実行するには、バックグラウンドプロセスを実行するか、AsyncTaskを使用する必要があります。

Android開発者向けサイトに、Painless Threadingに関する記事があり、これを紹介するのに適しています。これにより、ここで現実的に提供できるよりもはるかに深い回答が得られます。


76
  1. strictModeを使用しない(デバッグモードのみ)
  2. SDKバージョンを変更しないでください
  3. 別のスレッドを使用しないでください

サービスまたはAsyncTaskを使用する

Stack Overflowの質問も参照してください。

Androidからメールを送信するandroid.os.NetworkOnMainThreadException


8
おそらく、サービスを使用する場合でも、別のスレッドを作成する必要があることを強調しておきましょう。サービスコールバックはメインスレッドで実行されます。一方、IntentServiceは、onHandleIntentメソッドをバックグラウンドスレッドで実行します。
スティービー

長時間実行する操作にはAsyncTaskを使用しないでください。ガイドラインでは、最大2〜3秒を指定しています。
2014

76

別のスレッドでネットワークアクションを実行する

例えば:

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

そして、これをAndroidManifest.xmlに追加します

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

5
しかし、UIスレッドで次の一連のタスクを実行できるように、スレッドがこれでいつ終了するかをどのように確認できますか?AsyncTaskは、そのための機能を提供します。実行可能なスレッドを使用して同じことをする方法はありますか?
Piyush Soni、2014年

3
コードは段階的に処理されるため、コードの最後で終了し、ハンドラーを使用してUIスレッドに戻る必要があります
henry4343

1
ワーカータスクで実行されるため、非同期タスクまたはインテントサービスを使用できます。
Chetan Chaudhari 2017

63

次のコードを使用して、strictモードを無効にします。

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

これはお勧めできませんAsyncTask。インターフェースを使用してください。

両方の方法の完全なコード


2
はいANRエラーが発生します。アプリが5秒以内に応答しないことを意味します。
Muhammad Mubashir 2013年

12
これは本当に悪い答えです。スレッドのポリシーを変更するべきではありませんが、より良いコードを書くために、メインスレッドでネットワーク操作を行わないでください!
shkschneider 2013

@Sandeepあなたと他の視聴者もこれを読むべきです。stackoverflow.com/a/18335031/3470479
Prakhar1001

53

ネットワークベースの操作はメインスレッドでは実行できません。子スレッドですべてのネットワークベースのタスクを実行するか、AsyncTaskを実装する必要があります。

これは、子スレッドでタスクを実行する方法です。

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

1
匿名のRunnableは、囲んでいるクラスへの暗黙的な参照を持ち、スレッドが完了するまでGCされないようにするため、最良の方法ではありません。また、このスレッドはメイン/ USスレッドと同じ優先度で実行され、ライフサイクルメソッドやUIフレームレートと競合します。
Yousha Aleayoub 2016年

49

コードを中に入れます:

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

11番目を超えるAPIについては、2番目のものは最初のものよりも良い
Rohit Goswami

46

これは、Android 3.0以降で発生します。Android 3.0以降では、ネットワーク操作(インターネットにアクセスする機能)を使用したメインスレッド/ UIスレッド(アクティビティのon createおよびon resumeメソッドから生成されるもの)での実行が制限されています。

これは、ネットワーク操作に個別のスレッドを使用することを推奨するためです。ネットワークアクティビティを正しい方法で実行する方法の詳細については、AsyncTaskを参照してください。


46

Androidアノテーションの使用はオプションです。これにより、バックグラウンドスレッドで任意のメソッドを簡単に実行できます。

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

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

単純さと読みやすさの利点はありますが、欠点もあります。


6
@Gavrielは、メソッド、アクティビティ、フラグメント、シングルトンなど、注釈を付けるすべての複製を作成するため、コードが2倍になり、コンパイルに時間がかかります。ライブラリのバグが原因で問題が発生することもあります。エラーのデバッグと検出はさらに困難になります。
Oleksiy 2015

43

エラーは、メインスレッドで実行時間の長い操作が原因です。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
    }
});

42

ネットワーク操作、ファイルI / O、SQLiteデータベース操作など、時間のかかるメインスレッド(UIスレッド)のタスクを実行しないでください。したがって、この種の操作では、ワーカースレッドを作成する必要がありますが、問題は、ワーカースレッドからUI関連の操作を直接実行できないことです。そのためには、を使用Handlerして渡す必要がありMessageます。

これらすべてのものを簡素化するために、Androidはのような、さまざまな方法を提供しAsyncTaskAsyncTaskLoaderCursorLoaderまたはIntentService。したがって、要件に応じてこれらのいずれかを使用できます。


40

spektomのトップの答えは完璧に機能します。

AsyncTaskインラインで記述し、クラスとして拡張しない場合、さらにその上で、から応答を取得する必要がある場合は、以下AsyncTaskget()メソッドを使用できます。

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

(彼の例から)


5
使用get()は悪い考えです...それは再びAsyncTaskを「同期」にします
Selvin

それからもっと良い方法はありますか?@Selvin
sivag1

2
メインスレッドに結果について情報を提供できると思います。たとえば、結果を含むブロードキャストをメインスレッドに送信します。
Chine Gary、

32

これはHoneycomb SDK以降をターゲットとするアプリケーションでのみスローされます。以前のSDKバージョンを対象とするアプリケーションは、メインイベントループスレッドでネットワーキングを実行できます。

エラーはSDKの警告です。


28

私にとってはこれでした:

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

私がアプリをテストしたデバイスは4.1.2で、SDKバージョン16です。

ターゲットバージョンがAndroidターゲットライブラリと同じであることを確認します。ターゲットライブラリが不明な場合は、[プロジェクト]-> [ ビルドパス ] -> [ Android ]を右クリックすると、チェックマークが付いているはずです。

また、他の人が述べたように、インターネットにアクセスするための正しい権限を含めます。

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

11
ここで何をしているのかを説明しましょう:NetworkOnMainThreadExceptionガーディアンはあなたに言っています:自分の足で撃たないでください...あなたの解決策は:ガーディアンがいなかったときに過去に戻りましょう自由に足
セルビン

1
私もこのアプローチを取り、問題はありませんでした。ガーディアンは時々うるさいです。
FractalBob 2013年

25

あなたの活動でこれを使用してください

    btnsub.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            new Thread(new Runnable() {

                @Override
                public void run() {
                    // TODO Auto-generated method stub

                    //Initialize soap request + add parameters
                    SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME1);

                    //Use this to add parameters
                    request.addProperty("pincode", txtpincode.getText().toString());
                    request.addProperty("bg", bloodgroup.getSelectedItem().toString());

                    //Declare the version of the SOAP request
                    SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);

                    envelope.setOutputSoapObject(request);
                    envelope.dotNet = true;

                    try {
                        HttpTransportSE androidHttpTransport = new HttpTransportSE(URL);

                        //this is the actual part that will call the webservice
                        androidHttpTransport.call(SOAP_ACTION1, envelope);

                        // Get the SoapResult from the envelope body.
                        SoapObject result = (SoapObject) envelope.getResponse();
                        Log.e("result data", "data" + result);
                        SoapObject root = (SoapObject) result.getProperty(0);
                        // SoapObject s_deals = (SoapObject) root.getProperty(0);
                        // SoapObject s_deals_1 = (SoapObject) s_deals.getProperty(0);
                        //

                        System.out.println("********Count : " + root.getPropertyCount());

                        value = new ArrayList<Detailinfo>();

                        for (int i = 0; i < root.getPropertyCount(); i++) {
                            SoapObject s_deals = (SoapObject) root.getProperty(i);
                            Detailinfo info = new Detailinfo();

                            info.setFirstName(s_deals.getProperty("Firstname").toString());
                            info.setLastName(s_deals.getProperty("Lastname").toString());
                            info.setDOB(s_deals.getProperty("DOB").toString());
                            info.setGender(s_deals.getProperty("Gender").toString());
                            info.setAddress(s_deals.getProperty("Address").toString());
                            info.setCity(s_deals.getProperty("City").toString());
                            info.setState(s_deals.getProperty("State").toString());
                            info.setPinecode(s_deals.getProperty("Pinecode").toString());
                            info.setMobile(s_deals.getProperty("Mobile").toString());
                            info.setEmail(s_deals.getProperty("Email").toString());
                            info.setBloodgroup(s_deals.getProperty("Bloodgroup").toString());
                            info.setAdddate(s_deals.getProperty("Adddate").toString());
                            info.setWaight(s_deals.getProperty("waight").toString());
                            value.add(info);
                        }

                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    Intent intent = new Intent(getApplicationContext(), ComposeMail.class);
                    //intent.putParcelableArrayListExtra("valuesList", value);

                    startActivity(intent);
                }
            }).start();
        }
    });

23

何かを明確に説明するだけです:

メインスレッドは基本的にUIスレッドです。

つまり、メインスレッドでネットワーク操作を実行できないということは、UIスレッドでネットワーク操作を実行できないこと*runOnUiThread(new Runnable() { ... }*を意味します。つまり、他のスレッド内ブロックでネットワーク操作を実行することもできません

(メインスレッド以外の場所でエラーが発生した理由を理解しようとする長い頭を悩ます瞬間がありました。これが理由でした。このスレッドが役に立ちました。このコメントが他の誰かの役に立つことを願っています。)


22

この例外は、メインスレッドで実行される重いタスクが原因で、その実行タスクに時間がかかりすぎる場合に発生します。

これを回避するために、スレッドまたは実行プログラムを使用して処理できます。

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

18

この質問にはすでに多くのすばらしい回答がありますが、それらの回答が投稿されて以来、多くのすばらしいライブラリが出てきました。これは一種の初心者ガイドとして意図されています。

ネットワーク操作を実行するためのいくつかの使用例とそれぞれに1つまたは2つのソリューションについて説明します。

HTTP経由のReST

通常はJsonであり、XMLまたは他の何かにすることができます

完全なAPIアクセス

ユーザーが株価、金利、為替レートを追跡できるアプリを作成しているとします。次のようなJson APIを見つけます。

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)

スクエアからの改造

これは、複数のエンドポイントを持つAPIの優れた選択であり、ReSTエンドポイントを、ionやVolleyなどの他のライブラリのように個別にコーディングする代わりに宣言することができます。(ウェブサイト:http : //square.github.io/retrofit/

財務APIでそれをどのように使用しますか?

build.gradle

次の行をモジュールレベルのbuid.gradleに追加します。

implementation 'com.squareup.retrofit2:retrofit:2.3.0' //retrofit library, current as of September 21, 2017
implementation 'com.squareup.retrofit2:converter-gson:2.3.0' //gson serialization and deserialization support for retrofit, version must match retrofit version

FinancesApi.java

public interface FinancesApi {
    @GET("stocks")
    Call<ResponseWrapper<String>> listStocks();
    @GET("stocks/{symbol}")
    Call<Stock> getStock(@Path("symbol")String tickerSymbol);
    @GET("stocks/{symbol}/prices")
    Call<PriceHistory<Stock>> getPriceHistory(@Path("symbol")String tickerSymbol);

    @GET("currencies")
    Call<ResponseWrapper<String>> listCurrencies();
    @GET("currencies/{symbol}")
    Call<Currency> getCurrency(@Path("symbol")String currencySymbol);
    @GET("currencies/{symbol}/values/{compare_symbol}")
    Call<PriceHistory<Currency>> getComparativeHistory(@Path("symbol")String currency, @Path("compare_symbol")String currencyToPriceAgainst);
}

ファイナンス

public class FinancesApiBuilder {
    public static FinancesApi build(String baseUrl){
        return new Retrofit.Builder()
                    .baseUrl(baseUrl)
                    .addConverterFactory(GsonConverterFactory.create())
                    .build()
                    .create(FinancesApi.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
    }
}

APIがAPIキーまたはユーザートークンなどのその他のヘッダーの送信を必要とする場合、Retrofitはこれを簡単にします(詳細については、この素晴らしい回答を参照してください:https : //stackoverflow.com/a/42899766/1024412)。

1回限りのReST APIアクセス

ユーザーのGPS位置を調べ、そのエリアの現在の温度をチェックして気分を伝える「気分天気」アプリを作成しているとします。このタイプのアプリは、APIエンドポイントを宣言する必要はありません。1つのAPIエンドポイントにアクセスできる必要があるだけです。

イオン

これは、このタイプのアクセスに最適なライブラリです。

msysmiluの素晴らしい答えを読んでください(https://stackoverflow.com/a/28559884/1024412

HTTP経由で画像を読み込む

ボレー

VolleyはReST APIにも使用できますが、より複雑な設定が必要なため、上記のようにSquareからRetrofitを使用することを好みます(http://square.github.io/retrofit/

ソーシャルネットワーキングアプリを構築していて、友達のプロフィール写真をロードしたいとします。

build.gradle

次の行をモジュールレベルのbuid.gradleに追加します。

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

ImageFetch.java

VolleyはRetrofitよりも多くの設定が必要です。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());

ピカソ

Squareのもう1つの優れたライブラリ。いくつかの優れた例については、サイトをご覧ください:http : //square.github.io/picasso/


16

簡単に言えば、

UIスレッドでネットワーク作業を行わないでください

たとえば、HTTPリクエストを行う場合、それはネットワークアクションです。

解決:

  1. 新しいスレッドを作成する必要があります
  2. またはAsyncTaskクラスを使用します

仕方:

すべての作品を中に入れます

  1. run() 新しいスレッドのメソッド
  2. または doInBackground() AsyncTaskクラスのメソッド。

だが:

ネットワーク応答から何かを取得し、それをビューに表示したい場合(TextViewの応答メッセージの表示など)、UIスレッドに戻る必要があります。

そうしないと、が得られViewRootImpl$CalledFromWrongThreadExceptionます。

方法?

  1. AsyncTaskの使用中に、onPostExecute()メソッドからビューを更新する
  2. またはrunOnUiThread()メソッドを呼び出し、run()メソッド内のビューを更新します。

12

コードの一部を別のスレッドに移動してをオフロードし、ANRNetworkOnMainThreadExceptionIllegalStateExceptionmain threadを回避できます(たとえば、長期間UIをロックする可能性があるため、メインスレッドのデータベースにアクセスできません)。

状況に応じて選択する必要があるいくつかのアプローチがあります

Java スレッドまたはAndroid HandlerThread

Javaスレッドは1回限りの使用で、runメソッドの実行後に終了します。

HandlerThreadは、ルーパーを持つ新しいスレッドを開始するための便利なクラスです。

AsyncTask

AsyncTaskは、スレッドハンドラーのヘルパークラスになるように設計されており、汎用のスレッドフレームワークを構成しません。AsyncTasksは、理想的には短い操作(最大で数秒)に使用する必要があります。スレッドを長期間実行し続ける必要がある場合は、java.util.concurrentパッケージによって提供されるさまざまなAPIを使用することを強くお勧めします。エグゼキューターThreadPoolExecutorFutureTask

スレッドプールの実装ThreadPoolExecutorScheduledThreadPoolExecutor ...

スレッドプールを細かく制御するExecutorServiceを実装するThreadPoolExecutorクラス(コアプールサイズ、最大プールサイズ、キープアライブ時間など)

ScheduledThreadPoolExecutor-ThreadPoolExecutorを拡張するクラス。所定の遅延後または定期的にタスクをスケジュールできます。

FutureTask

FutureTaskは非同期処理を実行しますが、結果がまだ準備されていないか、処理が完了していない場合、get()を呼び出すとスレッドがブロックされます

AsyncTaskLoaders

AsyncTaskLoadersは、AsyncTaskに固有の多くの問題を解決します。

IntentService

これは、Androidでの長時間実行処理の事実上の選択です。良い例は、大きなファイルをアップロードまたはダウンロードすることです。ユーザーがアプリを終了し、これらのタスクの実行中にユーザーがアプリを使用できないようにブロックしたくない場合でも、アップロードとダウンロードが続行される場合があります。

JobScheduler

事実上、サービスを作成し、サービスをいつ実行するかの基準を指定するJobInfo.Builderを使用してジョブを作成する必要があります。

RxJava

監視可能なシーケンスを使用して、非同期およびイベントベースのプログラムを作成するためのライブラリ。

コルーチン(Kotlin)

その主な要点は、非同期コードを同期のように見えるようにすることです

詳細はこちらこちらこちらこちら


私のために働いた...私はAsyncTaskを広範囲に使用しましたが、1つのタスクが実行されているとき、別のタスクが待機します。これを解決したい。executeonexecutorで作業しています。メモリの少ないデバイスでどのように動作するかを見てみましょう。
Suraj Shingade

メソッドを見てください:asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR、params); タスクを同時に実行するには
yoAlex5 2018年

10

上記には巨大なソリューションプールがありますが、誰も言及していませんcom.koushikdutta.ionhttps : //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
    }
});

10

新規ThreadおよびAsyncTaskソリューションソリューションはすでに説明されています。

AsyncTask理想的には、短時間の操作に使用してください。正常ThreadAndroidの場合、は推奨されません。

HandlerThreadHandlerを使用した代替ソリューションをご覧ください

HandlerThread

ルーパーを持つ新しいスレッドを開始するための便利なクラス。その後、ルーパーを使用してハンドラークラスを作成できます。start()まだ呼び出さなければならないことに注意してください。

ハンドラ:

ハンドラーを使用すると、スレッドのMessageQueueに関連付けられたMessageオブジェクトとRunnableオブジェクトを送信して処理できます。各Handlerインスタンスは、単一のスレッドとそのスレッドのメッセージキューに関連付けられています。新しいハンドラーを作成すると、ハンドラーはそれを作成しているスレッドのスレッド/メッセージキューにバインドされます。その時点から、メッセージと実行可能ファイルがそのメッセージキューに配信され、メッセージから出たときに実行されます。キュー。

解決:

  1. 作成する HandlerThread

  2. コールstart()HandlerThread

  3. からHandler取得Looperして作成HanlerThread

  4. Runnableオブジェクトにネットワーク操作関連のコードを埋め込む

  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次のネットワーク運用のために破壊され、再作成されます。しかしとHandlerし、HandlerThreadアプローチ、あなたは、単一の(Runnableをタスクなど)の多くのネットワーク操作を提出することができますHandlerThread使用してHandler

8

RxAndroidこれは、この問題のもう1つの優れた代替手段であり、スレッドを作成して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()コールバックを呼び出したい


8

メインスレッドはUIスレッドであり、ユーザーインタラクションをブロックする可能性のあるメインスレッドでは操作を実行できません。これは2つの方法で解決できます。

このようにメインスレッドでタスクを強制する

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

詳細については、こちらをご覧ください:痛みのないスレッド


THX。バージョン1は、onCreateの最初のアクションとして追加するときに役立ちます。
Ingo

7

これは機能します。ルイジ博士の答えを少し単純にしました。

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

7

Androidでは、メインスレッドでネットワーク操作を実行できません。Thread、AsyncTask(短時間実行タスク)、Service(長時間実行タスク)を使用して、ネットワーク操作を実行できます。


7

メイン(UI)スレッドからネットワークリソースにアクセスすると、この例外が発生します。この問題を回避するには、ネットワークリソースへのアクセスに別のスレッドまたはAsyncTaskを使用します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.