java - networkonmainthreadexception 回避
android.os.NetworkOnMainThreadExceptionを修正するにはどうすればよいですか? (20)
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
この問題を解決するにはどうすればよいですか?
- strictModeを使用しないでください(デバッグモードでのみ)
- SDKのバージョンを変更しないでください
- 別のスレッドを使用しないでください
サービスまたはAsyncTaskを使用する
関連項目に関する質問:
Honeycomb UIスレッドでネットワークI/Oを実行することはできません。 技術的には 、Androidの以前のバージョンでは可能ですが、アプリの応答が停止し、OSが誤って動作しているためにアプリが強制終了することがあります。 バックグラウンドプロセスを実行するか、AsyncTaskを使用してバックグラウンドスレッドでネットワークトランザクションを実行する必要があります。
Androidの開発者サイトには、 Painless Threadingに関する記事があります。これは、これについてよく紹介されており、ここで現実的に提供できるよりもはるかに深い答えが得られます。
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);
}
});
指定すると
(Schedulers.io())
、RxAndroidはgetFavoriteMusicShows()
別のスレッドで実行されます。使用することにより
AndroidSchedulers.mainThread()
、我々は、すなわち私たちが望む、UIスレッド上で、この観測を観察したいonNext()
コールバックはUIスレッドで呼び出されます
あなたのコードを内部に入れてください:
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
}
}
このエラーは、メインスレッドで長時間実行されている操作が原因で発生しますAsynTaskまたはThreadを使用して問題を簡単に修正できます。 このライブラリAsyncHTTPClientをチェックアウトして、より適切な処理を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
}
});
この例外は、タスクの実行に時間がかかりすぎると、メインスレッドで重いタスクが実行されたために発生します。
これを避けるには、 スレッドやexecutersを使用して処理できます
Executors.newSingleThreadExecutor().submit(new Runnable() {
@Override
public void run() {
// You can perform your task here.
}
});
この質問にはすでに多くの素晴らしい答えがありますが、それらの回答が投稿されて以来、多くの素晴らしい図書館が出てきました。 これは初心者ガイドの一種として意図されています。
私は、ネットワークオペレーションを実行するためのいくつかのユースケースと、それぞれの解決策をカバーします。
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エンドポイントをイオンやVolleyなどの他のライブラリと同様に個別にコーディングするのではなく、宣言することができます。 (ウェブサイト: http://square.github.io/retrofit/ : 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);
}
FinancesApiBuilder
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://.com/a/42899766/1024412 : https://.com/a/42899766/1024412 )。
1つのReST APIへのアクセス
ユーザーのGPSの場所を検索し、その地域の現在の気温をチェックし、気分を伝える「気分天気」アプリを構築しているとしましょう。 このタイプのアプリでは、APIエンドポイントを宣言する必要はありません。 1つのAPIエンドポイントにアクセスできるだけで済みます。
イオン
これは、このタイプのアクセスに最適なライブラリです。
msysmiluの偉大な答えをお読みください( https://.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());
ピカソ
スクエアの別の優れた図書館 いくつかの素晴らしい例はサイトをご覧ください:http://square.github.io/picasso/ : http://square.github.io/picasso/
これは、 Honeycomb SDK以上をターゲットとするアプリケーションでのみスローされます。 以前のSDKバージョンを対象としたアプリケーションでは、メインイベントループスレッドでネットワーキングを行うことができます。
http://developer.android.com/reference/android/os/NetworkOnMainThreadException.html
これは機能します。 ちょうどDr.Luijiの答えを少しシンプルにしました。
new Thread() {
@Override
public void run() {
try {
//Your code goes here
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
メインスレッド(UIスレッド)では、ネットワーク操作、ファイルI / O、SQLiteデータベース操作など、時間のかかる作業を行うべきではありません。 このような操作の場合、ワーカースレッドを作成する必要がありますが、ワーカースレッドからUI関連の操作を直接実行できないという問題があります。 そのためには、 Handler
を使用してMessage
を渡す必要があります。
これらすべてを単純化するために、AndroidはAsyncTask
、 AsyncTaskLoader
、 CursorLoader
、 AsyncTaskLoader
などのさまざまな方法を提供しています。 したがって、これらのいずれかを要件に応じて使用できます。
別のスレッドでネットワークアクションを実行する
例えば:
new Thread(new Runnable(){
@Override
public void run() {
// Do network action in this function
}
}).start();
これをAndroidManifest.xmlに追加します
<uses-permission android:name="android.permission.INTERNET"/>
明示的に何かを明記するだけです:
メインスレッドは基本的にUIスレッドです。
つまり、メインスレッドでネットワーキング操作を行うことができないということは、UIスレッドでネットワーキング操作を行うことができないということです 。つまり、他のスレッド内の*runOnUiThread(new Runnable() { ... }*
ブロック*runOnUiThread(new Runnable() { ... }*
、どちらか。
(私はちょうど、なぜ私がメインスレッド以外のどこかでそのエラーを取得しているのを理解しようとしている間に、長い頭を抱いている瞬間を持っていました。
次のコードを使用してstrictモードを無効にします。
if (android.os.Build.VERSION.SDK_INT > 9) {
StrictMode.ThreadPolicy policy =
new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
}
これはお勧めしません AsyncTask
インターフェースを使用してAsyncTask
。
私はこの問題を新しいThread
を使って解決しました。
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
//Your code goes here
} catch (Exception e) {
e.printStackTrace();
}
}
});
thread.start();
簡単に言えば、
UIのスレッドでネットワーク作業をしないでください
たとえば、HTTP要求を行う場合、それはネットワークアクションです。
溶液:
- 新しいスレッドを作成する必要があります
- または AsyncTaskクラスを使用する
方法:
すべての作品を内部に入れて
- 新しいスレッドの
run()
メソッド - AsyncTaskクラスの
doInBackground()
メソッド。
しかし:
ネットワークレスポンスから何かを得て、それをあなたのビューに表示したい場合(TextViewの表示応答メッセージのように)、 UIスレッドに戻る必要があります。
それをしないと、 ViewRootImpl$CalledFromWrongThreadException
ます。
の仕方?
- AsyncTaskを使用しているときに、
onPostExecute()
メソッドのビューを更新する -
runOnUiThread()
メソッドを呼び出し、run()
メソッド内のビューを更新します。
AndroidのUIスレッドでネットワーク操作を実装することは許可されていません。AsyncTaskクラスを使用して、APIリクエストの送信、URLからのイメージのダウンロード、AsyncTaskのコールバックメソッドの使用などのネットワーク関連の操作を実行する必要があります。その結果、onPostExecuteメソッドが実行され、UIスレッドになりますWebサービスなどのデータをUIに取り込むことができます。
例:URLから画像をダウンロードするとしますhttps://www.samplewebsite.com/sampleimage.jpg : 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マニフェストファイルにインターネット権限を追加することを忘れないでください。それは魅力のように動作します。 :)
New Thread
およびAsynTaskソリューションはすでに説明されています。
AsyncTask
理想的には短い操作に使用する必要があります。Thread
Androidではノーマルは好ましくありません。
HandlerThreadとHandlerを使用して代替ソリューションを見てみましょうHandler
HandlerThread
ルーパーを持つ新しいスレッドを開始するためのハンディクラス。ルーパーを使用してハンドラクラスを作成することができます。
start()
それでも呼び出さなければならないことに注意してください。
ハンドラ:
ハンドラを使用すると、スレッドのMessageQueueに関連付けられたMessageおよびRunnableオブジェクトを送信および処理できます。各Handlerインスタンスは、単一のスレッドとそのスレッドのメッセージキューに関連付けられています。新しいHandlerを作成すると、それを作成しているスレッドのスレッド/メッセージキューにバインドされます。その時点から、そのメッセージキューにメッセージと実行可能ファイルを配信し、メッセージから出てくるように実行しますキュー。
溶液:
作成する
HandlerThread
電話
start()
するHandlerThread
から
Handler
取得Looper
して作成HanlerThread
ネットワーク操作関連のコードを
Runnable
オブジェクトに埋め込む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);
このアプローチを使用する利点:
Thread/AsyncTask
各ネットワーク操作のための新しい作成は高価です。Thread/AsyncTask
次のネットワーク運用のために破壊され、再作成されます。しかしとHandler
し、HandlerThread
アプローチ、あなたは、単一の多くのネットワーク操作(などのRunnableタスク)を提出することができますHandlerThread
使用してHandler
。
メイン(UI)スレッドからネットワークリソースにアクセスすると、この例外が発生します。この問題を回避するには、ネットワークリソースにアクセスするために別のスレッドまたはAsyncTaskを使用します。
上には巨大なソリューションプールがありますが、誰も言及していませんcom.koushikdutta.ion
:https://github.com/koush/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
}
});