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
でコードを実行します。
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();
受け入れられた答えには、いくつかの重大な欠点があります。 reallyあなたが何をしているかを知らない限り、ネットワークにAsyncTaskを使用することはお勧めできません。いくつかの欠点は次のとおりです。
executeOnExecutor
メソッドを使用し、代替エグゼキューターを提供しない限り)。 ICSで連続して実行すると正常に動作するコードは、不注意な実行順序の依存関係がある場合など、Gingerbreadで同時に実行すると破損する可能性があります。短期的なメモリリークを回避し、すべてのプラットフォームで実行特性を明確に定義し、非常に堅牢なネットワーク処理を構築するための基盤がある場合は、次のことを検討してください。
Service
またはIntentService
を使用し、おそらくPendingIntent
を使用して、アクティビティのonActivityResult
メソッドを介して結果を返します。欠点:
AsyncTask
よりも多くのコードと複雑さ、ただしあなたが思うほどではないIntentService
を同等のService
実装に置き換えることで、これを簡単に制御できます。アップサイド:
onActivityResult
メソッドを介してダウンロードの結果を受け取ることができますAsyncTask
内のActivity
から行うことができますが、ユーザーがアプリからコンテキスト切り替えて電話をかけると、システムmayアップロードが完了する前にアプリを強制終了します。アクティブなService
を持つアプリケーションを強制終了する可能性は少ない。IntentService
(上記でリンクしたバージョン)を使用する場合、Executor
を使用して同時実行のレベルを制御できます。IntentService
を実装して、単一のバックグラウンドスレッドでダウンロードを非常に簡単に実行できます。
ステップ1:ダウンロードを実行するIntentService
を作成します。 Intent
extra'sを介してダウンロードするものを指定し、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プロジェクトが利用可能です こちら 。
ハニカム のUIスレッドでネットワーク I/O を実行することはできません。技術的には、以前のバージョンのAndroidでは is が可能ですが、アプリが応答しなくなるため、OSがアプリの動作を悪くして殺害する可能性があるため、本当に悪い考えです。バックグラウンドスレッドでネットワークトランザクションを実行するには、バックグラウンドプロセスを実行するか、AsyncTaskを使用する必要があります。
Painless Threading についての記事がAndroidの開発者サイトにあります。これはこれを紹介するもので、実際に提供されるよりもはるかに優れた回答の深さを提供します。
サービスまたはAsyncTaskを使用
スタックオーバーフローの質問も参照してください。
別のスレッドでネットワークアクションを実行する
例えば:
new Thread(new Runnable(){
@Override
public void run() {
// Do network action in this function
}
}).start();
そしてこれをAndroidManifest.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);
}
これはお勧めできません。 :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 Annotations を使用することはオプションです。それはあなたが単にバックグラウンドスレッドで任意のメソッドを実行することを可能にします:
// normal method
private void normal() {
doSomething(); // do something in background
}
@Background
protected void doSomething()
// run your networking code here
}
それは単純さと読みやすさの利点を提供しますが、それには欠点があります。
これはAndroid 3.0以降で起こります。 Android 3.0以降では、ネットワーク操作(インターネットにアクセスする機能)をメインスレッド/ UIスレッド(アクティビティのcreateメソッドおよびresumeメソッドから生成されるもの)で実行することを制限しました。
これは、ネットワーク操作に別々のスレッドを使用することを推奨するためです。ネットワークアクティビティを正しい方法で実行する方法の詳細については、 AsyncTask を参照してください。
ネットワーク操作、ファイルI/O、またはSQLiteデータベース操作のように、メインスレッド(UIスレッド)に対して時間のかかるタスクを実行しないでください。したがって、この種の操作にはワーカースレッドを作成する必要がありますが、問題は、ワーカースレッドから直接UI関連の操作を実行できないことです。そのためには、Handler
を使用してMessage
を渡す必要があります。
これらすべてを単純化するために、AndroidにはAsyncTask
、AsyncTaskLoader
、CursorLoader
、IntentService
などのさまざまな方法が用意されています。だからあなたはあなたの要求に従ってこれらのどれでも使うことができる。
このエラーは、メインスレッドで長時間実行されている操作を実行することに起因します。 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
}
});
spektomの答え は完璧に動作します。
AsyncTask
をインラインで記述していてクラスとして拡張していない場合、さらにその上でAsyncTask
から応答を取得する必要がある場合は、以下のようにget()
メソッドを使用できます。
RSSFeed feed = new RetreiveFeedTask().execute(urlToRssFeed).get();
(彼の例から)
これは、 ハニカム SDK以上をターゲットとするアプリケーションに対してのみスローされます。以前のバージョンのSDKをターゲットとするアプリケーションは、メインのイベントループスレッドでネットワーキングを行うことが許可されています。
私にとってはこれでした:
<uses-sdk
Android:minSdkVersion="8"
Android:targetSdkVersion="10" />
私のアプリをテストしていたデバイスは4.1.2で、これはSDKバージョン16です。
ターゲットのバージョンがAndroidのターゲットライブラリと同じであることを確認してください。ターゲットライブラリがわからない場合は、プロジェクト - > ビルドパス - > Android を右クリックしてください。
また、他の人が言っているように、インターネットにアクセスするための正しい許可を含めてください。
<uses-permission Android:name="Android.permission.INTERNET"/>
何かを明示的に綴るために:
メインスレッドは基本的にUIスレッドです。
つまり、メインスレッドでネットワーク操作を実行できないということは、UIスレッドでネットワーク操作を実行できないことを意味します。つまり、 他のスレッドの内側の*runOnUiThread(new Runnable() { ... }*
ブロック でもネットワーク操作を実行できません。
(私のメインスレッド以外のどこかでこのエラーが発生している理由を突き止めようとしていたのは、長い間頭が痛い瞬間でした。このスレッドが助けになりました。
この例外は、実行中のタスクが長すぎる時間を要する場合、メインスレッド上で実行された重いタスクによって発生します。
これを避けるために、 threads または executors を使用して処理できます。
Executors.newSingleThreadExecutor().submit(new Runnable() {
@Override
public void run() {
// You can perform your task here.
}
});
この質問にはすでに多くの素晴らしい回答がありますが、それらの回答が投稿されてから多くの素晴らしいライブラリが出てきました。これは一種の初心者ガイドとして意図されています。
ネットワーク操作とaソリューションをそれぞれ実行するためのいくつかのユースケースをカバーします。
典型的にはJson、XMLまたは他の何かにすることができます
ユーザーが株価、金利、為替レートを追跡できるアプリを書いているとしましょう。次のような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にとって優れた選択肢であり、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 )。
ユーザーのGPS位置を検索し、そのエリアの現在の温度をチェックして気分を伝える「気分天気」アプリを構築しているとしましょう。このタイプのアプリでは、APIエンドポイントを宣言する必要はありません。 1つのAPIエンドポイントにアクセスできる必要があります。
これは、このタイプのアクセスに最適なライブラリです。
Msysmiluのすばらしい答えを読んでください( https://stackoverflow.com/a/28559884/1024412 )
VolleyはReST APIにも使用できますが、より複雑なセットアップが必要なため、上記のように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メソッド(フラグメント、アクティビティ)またはコンストラクター(ダイアログ)に追加します。
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/
あなたの活動にこれを使ってください
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スレッドでネットワーク作業をしない
たとえば、HTTPリクエストを実行した場合、それはネットワークアクションです。
解決策:
方法:
すべての作品を中に入れる
run()
メソッドdoInBackground()
メソッド。しかし、:
あなたがネットワークレスポンスから何かを得て、それをあなたのビューに表示したいとき(TextViewのレスポンスメッセージの表示のように)、あなたは UIに戻る必要があります thread。
そうしなければ、ViewRootImpl$CalledFromWrongThreadException
が手に入ります。
どうやって?
onPostExecute()
メソッドからビューを更新します。runOnUiThread()
methodを実行し、run()
メソッド内でビューを更新します。上記には巨大なソリューションプールがありますが、誰も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
}
});
新しいThread
と AsyncTask のソリューションはすでに説明されています。
AsyncTask
は理想的には短い操作に使われるべきです。通常のThread
はAndroidには向いていません。
HandlerThread および Handler を使用して別の解決策を見てください。
HandlerThread
ルーパーを持つ新しいスレッドを開始するための便利なクラス。その後、ルーパーを使用してハンドラクラスを作成できます。
start()
はまだ呼ばれなければならないことに注意してください。
ハンドラ:
Handlerを使用すると、スレッドのMessageQueueに関連付けられているMessageオブジェクトとRunnableオブジェクトを送信して処理できます。各Handlerインスタンスは、単一のスレッドとそのスレッドのメッセージキューに関連付けられています。新しいHandlerを作成すると、それはそれを作成しているスレッドのスレッド/メッセージキューにバインドされます。それ以降、メッセージとランナブルはそのメッセージキューに配信され、メッセージから出てきたら実行しますキュー。
溶液:
HandlerThread
を作成する
HandlerThread
に対してstart()
を呼び出す
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
のアプローチでは、HandlerThread
を使用することで、(Runnableタスクとして)多数のネットワーク操作を単一のHandler
に送信できます。 メイン(UI)スレッドからネットワークリソースにアクセスすると、この例外が発生します。この問題を回避するには、ネットワークリソースへのアクセスに別のスレッドまたはAsyncTaskを使用してください。
Androidでは、ネットワーク操作をメインスレッドで実行することはできません。スレッド、AsyncTask(短時間実行タスク)、Service(長時間実行タスク)を使用してネットワーク操作を実行できます。
これはうまくいきます。ルイジ博士の答えをもう少し簡単にしたところです。
new Thread() {
@Override
public void run() {
try {
//Your code goes here
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
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);
}
});
(Schedulers.io())
を指定することによって、RxAndroidは別のスレッドでgetFavoriteMusicShows()
を実行します。
AndroidSchedulers.mainThread()
を使用することで、UIスレッドでこのObservableを観察したい、すなわち、onNext()
コールバックをUIスレッドで呼び出すようにします。
コードの一部を別のスレッドに移動してmain thread
をオフロードし、 _ anr _ 、 NetworkOnMainThreadException 、 IllegalStateException を回避することができます。長期間UIをロックする可能性があります。
状況に応じて選択すべきアプローチがいくつかあります。
Java スレッド Android HandlerThread
Javaスレッドは1回限りの使用のみで、そのrunメソッドの実行後に停止します。
HandlerThreadは、ルーパーを持つ新しいスレッドを開始するための便利なクラスです。
AsyncTask は、 Thread および Handler の周りのヘルパークラスになるように設計されており、一般的なスレッド化フレームワークを構成するものではありません。 AsyncTasksは理想的には短い操作(せいぜい数秒)のために使用されるべきです。スレッドを長期間実行し続ける必要があるならば、Java.util.concurrentパッケージによって提供されるような様々なAPIを使うことが強く推奨されます。 エグゼキュータ 、 ThreadPoolExecutor および FutureTask 。
スレッドプールの実装 ThreadPoolExecutor 、 ScheduledThreadPoolExecutor ...
スレッドプールを細かく制御するExecutorServiceを実装するThreadPoolExecutorクラス(例:コアプールサイズ、最大プールサイズ、キープアライブタイムなど)
ScheduledThreadPoolExecutor - ThreadPoolExecutorを拡張するクラスです。一定の遅延後または定期的にタスクをスケジュールできます。
FutureTaskは非同期処理を実行しますが、結果がまだ準備できていない、または処理が完了していない場合、get()を呼び出すとスレッドがブロックされます。
AsyncTaskLoaderはAsyncTaskに固有の多くの問題を解決します。
これは、Android上で長時間実行される処理のための事実上の選択です。その良い例は、大きなファイルをアップロードまたはダウンロードすることです。ユーザーがアプリを終了しても、アップロードとダウンロードが続行される可能性があり、これらのタスクが進行している間は、ユーザーがアプリを使用できないようにしたくない場合があります。
事実上、サービスを作成し、サービスをいつ実行するかの基準を指定するJobInfo.Builderを使用してジョブを作成する必要があります。
観測可能なシーケンスを使用して非同期およびイベントベースのプログラムを作成するためのライブラリ。
コルーチン (Kotlin)
その主な要点は、非同期コードを同期コードのように見せることです。
もっと読む
Androidで非同期処理を行いカウントする8つの方法
Androidネットワークアクセスの進化
Androidでスレッドプールを使用する
Android上のUIスレッドにネットワーク操作を実装することは許可されていません。 AsyncTaskクラスを使用して、APIリクエストの送信、URLからの画像のダウンロードなどのネットワーク関連の操作を実行する必要があります。AsyncTaskのコールバックメソッドを使用すると、onPostExecuteメソッドになり、UIスレッドに入ります。 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マニフェストファイルにインターネット権限を追加することを忘れないでください。それは魅力のように働くでしょう。 :)
この問題に取り組むためのもう一つの非常に便利な方法があります - 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用の優れた電子ブック があります。
KOTLIN
とANKO
のどちらでも使用できます。
KotlinはAndroidの新しい公用語です。詳細はこちら https://kotlinlang.org/docs/tutorials/kotlin-Android.html
Anko AndroidでKotlin用のライブラリをサポートしました。いくつかのドキュメントはこちら https://github.com/Kotlin/anko
本当に便利で@AntonioLeiva https://antonioleiva.com/anko-background-kotlin-Android/ によって書かれた数行のコードしか持っていない解決策
doAsync {
var result = runLongTask()
uiThread {
toast(result)
}
}
それは単純です、NetworkOnMainThread
はUI Thread
でバックグラウンドジョブを実行したときに発生するので、あなたがしなければならないことの1つはバックグラウンドでlongTask job
を実行することです。あなたはあなたのAndroidアプリでAnkoでこのメソッドとKotlinを使ってこれを行うことができます。
私はこの問題を簡単な方法で解決しました...
oncreate
StrictMode.enableDefaults();
の後に追加してこれを解決しました。
または
これを解決するにはService
またはAsyncTask
を使用してください。
注意:
Do not change SDK version
Do not use a separate thread
詳しくは これをチェックしてください 。
この例外は、アプリケーションがメインスレッド上でネットワーク操作を実行しようとしたときにスローされます。
コードを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
}
}
メインスレッドでネットワーク操作が実行されると、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();
}
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のためのリンクがあります。お気軽にお問い合わせください。
メインスレッドは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);
詳しくはこちらをご覧ください。Painless threading
以下のコードを使用して厳密モードを使用してこの問題を解決することもできます。これはこの問題を解決するための代替手段でもあります。
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
しかし、ベストプラクティスはAsyncTaskを使用することです。
Android.os.NetworkOnMainThreadExceptionを修正する方法
NetworkOnMainThreadExceptionとは何ですか?
Androidでは、UIスレッド(メインスレッド)に対して行う必要があるすべてのUI操作。メインスレッドでバックグラウンド操作または何らかのネットワーク操作を実行すると、この例外が発生してアプリが応答しなくなる恐れがあります。
それを修正する方法:
この問題を回避するには、asyncTaskを使用するなど、バックグラウンド操作またはネットワーク操作に別のスレッドを使用し、Volley、AsyncHttpなどのネットワーク操作にライブラリを使用する必要があります。
NetworkOnMainThread例外は、デフォルトのスレッド、つまりUIスレッドで何らかのネットワーク操作を呼び出したために発生します。 許可されていないAndroidのバージョン Android 3 (ハニカム)によると、メインスレッドの外部でネットワーク操作を呼び出す必要があります。
AsyncTask、IntentServiceを使用するか、独自のスレッドを作成してrunメソッド内で呼び出すことができます。詳細については、ネットワークへの接続を参照してください。
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");
コトリン
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
}
}
コトリンを呼び出す
MyNetworkTask().execute(url)
メインスレッドやUIスレッドでネットワークを呼び出すことはできません。あなたがネットワークを呼び出したい場合はAndroid上で2つのオプションがあります -
個人的にはasynctaskが好きです。詳細については このリンク を参照してください。
Androidはシングルスレッドで動作しているので、メインスレッドでネットワーク操作をしないでください。これを回避するにはさまざまな方法があります。
以下の方法でネットワーク操作を行います。
StrictMode.setThreadPolicy(policy) を使用しないでください。URIがフリーズし、まったく良い考えではありません。
あなたは実際に新しいThreadを始めることができます、私は以前にこの問題を抱えていて、この方法でそれを解決しました。
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)
あなたはKotlin-coroutines
を使うことができます
class YoutActivity : AppCompatActivity, CoroutineScope {
override fun onCreate(...) {
launch { yourHeavyMethod() }
}
suspend fun yourHeavyMethod() {
async{ yourNetworkCall() }.await()
...
...
}
}
これ /ガイドに従うことができます。
同様の問題がありました。あなたのアクティビティのoncreateメソッドで以下を使用しました。
//allow strict mode
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
そしてそれはうまくいきました。
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)
}
さまざまなオプション
通常のJava実行可能スレッドを使用してネットワークタスクを処理し、runOnUIThread()を使用してUIを更新することができます。
ネットワークレスポンスを取得した後にUIを更新したい場合は、intentservice/asyncタスクを使用できます。
UIスレッドでは決して長時間の作業をしないでください。その長時間の作業はサーバーとの通信、ファイルの読み取り/書き込みなどです。これらのタスクはバックグラウンドスレッドで行う必要があるので、Service
、AsyncTask
、Threads
が作成されました。クラッシュを防ぐためにStrictMode
を無効にすることはできますが、お勧めできません。
デバッグモードでStrictMode
少なくともを利用することをお勧めします。メインスレッドであなたのAppを遅くする問題のログを取得するために以下のコードを使用してください。
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
https://developer.Android.com/reference/Android/os/StrictMode.html についての多くがあります
あなたがコトリンとアンコで働いているならば、あなたは加えることができます
doAsync {
method()
}