Symfoware

Symfowareについての考察blog

Android http経由でのデータ取得とJSONデータの解析

Androidでリストビューに画像を表示してみました。
Android ListViewにリソースとして追加した画像を表示する

リストの情報はプログラム中にハードコーティングしていますが、
実際はwebサーバーからデータを取得して表示することになると思います。


下準備



webサーバーから受け取るデータはJSON形式を想定しています。
まずはJSONな文字列を解析する方法を調べてみます。

下準備として、前回のソースを修正。
ボタンを押したら表示用のArrayListを作成し、リストを表示するよう修正しました。

・activity_main.xml


  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout
  3.     xmlns:android="http://schemas.android.com/apk/res/android"
  4.     android:layout_width="match_parent"
  5.     android:layout_height="match_parent"
  6.     android:orientation="vertical">
  7.     <LinearLayout
  8.         android:layout_width="match_parent"
  9.         android:layout_height="wrap_content"
  10.         android:orientation="horizontal">
  11.         <Button
  12.             android:layout_width="wrap_content"
  13.             android:layout_height="wrap_content"
  14.             android:text="@string/button_read"
  15.             android:onClick="readJson" />
  16.     </LinearLayout>
  17.     <ListView
  18.         android:id="@+id/listView1"
  19.         android:layout_width="match_parent"
  20.         android:layout_height="match_parent"></ListView>
  21. </LinearLayout>



770_01.png


・MainActivity.java


  1. import android.graphics.Bitmap;
  2. import android.graphics.BitmapFactory;
  3. import android.support.v7.app.AppCompatActivity;
  4. import android.os.Bundle;
  5. import android.view.View;
  6. import android.widget.ListView;
  7. import java.util.ArrayList;
  8. public class MainActivity extends AppCompatActivity {
  9.     private ArrayList<ListRow> mListItems;
  10.     private MyAdapter mAdapter;
  11.     @Override
  12.     protected void onCreate(Bundle savedInstanceState) {
  13.         super.onCreate(savedInstanceState);
  14.         setContentView(R.layout.activity_main);
  15.         // リストに表示するデータ
  16.         mListItems = new ArrayList<>();
  17.         // MyAdapterを作成し、データを設定
  18.         mAdapter = new MyAdapter(this, R.layout.list_layout, mListItems);
  19.         // ListViewにArrayAdapterを関連付け、データの表示を行う
  20.         ListView listView = (ListView) findViewById(R.id.listView1);
  21.         listView.setAdapter(mAdapter);
  22.     }
  23.     public void readJson(View view) {
  24.         mListItems.clear();
  25.         // TODO
  26.         // ここでJSONを解析し、リストにデータを設定する
  27.         //
  28.         for (int i = 0; i < 3; i++) {
  29.             Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_suiginto);
  30.             ListRow item = new ListRow(bmp, "sample text No. " + String.valueOf(i));
  31.             mListItems.add(item);
  32.         }
  33.         mAdapter.notifyDataSetChanged();
  34.     }
  35. }




初期表示

770_02.png


ボタンを押すとリストを表示。

770_03.png





JSONデータの解析



JSONデータの解析には、標準で用意されているJSONObjectを使用すれば良いようです。
JSONObject

【Android】JSONデータをパースする方法
こちらを参考にさせていただきました。

読み込むJSONは以下の通り。


  1. {
  2.     rows : [
  3.         {
  4.             title : \"タイトル1\"
  5.         },
  6.         {
  7.             title : \"タイトル2\"
  8.         },
  9.         {
  10.             title : \"タイトル3\"
  11.         }
  12.     ]
  13. }




ソースはこのようになりました。


・MainActivity.java


  1. import android.graphics.Bitmap;
  2. import android.graphics.BitmapFactory;
  3. import android.support.v7.app.AppCompatActivity;
  4. import android.os.Bundle;
  5. import android.view.View;
  6. import android.widget.ListView;
  7. import org.json.JSONArray;
  8. import org.json.JSONException;
  9. import org.json.JSONObject;
  10. import java.util.ArrayList;
  11. public class MainActivity extends AppCompatActivity {
  12.     private ArrayList<ListRow> mListItems;
  13.     private MyAdapter mAdapter;
  14.     @Override
  15.     protected void onCreate(Bundle savedInstanceState) {
  16.         super.onCreate(savedInstanceState);
  17.         setContentView(R.layout.activity_main);
  18.         // リストに表示するデータ
  19.         mListItems = new ArrayList<>();
  20.         // MyAdapterを作成し、データを設定
  21.         mAdapter = new MyAdapter(this, R.layout.list_layout, mListItems);
  22.         // ListViewにArrayAdapterを関連付け、データの表示を行う
  23.         ListView listView = (ListView) findViewById(R.id.listView1);
  24.         listView.setAdapter(mAdapter);
  25.     }
  26.     public void readJson(View view) {
  27.         mListItems.clear();
  28.         StringBuilder jsonBuilder = new StringBuilder();
  29.         jsonBuilder.append("{");
  30.         jsonBuilder.append("    rows : [");
  31.         jsonBuilder.append("        {");
  32.         jsonBuilder.append("            title : \"タイトル1\"");
  33.         jsonBuilder.append("        },");
  34.         jsonBuilder.append("        {");
  35.         jsonBuilder.append("            title : \"タイトル2\"");
  36.         jsonBuilder.append("        },");
  37.         jsonBuilder.append("        {");
  38.         jsonBuilder.append("            title : \"タイトル3\"");
  39.         jsonBuilder.append("        }");
  40.         jsonBuilder.append("    ]");
  41.         jsonBuilder.append("}");
  42.         String jsonString = jsonBuilder.toString();
  43.         try {
  44.             // JSONな文字列をオブジェクトに変換
  45.             JSONObject json = new JSONObject(jsonString);
  46.             // "rows"の項目を取得し、ループ
  47.             JSONArray rows = json.getJSONArray("rows");
  48.             for (int i = 0; i < rows.length(); i++) {
  49.                 // titleを取得
  50.                 JSONObject row = rows.getJSONObject(i);
  51.                 String title = row.getString("title");
  52.                 Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_suiginto);
  53.                 ListRow item = new ListRow(bmp, title);
  54.                 mListItems.add(item);
  55.             }
  56.         } catch (JSONException e) {
  57.             e.printStackTrace();
  58.         }
  59.         mAdapter.notifyDataSetChanged();
  60.     }
  61. }




実行結果

770_04.png





AsyncTaskLoader



ネットワーク接続など、外部リソースからデータを取得する処理はメインスレッドで実行するとエラーになる模様。
AsyncTaskLoaderを利用して、別スレッドで実行するようにしてやります。

AsyncTaskLoaderの使い方がよくわからなかったので、http接続は一旦おいておき、
ローダーからJSON文字列を受け取るプログラムを書いてみます。

こちらを参考にさせていただきました。
正しいAsyncTaskLoaderの使い方

わかりやすさのため、サブクラスにせずひとつのクラスに記載しています。
MyLoaderという新規クラスを作成。

・MyLoader.java


  1. import android.content.Context;
  2. import android.support.v4.content.AsyncTaskLoader;
  3. public class MyLoader extends AsyncTaskLoader<String> {
  4.     private String mResult;
  5.     private boolean mIsStarted = false;
  6.     private String mUrl;
  7.     public MyLoader(Context context, String url) {
  8.         super(context);
  9.         this.mUrl = url;
  10.     }
  11.     @Override
  12.     protected void onStartLoading() {
  13.         if (mResult != null) {
  14.             deliverResult(mResult);
  15.             return;
  16.         }
  17.         if (!mIsStarted || takeContentChanged()) {
  18.             forceLoad();
  19.         }
  20.     }
  21.     @Override
  22.     protected void onForceLoad() {
  23.         super.onForceLoad();
  24.         mIsStarted = true;
  25.     }
  26.     @Override
  27.     public void deliverResult(String data) {
  28.         mResult = data;
  29.         super.deliverResult(data);
  30.     }
  31.     @Override
  32.     public String loadInBackground() {
  33.         try {
  34.             Thread.sleep(3000);
  35.         } catch (InterruptedException ignored) {}
  36.         StringBuilder jsonBuilder = new StringBuilder();
  37.         jsonBuilder.append("{");
  38.         jsonBuilder.append("    rows : [");
  39.         jsonBuilder.append("        {");
  40.         jsonBuilder.append("            title : \"タイトル1\"");
  41.         jsonBuilder.append("        },");
  42.         jsonBuilder.append("        {");
  43.         jsonBuilder.append("            title : \"タイトル2\"");
  44.         jsonBuilder.append("        },");
  45.         jsonBuilder.append("        {");
  46.         jsonBuilder.append("            title : \"タイトル3\"");
  47.         jsonBuilder.append("        }");
  48.         jsonBuilder.append("    ]");
  49.         jsonBuilder.append("}");
  50.         return jsonBuilder.toString();
  51.     }
  52. }





loadInBackgroundで3秒待った後、json文字列を返却しています。
ここが外部リソースを取得する処理に変わる予定です。


・MainActivity.java


  1. import android.graphics.Bitmap;
  2. import android.graphics.BitmapFactory;
  3. import android.support.v4.app.LoaderManager;
  4. import android.support.v4.content.Loader;
  5. import android.support.v7.app.AppCompatActivity;
  6. import android.os.Bundle;
  7. import android.view.View;
  8. import android.widget.ListView;
  9. import org.json.JSONArray;
  10. import org.json.JSONException;
  11. import org.json.JSONObject;
  12. import java.util.ArrayList;
  13. public class MainActivity extends AppCompatActivity {
  14.     private ArrayList<ListRow> mListItems;
  15.     private MyAdapter mAdapter;
  16.     private static final int LOADER_ID = 1;
  17.     private static final String ARG_URL_PARAM = "ARG_URL_PARAM";
  18.     @Override
  19.     protected void onCreate(Bundle savedInstanceState) {
  20.         super.onCreate(savedInstanceState);
  21.         setContentView(R.layout.activity_main);
  22.         // リストに表示するデータ
  23.         mListItems = new ArrayList<>();
  24.         // MyAdapterを作成し、データを設定
  25.         mAdapter = new MyAdapter(this, R.layout.list_layout, mListItems);
  26.         // ListViewにArrayAdapterを関連付け、データの表示を行う
  27.         ListView listView = (ListView) findViewById(R.id.listView1);
  28.         listView.setAdapter(mAdapter);
  29.     }
  30.     public void readJson(View view) {
  31.         mListItems.clear();
  32.         Bundle args = new Bundle();
  33.         args.putString(ARG_URL_PARAM, "取得したいjsonのあるURLを指定");
  34.         // LoaderManagerに最後の引数のコールバックを利用して、AsyncTaskLoaderを作成してもらう
  35.         getSupportLoaderManager().initLoader(LOADER_ID, args, mCallback);
  36.     }
  37.     private final LoaderManager.LoaderCallbacks<String> mCallback = new LoaderManager.LoaderCallbacks<String>() {
  38.         @Override
  39.         // ローダーを取得 ここで引数を設定する
  40.         public Loader<String> onCreateLoader(int id, Bundle args) {
  41.             String url = args.getString(ARG_URL_PARAM);
  42.             return new MyLoader(MainActivity.this, url);
  43.         }
  44.         @Override
  45.         // MyLoaderからのコールバック
  46.         public void onLoadFinished(Loader<String> loader, String data) {
  47.             // ローダーを削除
  48.             getSupportLoaderManager().destroyLoader(loader.getId());
  49.             // ローダーが取得した文字列を解析して表示
  50.             try {
  51.                 // JSONな文字列をオブジェクトに変換
  52.                 JSONObject json = new JSONObject(data);
  53.                 // "rows"の項目を取得し、ループ
  54.                 JSONArray rows = json.getJSONArray("rows");
  55.                 for (int i = 0; i < rows.length(); i++) {
  56.                     // titleを取得
  57.                     JSONObject row = rows.getJSONObject(i);
  58.                     String title = row.getString("title");
  59.                     Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_suiginto);
  60.                     ListRow item = new ListRow(bmp, title);
  61.                     mListItems.add(item);
  62.                 }
  63.             } catch (JSONException e) {
  64.                 e.printStackTrace();
  65.             }
  66.             mAdapter.notifyDataSetChanged();
  67.         }
  68.         @Override
  69.         public void onLoaderReset(Loader<String> loader) {
  70.             // do nothing
  71.         }
  72.     };
  73. }




ボタンをクリックしたらローダーを呼び出し。
コールバックでデータを受け取り解析しています。

これで、ボタンを押して3秒後にリストが表示されるようになりました。





外部リソースからJOSNデータを取得



いよいよ外部リソースからJSONデータを受け取り、リストを表示してみます。
MyLoaderのloadInBackgroundを書き換えます。

・MyLoader.java


  1. import android.content.Context;
  2. import android.os.Build;
  3. import android.support.annotation.RequiresApi;
  4. import android.support.v4.content.AsyncTaskLoader;
  5. import java.io.BufferedReader;
  6. import java.io.InputStreamReader;
  7. import java.net.HttpURLConnection;
  8. import java.net.URL;
  9. import java.nio.charset.StandardCharsets;
  10. public class MyLoader extends AsyncTaskLoader<String> {
  11.     private String mResult;
  12.     private boolean mIsStarted = false;
  13.     private String mUrl;
  14.     public MyLoader(Context context, String url) {
  15.         super(context);
  16.         this.mUrl = url;
  17.     }
  18.     @Override
  19.     protected void onStartLoading() {
  20.         if (mResult != null) {
  21.             deliverResult(mResult);
  22.             return;
  23.         }
  24.         if (!mIsStarted || takeContentChanged()) {
  25.             forceLoad();
  26.         }
  27.     }
  28.     @Override
  29.     protected void onForceLoad() {
  30.         super.onForceLoad();
  31.         mIsStarted = true;
  32.     }
  33.     @Override
  34.     public void deliverResult(String data) {
  35.         mResult = data;
  36.         super.deliverResult(data);
  37.     }
  38.     @RequiresApi(api = Build.VERSION_CODES.KITKAT)
  39.     @Override
  40.     public String loadInBackground() {
  41.         HttpURLConnection connection = null;
  42.         StringBuilder jsonBuilder = new StringBuilder();
  43.         try {
  44.             URL url = new URL(mUrl);
  45.             connection = (HttpURLConnection) url.openConnection();
  46.             connection.setRequestMethod("GET");
  47.             if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
  48.                 throw new Exception("HTTP Response:" + connection.getResponseCode());
  49.             }
  50.             try (InputStreamReader isr = new InputStreamReader(
  51.                     connection.getInputStream(),
  52.                     StandardCharsets.UTF_8);
  53.                 BufferedReader reader = new BufferedReader(isr)) {
  54.                 String line;
  55.                 while ((line = reader.readLine()) != null) {
  56.                     jsonBuilder.append(line);
  57.                 }
  58.             }
  59.         } catch (Exception e) {
  60.             e.printStackTrace();
  61.         } finally {
  62.             if (connection != null) {
  63.                 connection.disconnect();
  64.             }
  65.         }
  66.         return jsonBuilder.toString();
  67.     }
  68. }





これでOKだと思い実行してみると、こんなエラーが発生します。


socket failed: EACCES (Permission denied)




こちらを参考に
Android でインターネットに接続するためのパーミッションを設定する

AndroidManifest.xmlを編集

770_05.png


  1. <?xml version="1.0" encoding="utf-8"?>
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  3.     package="com.example.baranche.myapplication">
  4.     <!-- この行を追加 -->
  5.     <uses-permission android:name="android.permission.INTERNET" />
  6.     <application
  7.         android:allowBackup="true"
  8.         android:icon="@mipmap/ic_launcher"
  9.         android:label="@string/app_name"
  10.         android:roundIcon="@mipmap/ic_launcher_round"
  11.         android:supportsRtl="true"
  12.         android:theme="@style/AppTheme">
  13.         <activity android:name=".MainActivity">
  14.             <intent-filter>
  15.                 <action android:name="android.intent.action.MAIN" />
  16.                 <category android:name="android.intent.category.LAUNCHER" />
  17.             </intent-filter>
  18.         </activity>
  19.     </application>
  20. </manifest>




ボタンを押してみると、webサーバーのjsonファイルを取得&解析して表示できました。

770_06.png



【参考URL】

JSONObject
【Android】JSONデータをパースする方法
正しいAsyncTaskLoaderの使い方
Android でインターネットに接続するためのパーミッションを設定する
関連記事

テーマ:プログラミング - ジャンル:コンピュータ

  1. 2017/08/12(土) 17:07:37|
  2. Java
  3. | トラックバック:0
  4. | コメント:0
  5. | 編集
<<Android ネットワークライブラリ「Volley」を使用してJSONデータ取得 | ホーム | Python3 別途ライブラリをインストールせず、ImageMagickを使用するpymraw>>

コメント

コメントの投稿


管理者にだけ表示を許可する

トラックバック

トラックバック URL
http://symfoware.blog68.fc2.com/tb.php/2023-5e919869
この記事にトラックバックする(FC2ブログユーザー)