- 締切済み
Androidの非同期処理でネットワークエラー発生
Androidから、AsyncTaskを使用してURLにアクセスし、 外部DBのMySQLから引っ張ってきたデータをXML形式に変換したものを 取得したいと思っています。 一通り処理を作成し、実験的にデータを取得してみたのですが、 問題なく取得できるものと、途中でNetworkOnMainThreadExceptionが発生するものが 出てきてしまいました。 具体的には、下記のようなテーブルのデータを全カラム分取得し、 XML形式に変換しています。 【取得に成功したテーブル】 カラム数:3 内訳:INT型→1カラム、VARCHAR型→2カラム 一度に取得するデータ数:7行 【取得に失敗するテーブル】 カラム数:22 内訳:INT型→7カラム、CARCHAR型→12カラム、 TEXT型→1カラム、DATETIME型→2カラム 一度に取得するデータ数:最大10行 NetworkOnMainThreadExceptionが発生するタイミングは、 XMLデータを取得したタイミングではなく、 取得したXMLをデータ格納用オブジェクトに入れて ArrayListに蓄積しているタイミングです。 データが取得できているテーブルもあるので、 AsyncTaskの使い方が間違っているわけではないと思うのですが NetworkOnMainThreadExceptionが発生する原因を調べてみても、 AsyncTaskを使っていないから、というものしか出てこないので、 原因がさっぱりわかりません。 AsyncTaskの使い方で何か間違っている箇所があるのか、 そもそもAsyncTaskで取得させるデータ量に制限があるのか、 何かの指定をすればうまくできるものなのか、 何か思い当たることがあれば、教えていただきたいと思っています。 【AsyncTaskをextendsした自作クラス】 public class AsyncXmlLoader extends AsyncTask<String, Integer, InputStream> { public interface AsyncCallback { void preExecute(); void postExecute(InputStream result); void progressUpdate(int progress); void cancel(); } private AsyncCallback asyncCallback = null; public AsyncXmlLoader(AsyncCallback asyncCallback) { this.asyncCallback = asyncCallback; } @Override protected void onPreExecute() { super.onPreExecute(); asyncCallback.preExecute(); } @Override protected void onProgressUpdate(Integer... progress) { super.onProgressUpdate(progress); asyncCallback.progressUpdate(progress[0]); } @Override protected void onPostExecute(InputStream result) { super.onPostExecute(result); asyncCallback.postExecute(result); } @Override protected void onCancelled() { super.onCancelled(); asyncCallback.cancel(); } @Override protected InputStream doInBackground(String... uri) { InputStream inputStream = null; try { // URLクラスのインスタンス作成 URL url = new URL(uri[0]); // コネクション接続 URLConnection connection = url.openConnection(); // ストリームを取得 inputStream = connection.getInputStream(); } catch (MalformedURLException e) { Log.e("AsyncXmlLoader", e); } catch (IOException e) { Log.e("AsyncXmlLoader", e); } catch (Exception e) { Log.e("AsyncXmlLoader", e); } return inputStream; } } 【実際にデータを取得している箇所】 public class MainActivity extends ActionBarActivity { private final String URL = "http://hoge.jp/test.php"; private TextView testTextView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // TextViewを取得 testTextView = (TextView)findViewById(R.id.textViewTest); // XMLからカテゴリデータを取得 AsyncXmlLoader xml = new AsyncXmlLoader(new AsyncXmlLoader.AsyncCallback() { // 実行前 public void preExecute() { } // 実行後 public void postExecute(InputStream result) { if (result == null) { Log.e("AsyncCallback", "データ取得失敗"); return; } // XMLのデータを取得して画面に表示 BufferedReader br = new BufferedReader(new InputStreamReader(result)); StringBuilder sb = new StringBuilder(); String line; try { while ((line = br.readLine()) != null) { sb.append(line); } } catch (IOException e) { Log.e("AsyncCallback", e); } testTextView.setText(sb.toString()); } // 実行中 public void progressUpdate(int progress) { } // キャンセル public void cancel() { } }); // 処理を実行 xml.execute(URL); } } お知恵を貸していただきたいと思います。 どうぞよろしくお願いいたします。
- みんなの回答 (1)
- 専門家の回答
みんなの回答
- HNEX
- ベストアンサー率62% (43/69)
doInBackgroundの戻りをInputStreamにしているのが問題だと思います。 このInputStreamはURLConnectionによるダウンロードのStreamなのでこれをメインスレッド上で取得しようとした為にNetworkOnMainThreadExceptionが発生していると思われます。 じゃあなぜ発生する時と発生しない時があるのか?という所ですが InputStream上にバッファがある為、データ件数が少ない場合はそのバッファに全データが収まっているので、メインスレッド上で読み出しを行ってもバッファへのアクセスとなり例外が発生しないのではないかと推察します。 データが多ければバッファに収まらずに次のデータをバッファにロードしようとするので、それによって例外発生。 こういうケースの実装としてはInputStreamの取得はdoInBackground内で完結させ、取得したデータを何らかのオブジェクトにしてから戻すようにするのが良いと思います。