'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で非推奨になりました。
https://developer.android.com/reference/android/os/AsyncTask
この例外は、アプリケーションがメインスレッドでネットワーク操作を実行しようとしたときにスローされます。でコードを実行します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);
あなたのクラスでは、
そして
Androidのmanifest.xmlファイルにこの権限を追加します。
<uses-permission android:name="android.permission.INTERNET"/>
結果:
アプリは(インターネット接続が不安定な領域では)応答しなくなり、ロックされ、ユーザーは速度の低下を認識して強制的に強制終了する必要があります。また、アクティビティマネージャーがアプリを強制終了し、アプリが停止したことをユーザーに通知するリスクがあります。
Androidには、応答性を考慮して設計するための優れたプログラミング手法に関するいくつかの優れたヒントがあります。 http://developer.android.com/reference/android/os/NetworkOnMainThreadException.html
新しいを使用してこの問題を解決しました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オブジェクト、そのコンテキスト、およびそのアクティビティによって作成されたビュー階層全体への暗黙の参照があります。この参照は、AsyncTaskのバックグラウンド作業が完了するまで、アクティビティがガベージコレクションされるのを防ぎます。ユーザーの接続が遅い場合やダウンロードが大きい場合、これらの短期メモリリークが問題になる可能性があります。たとえば、向きが数回変わる場合(および実行中のタスクをキャンセルしない場合)、またはユーザーアクティビティから移動します。
- AsyncTaskには、実行するプラットフォームに応じて異なる実行特性があります。APIレベル4より前では、AsyncTaskは単一のバックグラウンドスレッドでシリアルに実行されます。APIレベル4からAPIレベル10まで、AsyncTasksは最大128スレッドのプールで実行されます。APIレベル11以降、AsyncTaskは単一のバックグラウンドスレッドでシリアルに実行されます(オーバーロードされた
executeOnExecutor
メソッドを使用して代替エグゼキューターを指定しない限り)。ICSでシリアルに実行するときに正常に機能するコードは、Gingerbreadで同時に実行すると破損する可能性があります。たとえば、不注意な実行順序の依存関係がある場合などです。
短期的なメモリリークを回避し、すべてのプラットフォームで明確に定義された実行特性を持ち、非常に堅牢なネットワーク処理を構築するための基盤が必要な場合は、次のことを検討してください。
- あなたのためにこれの素晴らしい仕事をするライブラリを使用する-この質問のネットワークライブラリの素晴らしい比較があります、または
Service
またはのIntentService
代わりに、またはを使用しPendingIntent
て、アクティビティのonActivityResult
メソッドを介して結果を返します。
IntentServiceアプローチ
欠点:
AsyncTask
思ったほどではありませんが、コードと複雑さは- リクエストをキューに入れ、単一のバックグラウンドスレッドで実行します。おそらくこのような
IntentService
同等のService
実装に置き換えることで、これを簡単に制御できます。 - ええと、私は今実際に他の人のことを考えることができません
利点:
- 短期記憶リークの問題を回避します
- ネットワーク操作の実行中にアクティビティが再開された場合でも、その
onActivityResult
方法でダウンロードの結果を受け取ることができます。 - 堅牢なネットワークコードを構築して再利用するには、AsyncTaskよりも優れたプラットフォームです。例:あなたは、重要なアップロードを行う必要がある場合、あなたはからそれを行うこと
AsyncTask
でActivity
はなく、ユーザーの電話を取るためにAPPのうちコンテキストが-切り替えた場合、システムがあり、アップロードが完了する前にアプリを殺します。がアクティブなアプリケーションを強制終了する可能性は低くなりますService
。 - 独自の同時バージョン
IntentService
(上記でリンクしたものなど)を使用する場合は、を介して同時実行のレベルを制御できますExecutor
。
実装の概要
を実装IntentService
して、単一のバックグラウンドスレッドでダウンロードを非常に簡単に実行できます。
ステップ1:を作成しIntentService
てダウンロードを実行します。Intent
エクストラを介して何をダウンロードするかを指示し、それを渡して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プロジェクトはこちらから入手できます。
HoneycombのUIスレッドでネットワークI / Oを実行することはできません。技術的には、以前のバージョンのAndroidでも可能ですが、アプリの応答が停止し、OSがアプリの動作が悪いためにアプリを強制終了する可能性があるため、これは非常に悪い考えです。バックグラウンドプロセスを実行するか、AsyncTaskを使用してバックグラウンドスレッドでネットワークトランザクションを実行する必要があります。
Android開発者サイトにPainlessThreadingに関する記事があります。これは、これについての優れた入門書であり、ここで実際に提供できるよりもはるかに深い答えを提供します。
別のスレッドでネットワークアクションを実行する
例えば:
new Thread(new Runnable(){
@Override
public void run() {
// Do network action in this function
}
}).start();
そしてこれをAndroidManifest.xmlに追加します
<uses-permission android:name="android.permission.INTERNET"/>
- strictModeを使用しないでください(デバッグモードでのみ)
- SDKのバージョンを変更しないでください
- 別のスレッドを使用しないでください
ServiceまたはAsyncTaskを使用する
StackOverflowの質問も参照してください。
android.os.NetworkOnMainThreadExceptionAndroidからメールを送信する
次のコードを使用して、厳密モードを無効にします。
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
}
}
これはAndroid3.0以降で発生します。Android 3.0以降では、ネットワーク操作(インターネットにアクセスする関数)の使用がメインスレッド/ UIスレッド(アクティビティの作成時および再開時のメソッドから生成されるもの)での実行が制限されています。
これは、ネットワーク操作に個別のスレッドを使用することを推奨するためです。ネットワークアクティビティを正しい方法で実行する方法の詳細については、AsyncTaskを参照してください。
Androidアノテーションの使用はオプションです。これにより、バックグラウンドスレッドで任意のメソッドを簡単に実行できます。
// normal method
private void normal() {
doSomething(); // do something in background
}
@Background
protected void doSomething()
// run your networking code here
}
シンプルさと読みやすさの利点はありますが、欠点があることに注意してください。
このエラーは、メインスレッドで長時間実行されている操作を実行していることが原因です。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
}
});
ネットワーク操作、ファイルI / O、SQLiteデータベース操作など、メインスレッド(UIスレッド)で時間のかかるタスクを実行しないでください。したがって、この種の操作では、ワーカースレッドを作成する必要がありますが、問題は、ワーカースレッドからUI関連の操作を直接実行できないことです。そのためには、を使用Handler
して渡す必要がありMessage
ます。
これらすべてのものを簡素化するために、Androidはのような、さまざまな方法を提供しAsyncTask
、AsyncTaskLoader
、CursorLoader
またはIntentService
。したがって、要件に応じてこれらのいずれかを使用できます。
spektomのトップアンサーは完璧に機能します。
AsyncTask
インラインを記述していて、クラスとして拡張していない場合、さらにAsyncTask
、から応答を取得する必要がある場合は、get()
次のメソッドを使用できます。
RSSFeed feed = new RetreiveFeedTask().execute(urlToRssFeed).get();
(彼の例から。)
これは、HoneycombSDK以降を対象とするアプリケーションに対してのみスローされます。以前のSDKバージョンを対象とするアプリケーションは、メインイベントループスレッドでネットワーキングを行うことができます。
エラーはSDKの警告です!
私にとってそれはこれでした:
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="10" />
私がアプリをテストしていたデバイスは、SDKバージョン16である4.1.2でした!
ターゲットバージョンがAndroidターゲットライブラリと同じであることを確認してください。ターゲットライブラリが何かわからない場合は、プロジェクト->ビルドパス-> Androidを右クリックすると、チェックマークが付いているはずです。
また、他の人が言及しているように、インターネットにアクセスするための正しいアクセス許可を含めます。
<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.
}
});
この質問にはすでに多くの素晴らしい答えがありますが、それらの答えが投稿されて以来、多くの素晴らしい図書館が出てきました。これは一種の初心者ガイドとして意図されています。
ネットワーク操作を実行するためのいくつかのユースケースと、それぞれに1つか2つのソリューションについて説明します。
HTTPを介したReST
通常、JsonはXMLまたはその他のものにすることができます
フルAPIアクセス
ユーザーが株価、金利、為替レートを追跡できるアプリを作成しているとします。次のようなJsonAPIが見つかります。
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に最適であり、ionやVolleyなどの他のライブラリのように個別にコーディングする代わりに、ReSTエンドポイントを宣言できます。(ウェブサイト: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://stackoverflow.com/a/42899766/1024412)。
1回限りのReSTAPIアクセス
ユーザーのGPS位置を検索し、そのエリアの現在の気温をチェックして気分を伝える「気分天気」アプリを作成しているとします。このタイプのアプリは、APIエンドポイントを宣言する必要はありません。1つのAPIエンドポイントにアクセスできる必要があります。
イオン
これは、このタイプのアクセスに最適なライブラリです。
msysmiluの素晴らしい答えを読んでください(https://stackoverflow.com/a/28559884/1024412)
HTTP経由で画像を読み込む
ボレー
VolleyはReSTAPIにも使用できますが、セットアップがより複雑になるため、上記のようにSquareのRetrofitを使用することをお勧めします(http://square.github.io/retrofit/)
ソーシャルネットワーキングアプリを作成していて、友達のプロフィール写真を読み込みたいとします。
build.gradle
次の行をモジュールレベルのbuid.gradleに追加します。
implementation 'com.android.volley:volley:1.0.0'
ImageFetch.java
ボレーはレトロフィットよりも多くのセットアップが必要です。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/
簡単に言えば、
UIスレッドでネットワーク作業を行わないでください
たとえば、HTTPリクエストを実行する場合、それはネットワークアクションです。
解決:
- 新しいスレッドを作成する必要があります
- または、AsyncTaskクラスを使用します
仕方:
すべての作品を中に入れてください
run()
新しいスレッドの方法- または
doInBackground()
AsyncTaskクラスのメソッド。
だが:
ネットワーク応答から何かを取得し、それをビューに表示したい場合(TextViewでの応答メッセージの表示など)、UIスレッドに戻る必要があります。
あなたがそれをしなければ、あなたは得るでしょうViewRootImpl$CalledFromWrongThreadException
。
方法?
- AsyncTaskの使用中に、
onPostExecute()
メソッドからビューを更新します - または、runOnUiThread()メソッドを呼び出して、
run()
メソッド内のビューを更新します。
あなたはオフロードするために別のスレッドにあなたのコードの一部を移動することができるmain thread
と避けるANR、NetworkOnMainThreadException、IllegalStateExceptionが(例えば缶、それは潜在的に長時間のUIをロックする可能性があるため、メインスレッドではないアクセスデータベース)。
状況に応じて選択する必要があるいくつかのアプローチがあります
JavaスレッドまたはAndroidHandlerThread
Javaスレッドは1回限りの使用であり、runメソッドの実行後に終了します。
HandlerThreadは、ルーパーを持つ新しいスレッドを開始するための便利なクラスです。
AsyncTask
AsyncTaskは、スレッドとハンドラーの周りのヘルパークラスになるように設計されており、一般的なスレッドフレームワークを構成しません。AsyncTasksは、理想的には短い操作(最大で数秒)に使用する必要があります。スレッドを長期間実行し続ける必要がある場合は、java.util.concurrentパッケージによって提供されるさまざまなAPIを使用することを強くお勧めします。Executor、ThreadPoolExecutorおよびFutureTask。
メインスレッドはUIコンポーネントを独占しているため、一部のビューにアクセスすることはできません。そのため、ハンドラーが助けになっています。
スレッドプールの実装ThreadPoolExecutor、ScheduledThreadPoolExecutor .. ..
スレッドプールを細かく制御するExecutorServiceを実装するThreadPoolExecutorクラス(コアプールサイズ、最大プールサイズ、キープアライブ時間など)
ScheduledThreadPoolExecutor-ThreadPoolExecutorを拡張するクラス。所定の遅延後または定期的にタスクをスケジュールできます。
FutureTask
FutureTaskは非同期処理を実行しますが、結果の準備ができていないか、処理が完了していない場合、get()を呼び出すとスレッドがブロックされます
AsyncTaskLoaders
AsyncTaskLoadersは、AsyncTaskに固有の多くの問題を解決します。
IntentService
これは、Androidで長時間実行される処理の事実上の選択です。良い例は、大きなファイルをアップロードまたはダウンロードすることです。ユーザーがアプリを終了しても、アップロードとダウンロードが続行される場合があります。これらのタスクの実行中にユーザーがアプリを使用できないようにする必要はありません。
JobScheduler
事実上、サービスを作成し、いつサービスを実行するかの基準を指定するJobInfo.Builderを使用してジョブを作成する必要があります。
RxJava
監視可能なシーケンスを使用して非同期およびイベントベースのプログラムを作成するためのライブラリ。
コルーチン(Kotlin)
その主な要点は、非同期コードを同期のように見せることです
続きを読むここ、ここ、ここ、ここ
新しいソリューションThread
とAsyncTaskソリューションについてはすでに説明しました。
AsyncTask
理想的には短い操作に使用する必要があります。通常Thread
はAndroidには適していません。
HandlerThreadとHandlerを使用した代替ソリューションをご覧ください
HandlerThread
ルーパーを持つ新しいスレッドを開始するための便利なクラス。その後、ルーパーを使用してハンドラークラスを作成できます。
start()
まだ呼び出す必要があることに注意してください。
ハンドラ:
ハンドラーを使用すると、スレッドのMessageQueueに関連付けられたMessageオブジェクトとRunnableオブジェクトを送信および処理できます。各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
。
上記には巨大なソリューションプールがありますが、誰も言及していません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()スケジューラーのサポートが含まれます。
rx-java用の優れた電子ブックがあります。
RxAndroid
はこの問題のもう1つの優れた代替手段であり、スレッドを作成して結果をAndroidUIスレッドに投稿する手間を省きます。タスクを実行する必要があるスレッドを指定するだけで、すべてが内部で処理されます。
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スレッドでこのObservableを監視する必要があります。つまり、UIスレッドでonNext()
コールバックを呼び出す必要があります。
メインスレッドは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);
詳細については、これをチェックしてください:痛みのないスレッド
これは機能します。ルイジ博士の答えをもう少し簡単にしました。
new Thread() {
@Override
public void run() {
try {
//Your code goes here
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
Androidでは、ネットワーク操作をメインスレッドで実行することはできません。Thread、AsyncTask(短時間実行タスク)、Service(長時間実行タスク)を使用して、ネットワーク操作を実行できます。