Android 初心者です。新しい言語を学ぶとき、まずは RSSリーダ的なものか、外部APIでデータを取得する何かを作りながら学びたい派なのですが、何故か Android の入門的な書籍には方法が書いてないので、調べながらやってみました。
参考にさせて頂いた記事
- Androidの汎用的な非同期通信クラスできたよー!
- AsyncTaskにリスナーを追加してActivityで処理する
- Android AsyncTask with JSON Parsing
- JSONをパース(解析)する
今回の環境
Android Stuido
:1.0.1
JDK
:1.7.0_71
compileSdkVersion
:21
minSdkVersion
:15
targetSdkVersion
:21
通信用クラスの作成
AsyncTask を継承してクラスを作成し、コールバック用のインタフェースを実装します。実際に通信している部分は doInBackground
メソッドです。
package com.example.hkusu.jsongetsample;
import android.os.AsyncTask;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
public class AsyncJsonLoader extends AsyncTask<String, Integer, JSONObject> {
public interface AsyncCallback {
void preExecute();
void postExecute(JSONObject result);
void progressUpdate(int progress);
void cancel();
}
private AsyncCallback mAsyncCallback = null;
public AsyncJsonLoader(AsyncCallback _asyncCallback) {
mAsyncCallback = _asyncCallback;
}
@Override
protected void onPreExecute() {
super.onPreExecute();
mAsyncCallback.preExecute();
}
@Override
protected void onProgressUpdate(Integer... _progress) {
super.onProgressUpdate(_progress);
mAsyncCallback.progressUpdate(_progress[0]);
}
@Override
protected void onPostExecute(JSONObject _result) {
super.onPostExecute(_result);
mAsyncCallback.postExecute(_result);
}
@Override
protected void onCancelled() {
super.onCancelled();
mAsyncCallback.cancel();
}
@Override
protected JSONObject doInBackground(String... _uri) {
HttpClient httpClient = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(_uri[0]);
try {
HttpResponse httpResponse = httpClient.execute(httpGet);
if (httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
httpResponse.getEntity().writeTo(outputStream);
outputStream.close();
return new JSONObject(outputStream.toString());
} else {
httpResponse.getEntity().getContent().close();
throw new IOException();
}
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
return null;
}
}
Fragment から呼び出す
今回はイベント開催サービスである ATND のAPIを利用し、"Android" に関するイベントのタイトルを ListView に表示することにします。
レイアウトを用意して、
<ListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/listView" />
Fragment から呼び出します。asyncJsonLoader.execute(uri)
で通信を指示し、通信が完了するとコールバックで postExecute
メソッドが呼び出されます。
package com.example.hkusu.jsongetsample;
import android.support.v7.app.ActionBarActivity;
import android.support.v4.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
public class MainActivity extends ActionBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.container, new PlaceholderFragment())
.commit();
}
}
public static class PlaceholderFragment extends Fragment {
private final String uri = "http://api.atnd.org/events/?keyword=android&format=json";
public PlaceholderFragment() {
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main, container, false);
return rootView;
}
@Override
public void onStart() {
super.onStart();
AsyncJsonLoader asyncJsonLoader = new AsyncJsonLoader(new AsyncJsonLoader.AsyncCallback() {
// 実行前
public void preExecute() {
}
// 実行後
public void postExecute(JSONObject result) {
if (result == null) {
showLoadError(); // エラーメッセージを表示
return;
}
try {
// 各 ATND イベントのタイトルを配列へ格納
ArrayList<String> list = new ArrayList<>();
JSONArray eventArray = result.getJSONArray("events");
for (int i = 0; i < eventArray.length(); i++) {
JSONObject eventObj = eventArray.getJSONObject(i);
JSONObject event = eventObj.getJSONObject("event");
//Log.d("title", event.getString("title"));
list.add(event.getString("title"));
}
// ListView 用のアダプタを作成
ArrayAdapter<String> arrayAdapter = new ArrayAdapter<>(
getActivity(), android.R.layout.simple_list_item_1, list
);
// ListView にアダプタをセット
ListView listView = (ListView)getActivity().findViewById(R.id.listView);
listView.setAdapter(arrayAdapter);
} catch (JSONException e) {
e.printStackTrace();
showLoadError(); // エラーメッセージを表示
}
}
// 実行中
public void progressUpdate(int progress) {
}
// キャンセル
public void cancel() {
}
});
// 処理を実行
asyncJsonLoader.execute(uri);
}
// エラーメッセージ表示
private void showLoadError() {
Toast toast = Toast.makeText(getActivity(), "データを取得できませんでした。", Toast.LENGTH_SHORT);
toast.show();
}
}
}
AndroidManifest.xml
にて、通信の許諾を追加します。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.hkusu.jsongetsample" >
+ <uses-permission android:name="android.permission.INTERNET" />
〜
実機で動かしてみる
ほか
学習やプロトタイプ的にやるなら上記の方法で良いと思うが、本格的にやるならモデル層を作ったり、HTTP アクセスや JSON パースは何かライブラリを使った方が良さげ。
⇒ Androidの開発におけるベストプラクティス