月別アーカイブ: 2014年10月

経路情報をパースする 設計 3

RouteJsonParserさんに離婚されて、一人で子供や孫たちの面倒を見ることになったRoutesさん、母子家庭で大変なので整理してみます。
今はこういった現状

public class Routes {
	private String summary = "";
	private List<Legs> legs = null;
	private String waypoint_order = "";
	private List<OverviewPolyline> overview_polyline = null;
	private String bounds = "";
	private String copyrights = "";
	private String warnings = "";
	class Legs {
		private List<Steps> steps = null;
		private Distance distance = null;
		private Duration duration = null;
		private ArrivalTime arrival_time = null;
		private String departure_time = ""; //Time オブジェクト?
		private String start_location = "";
		private String end_location = "";
		private String start_address = "";
		private String end_address = "";
		class Steps {
			private String html_instructions = "";
			private Distance distance = null;
			private Duration duration = null;
			private String start_location = "";
			private String end_location = "";
			private List<Steps> sub_steps = null;
			private TransitDetails transit_details = null;
			class TransitDetails {
				// 乗り換えについては後で設計する
			}
		}
		class Distance {}
		class Duration {}
		class ArrivalTime {}
	}
	class OverviewPolyline {}
	
	Routes() {}

}

最初に手がけるのは、子供たちを自立させます。とくに子供や孫を抱えてるものたち!

class Legs、class OverviewPolylineを外部クラスにします。
アトリビュートを眺めると、内部クラスとするべきものがない。
ただし、クラスにすべきものがいくつかある。
waypoint_order 、bounds 、warnings などは配列もしくはデータ構造を持っている。
現在、その構造やオブジェクトの分析は行なっていないので、Stringのままとして扱う。

そうすると
Routes.java

public class Routes {
	private String summary = "";
	private List<Legs> legs = null;
	private String waypoint_order = "";
	private List<OverviewPolyline> overview_polyline = null;
	private String bounds = "";
	private String copyrights = "";
	private String warnings = "";

	Routes() {}
}

class Routes の要件は JSONObject から必要な値を取得することである。
その要件で書くと

public class Routes {
	/**
	 * ログのタグ
	 */
	private final String LOG_TAG = this.getClass().getSimpleName();
	/**
	 * JSON TAGS
	 */
	public enum Tags {
		SUMMARY("summary"),  
		WAYPOINTORDER("waypoint_order"), 
		BOUNDS("bounds"),
		COPYRIGHTS("copyrights"), 
		WARNINGS("warnings"), 
		LEGS("legs"), 
		OVERVIEWPOLYLINE("overview_polyline");
		private String name;
		Tags(String name){this.name = name;}
		public String getName(){return this.name;}
	}
	/**
	 * JSON オブジェクト
	 */
	JSONObject jsonRoutes = null;
	/**
	 * コンストラクタ
	 * @param json RouteJsonParser からわたされる
	 */
	public Routes(JSONObject json) {
		jsonRoutes = json;
	}
	public String getSummary() throws JSONException {
		return jsonRoutes.getString(Tags.SUMMARY.getName());
	}
	public String getWaypointOrder() throws JSONException {
		return jsonRoutes.getString(Tags.WAYPOINTORDER.getName());
	}
	public String getBounds() throws JSONException {
		return jsonRoutes.getString(Tags.BOUNDS.getName());
	}
	public String getCopyrights() throws JSONException {
		return jsonRoutes.getString(Tags.COPYRIGHTS.getName());
	}
	public String getWarnings() throws JSONException {
		return jsonRoutes.getString(Tags.WARNINGS.getName());
	}
	public List<Legs> getLegsList() {
		List<Legs> list = new ArrayList<Legs>();
		JSONArray array;
		try {
			array = jsonRoutes.getJSONArray(Tags.LEGS.getName());
			for(int i=0;i<array.length();i++) {
				list.add( new Legs(array.getJSONObject(i)) );
			}
		} catch (JSONException e) {
			list.clear();
			Log.e(LOG_TAG, "getLegsList() " + e.getMessage() );
		}
		return list;
	}
	public List<OverviewPolyline> getOverviewPolylineList(){
		List<OverviewPolyline> list = new ArrayList<OverviewPolyline>();
		JSONArray array;
		try {
			array = jsonRoutes.getJSONArray(Tags.OVERVIEWPOLYLINE.getName());
			for(int i=0;i<array.length();i++) {
				list.add( new OverviewPolyline(array.getJSONObject(i)) );
			}
		} catch (JSONException e) {
			list.clear();
			Log.e(LOG_TAG, "getOverviewPolyline() " + e.getMessage() );
		}
		return list;
	}
}

ちゃっかり実装してますが、してないこととして見てください。(笑)
子供たちを自立させたので、内部クラスは存在しません。ただ、アトリビュートを enum Tags で宣言しています。
enum Tags を インターフェイスにしても問題はありません。
インターフェイスについてはここでは触れません。何かに機会があれば、書くかもしれませんが・・・

このクラスの要件はJSONObjectから取得するなので、set関数は存在しません。
すべて、get関数です。
コンストラクタだけは、RouteJsonParser から 子供たちの養育費(JSONObject)をもらっています。(笑)
内部クラスであった、子や孫たちも同じように設計していけば完了です。

これだと、アトリビュートが追加されたり、バリュー(値)に若干の変更があってもクラスを派生させるなり、直接改修するなりしても他のクラスには依存してないので問題ありません。
大規模な構造変化の場合は最初から作り直さなければなりませんが(笑)

内部クラスは、その他のクラスと依存関係がないものだけにしましょう。
そのようにすると、そのクラスだけで完結できます。ただし、例外的なものはありますが、ここではふれません。

Routesさんに後日聞いてみました。
「子供たちはどうしました?」
「みんな自立して働いてます。必要なときに養育費をわけてます(依存関係)」

経路情報をパースする 設計 2

RouteJsonParser の構造は、いまだと必要のないアトリビュートが存在している。

status、class Routes に関連する子や孫たちである。

ストーリを読んでみると、RouteJsonParserさんは大変だ。自分の仕事だけでなく、子供、孫まで面倒見てるんだね。
子供が生まれたらそのつど、かみさんにはまかせておけないから、RouteJsonParserさんが面倒見るんだ、しかもその孫たちまでも、もしかしたら曾孫も面倒見るのかな?

といった感じ!

JSONデータのバージョンが変わったらそのつど、RouteJsonParser を改修し複数のバージョンに対応していく。
・・・ありえん!
これは、内部クラスのひとつの弊害である。
お手伝いしたプロジェクトで、こんな事例によく遭遇した。

なので、RouteJsonParser を書き直します。

public class RouteJsonParser {
	/**
	 * 元になる経路情報 
	 */
	private JSONObject jsonRoute = null;
	/**
	 * パースされた経路情報
	 */
	private List<Routes> routes = null;
	/**
	 * パースできない場合のエラーコード
	 */
	private int error = 0;
	/**
	 * GoogleDirectionsAPI のバージョン
	 * 2014/10/01 現在リリースされているバージョン 1 を初期値とする
	 */
	private int verGoogleDirectionsAPI = 1;
	/**
	 * コンストラクタ
	 * Google Directions API のバージョンは 1
	 */
	public RouteJsonParser() {}
	/**
	 * コンストラクタ
	 * @param ver Google Directions API のバージョン
	 */
	public RouteJsonParser(int ver) {
		verGoogleDirectionsAPI = ver;
	}
	/**
	 * JSONデータをパースする
	 * @param jsonString JSON文字列
	 * @return パースした経路情報
	 */
	public List<Routes> parse(String jsonString) {
		// バージョンごとに対応、JSONデータをパースする
		switch(verGoogleDirectionsAPI) {
		case 1:
			break;

		default:
			
		}
		return routes;
	}
	/**
	 * パースされた経路情報を取得
	 * @return
	 */
	public List<Routes> getParseListt() {
		return routes;
	}
	/**
	 * JSONデータが有効であるか検査する
	 * @return true:有効 false:無効
	 */
	public boolean isSuccess() {
		return true;
	}
	/**
	 * ステータスコードの取得
	 * @return
	 */
	public String getStatus() {
		return "";
	}
	/**
	 * エラーコードを取得
	 * @return
	 */
	public int getError() {
		return error;
	}
}

だいぶすっきり、明確になりました。

ストーリーを読んでみると、
RouteJsonParserさんのお仕事はJSONデータをパースすることで、パースした内容はいつでも引き出せるように保持する。
パースするデータがバージョンの異なるデータが放り込まれても、生まれたときに対応するバージョンが決められているので、決められたことしかしない。なので、生まれる前にバージョンをきめといてね!
パースのお仕事が済んだら、うまくできたか報告することと、ちょっとだけパースした中身のステータスを教えてくれるそうだ.
また、パースできなかった原因も報告してくれるらしい。

こんな感じで、私には読めました。いかがですか?

ちなみに、RouteJsonParserさんに Routesさんのことを聞いてみました。
「俺、Routesさんと付き合ってるんだけど、よく知らないんだ」
とのお答えでした。
以前のソースでは結婚し子供まで作っていたのに・・・・

経路情報をパースする 設計

内部クラス、無名クラスのことでぼやいていたので、そんじゃ、どうすればいいか書いてなかった。
入社1年目のシステムエンジニアやプログラマに、教えていた方法で、経路情報のパース処理について設計、実装を書いておく。

JSONデータの構造は、こちら Google Directions API を参考にした

RouteJsonParser.java

import org.json.JSONObject;

/**
 * @author Ryan's Factory
 *
 */
public class RouteJsonParser {
	private JSONObject jsonRoute = null;
	private String status = "";
	private List<Routes> routes = null;
	class Routes {
		private String summary = "";
		private List<Legs> legs = null;
		private String waypoint_order = "";
		private List<OverviewPolyline> overview_polyline = null;
		private String bounds = "";
		private String copyrights = "";
		private String warnings = "";
	}
	class Legs {
		private List<Steps> steps = null;
		private Distance distance = null;
		private Duration duration = null;
		private ArrivalTime arrival_time = null;
		private String departure_time = ""; //Time オブジェクト?
		private String start_location = "";
		private String end_location = "";
		private String start_address = "";
		private String end_address = "";
	}
	class OverviewPolyline {
	}
	class Steps {
		private String html_instructions = "";
		private Distance distance = null;
		private Duration duration = null;
		private String start_location = "";
		private String end_location = "";
		private List<Steps> sub_steps = null;
		private TransitDetails transit_details = null;
	}
	class Distance {
	}
	class Duration {
	}
	class ArrivalTime {
	}
	class TransitDetails {
		// 乗り換えについては後で設計する
	}
	RouteJsonParser() {
	}
	public List<Routes> parse(String jsonString) {
		return routes;
	}
}

書かれていたドキュメントを信じて(?)そのままをクラス構造としている。
実装するアトリビュート、オブジェクトはこの段階では具体的に書けないが、少なくとも、Jsonであることがわかっているので、基本を String と JSONObject にしている。

ドキュメントには配列の記述があるが、配列のままでは、メモリアロケーションに問題がある。
どういうことかと簡単に説明すると、配列の要素数が可変する場合、そのつどアロケーションしなければならないので、ListかMapのほうが効率がよい。
List or Map のどちらにするか?
Mapのほうが扱いやすいが、格納順序も考慮しなければいけないようなので、Listが最適だろうと判断する。

その他のオブジェクトについて判明しているのは、内部クラスで記述している。
Javaの利点、クラス設計が考えているままを記述できる。
元のデータが、構造化および機能などが設計されているので、そのままを書ける。
現段階では、内部クラスだが、設計が進めば外部クラスにしなければならないオブジェクトが見えてくる。

実装がなくても、コンパイルができるので、ほかのチームに迷惑がかからない(笑)

コーディングは文章です。Javaという言語を用いて、相手が内容を理解できるように書くのが基本です。
ソースを見れば、書いた人間のひととなりがうかがえます。

読みやすいソースは、ストーリをそのまま受け入れられます。
読みにくいソースは、ストーリが混乱したままよくわかりません。

非同期タスクでSandBoxにアクセスする 4

使用例として、GOOGLEマップの経路情報を取得してみる。

	private void searchRoute(LatLng origin, LatLng dest) {
		SandBox sandbox = new SandBox();
		sandbox.setScheme("https");
		sandbox.setAuthority("maps.googleapis.com");
		sandbox.setPath("/maps/api/directions/json");
		sandbox.putRequestor("origin", origin.latitude + "," + origin.longitude);
		sandbox.putRequestor("destination", dest.latitude + "," + dest.longitude);
		sandbox.putRequestor("language", "ja");
		sandbox.putRequestor("sensor", "true");
		sandbox.putRequestor("mode","walking");

		sandbox.setProgress(findViewById(R.id.progressBar1));
		SandBoxAsyncTask aTask = new SandBoxAsyncTask(this, sandbox, new RouteReceiver());
		aTask.execute(sandbox.getRequestor());
	}

this:アクティビティ
R.id.progressBar1:プログレスのID
RouteReceiver:受信処理で呼び出されるクラス

1、SandBox クラスを生成
2、サーバのアクセスに必要なパラメータなどを設定する
3、プログレスバーのオブジェクトを設定する
4、SandBoxAsyncTask(this, sandbox, new RouteReceiver()) で非同期タスクを生成する
5、タスクを実行する

コードはこんだけで、簡単にサンドボックスから情報を取得できる。
タスクを生成するとき設定するコールバックは、アクティビティで実装してもしても良いが、アクティビティがでかくなりすぎて可読性が悪くなる上お行儀が悪い!
なので、RouteReceiverというクラスを実装している。

RouteReceiver.java

import android.content.Context;
import android.view.View;

/**
 * @author Ryan's Factory
 *
 */
public class RouteReceiver implements SandBoxAsyncTaskCallback {
	/* (non-Javadoc)
	 * @see com.ryans.factory.introcatmap.SandBoxAsyncTaskCallback#onPreExecuteSandBox(android.content.Context, java.lang.Object)
	 */
	@Override
	public void onPreExecuteSandBox(Context context, Object progress) {
		if (progress instanceof View) {
			((View) progress).setVisibility(View.VISIBLE);
		}
	}
	/* (non-Javadoc)
	 * @see com.ryans.factory.introcatmap.SandBoxAsyncTaskCallback#onSuccessSandBox(java.lang.String, android.content.Context, java.lang.Object)
	 */
	@Override
	public void onSuccessSandBox(String jsonString, Context context, Object progress) {
		if (progress instanceof View) {
			((View) progress).setVisibility(View.GONE);
		}
		// パース処理を実装するならここに記述する
	}
	/* (non-Javadoc)
	 * @see com.ryans.factory.introcatmap.SandBoxAsyncTaskCallback#onFailedSandBox(int, android.content.Context, java.lang.Object)
	 */
	@Override
	public void onFailedSandBox(int error, Context context, Object progress) {
		if (progress instanceof View) {
			((View) progress).setVisibility(View.GONE);
		}
		// エラー処理を実装するならここに記述する
	}
}

onPreExecuteSandBox() 通信前にプログレスバーをぐるぐると表示する
onSuccessSandBox() 通信が成功したのでプログレスバーを消してJSONデータをパースする
onFailedSandBox() 通信が失敗したのでプログレスバーを消してエラーだとユーザに通知する

前に、サンプルソースや参考書などの可読性が悪いと書いたが、あるブログにアプリケーションの実行速度を高めるためにする方法なるものがあった。
その内容は、
1、get/set関数は使用しない
2、内部クラスもしくは無名クラスを多用する
3、速度優先するリソースの場合は静的に配置する
などなど・・・

そんなんじゃ、可読性が悪くなるはずだ。
そんなことやって、少しはましになるだろうけど、メンテナンスはどうするのだろう・・・
処理速度優先ならば、C/C++で書きましょう!
Javaは遅いです。Javaの利点を無くしてコードを工夫して速度を高めるなんてやめましょう!

30年ほど前に、Cでは遅すぎてユーザのストレスが高くなるのでCソース最適化なんてものがあったけれど、
アセンブラで書けばそんなことしなくても、処理速度やリソースを有効に利用できる。

それと同じじゃ!

時代が変わっても、同じような輩が発生するんだな。

非同期タスクでSandBoxにアクセスする 3

通信を実行するクラス
SandBox .java

import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.util.EntityUtils;

import android.net.Uri;
import android.net.Uri.Builder;
import android.util.Log;

/**
 * サンドボックス 通信処理クラス
 * @author Ryan's Factory
 * @since 2014/10/01
 */
public class SandBox {
	
	/**
	 * ログのタグ
	 */
	private final String LOG_TAG = this.getClass().getSimpleName();
	/**
	 * クエリーパラメータのクラス
	 */
	public class Requestor {
		/**
		 * パラメータの名前
		 */
		private String name;
		/**
		 * パラメータの値
		 */
		private String parmeter;
		
		public Requestor(String name,String parmeter) {
			this.name = name;
			this.parmeter = parmeter;
		}

		public String getName() {
			return name;
		}

		public String getParmeter() {
			return parmeter;
		}
	}
	/**
	 * 接続のタイムアウト
	 * 初期値:3分
	 */
	private static final int CONNECTION_TIMEOUT = 1000 * 60 * 3;
	private int connectiion_timeout = CONNECTION_TIMEOUT;
	/**
	 * ソケットのタイムアウト
	 * 初期値:3分
	 */
	private static final int SOKET_TIMEOUT =  1000 * 60 * 3;
	private int soket_timeout = SOKET_TIMEOUT;
	/**
	 * キャラセット
	 */
	private static final String CHARSET = "utf-8";
	/**
	 * エラーコード
	 */
	private int error = 0;
	/**
	 * IOException が発生した
	 */
	public static final int IO_ERROR = 1000;
	/**
	 * ClientProtocolException が発生した
	 */
	public static final int CLIENTPROTOCOL_ERROR = 1001;
	/**
	 * プロトコル
	 */
	private String scheme;
	/**
	 * ドメイン
	 */
	private String authority;
	/**
	 * パス
	 */
	private String path;
	/**
	 * クエリーパラメータ
	 */
	private Map<String, String> mapRequestor = null;
	/**
	 * プログレスオブジェクト
	 */
	private Object progress = null;
	/**
	 * コンストラクタ
	 */
	public SandBox() {
		mapRequestor = new HashMap<String,String>();
	}
	
	public void setScheme(String scheme) {
		this.scheme = scheme;
	}

	public void setAuthority(String authority) {
		this.authority = authority;
	}

	public void setPath(String path) {
		this.path = path;
	}

	public void setConnectiion_timeout(int connectiion_timeout) {
		this.connectiion_timeout = connectiion_timeout;
	}

	public void setSoket_timeout(int soket_timeout) {
		this.soket_timeout = soket_timeout;
	}
	
	public void putRequestor(String key,String value) {
		mapRequestor.put(key, value);
	}
	
	public Requestor[] getRequestor() {
		Requestor[] params;
		if ( mapRequestor.isEmpty() ) {
			params = new Requestor[1];
			params[0]= new Requestor("","");
		} else {
			params = new Requestor[mapRequestor.size()];
			int i = 0;
			for (Iterator<Map.Entry<String, String>> it = mapRequestor.entrySet().iterator(); it.hasNext();i++ ) {
			    Map.Entry<String, String> entry = it.next();
			    params[i] = new Requestor(entry.getKey(),entry.getValue());
			}
		}
		return params;
	}
	
	/**
	 * プログレスオブジェクトを設定
	 * @param progress
	 */
	public void setProgress(Object progress) {
		this.progress = progress;
	}

	/**
	 * プログレスオブジェクトを取得
	 * @return
	 */
	public Object getProgress() {
		return progress;
	}

	public String request(Requestor[] params) {
		String resulte = "";
		error = 0;
		// クライアント生成
		HttpClient httpClient = createHttpClient();
		// リクエスト生成
		HttpUriRequest httpRequest = createHttpUriRequest(params);
		// レスポンス取得
		HttpResponse httpResponse = getHttpResponse(httpClient,httpRequest);
		// レスポンス正常?
		if ( isResponseOK(httpResponse) ) {
			// データ取得
			try {
				resulte = getTextResponse(httpResponse);
			} catch (IOException e) {
				error = IO_ERROR;
				Log.e(LOG_TAG, "" + e.getMessage() );
			}
		}
		// 切断
		httpClient.getConnectionManager().shutdown();
		return resulte;
	}
	
	public boolean isSuccess() {
		if ( 0 == error) {
			return true;
		}
		return false;
	}

	public int getErrorCode() {
		return error;
	}
	
	private boolean isResponseOK(HttpResponse httpResponse) {
		if (httpResponse != null ) {
			if ( httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK ) {
				return true;
			}
			error = httpResponse.getStatusLine().getStatusCode();
			Log.e(LOG_TAG, "" + httpResponse.getStatusLine().getReasonPhrase() );
		}
		return false;
	}
	
	private String getTextResponse(HttpResponse httpResponse) throws IOException {
		String resulte = "";
		HttpEntity httpEntity = httpResponse.getEntity();
		if( null == httpEntity ) {
			error = IO_ERROR;
			return "";
		}
		try {
			resulte = EntityUtils.toString(httpEntity, CHARSET);
		} catch (Exception e) {
			error = IO_ERROR;
			Log.e(LOG_TAG, "" + e.getMessage() );
		} finally {
			httpEntity.consumeContent();
		}
		return resulte;
	}

	private HttpClient createHttpClient() {
		DefaultHttpClient httpClient = new DefaultHttpClient();
		HttpConnectionParams.setConnectionTimeout(httpClient.getParams(), connectiion_timeout);
		HttpConnectionParams.setSoTimeout(httpClient.getParams(), soket_timeout);
		return httpClient;
	}

	private HttpUriRequest createHttpUriRequest(Requestor[] params) {
		return new HttpGet(createUriBuilder(params).toString());
	}
	
	private Builder createUriBuilder(Requestor[] params) {
		Uri.Builder uriBuilder = new Uri.Builder();
		uriBuilder.scheme(scheme);
		uriBuilder.authority(authority);
		uriBuilder.path(path);
		for(Requestor req: params) {
			uriBuilder.appendQueryParameter(req.getName(),req.getParmeter());
		}
		return uriBuilder;
	}
	
	private HttpResponse getHttpResponse(HttpClient httpClient, HttpUriRequest httpRequest)  {
		HttpResponse httpResponse = null;
		try {
			httpResponse = httpClient.execute(httpRequest);
		} catch (ClientProtocolException e) {
			error = CLIENTPROTOCOL_ERROR;
			Log.e(LOG_TAG, "" + e.getMessage() );
		} catch (IOException e) {
			error = IO_ERROR;
			Log.e(LOG_TAG, "" + e.getMessage() );
		}
		return httpResponse;
	}
	
}

通信処理を行なう関数 String request(Requestor[] params)
パラメータを格納するクラス Requestor は使い回しするクラスではないから内部クラスとする。

通信処理が成功したか検査する関数  isSuccess()

細かいところは実際に使用する例で説明する

非同期タスクでSandBoxにアクセスする 2

インターフェイス
SandBoxAsyncTaskCallback.java

import android.content.Context;

/**
 * 非同期タスクのコールバックインターフェイス
 * @author Ryan's Factory
 * @since 2014/10/01
 *
 */
public interface SandBoxAsyncTaskCallback {
	/**
	 * 通信する前に呼び出される
	 * @param context コンストラクタにわたされた Context
	 * @param progress プログレスオブジェクト {@link SandBox#setProgress(Object)}
	 */
	public void onPreExecuteSandBox(Context context,Object progress);
	/**
	 * SandBox から JSONデータを取得できたときに呼び出される
	 * @param jsonString 取得できたJSONデータ
	 * @param context コンストラクタにわたされた Context
	 * @param progress プログレスオブジェクト {@link SandBox#setProgress(Object)}
	 */
	public void onSuccessSandBox(String jsonString,Context context,Object progress);
	/**
	 * 通信が失敗したときに呼び出される
	 * @param error サンドボックス内で発生した例外コード、もしくは HttpStatus のエラーコード
	 * @param context コンストラクタにわたされた Context
	 * @param progress プログレスオブジェクト {@link SandBox#setProgress(Object)}
	 */
	public void onFailedSandBox(int error,Context context,Object progress);
}

onPreExecuteSandBox()
コンテキストやプログレスオブジェクトが引数としてあるので、通信を開始する前にダイアログやビューなどを表示できる

onSuccessSandBox()
通信が成功したらJSONデータをパースしたり、ダイアログやビューなどを表示・消去したりできる

onFailedSandBox()
通信が失敗したときの処理を書く。ダイアログなりテキストビューなりなんなりと

非同期タスクでSandBoxにアクセスする

Android ではアクティビティ内に通信処理を直接書くとコンパイルできない。
仕方がないのでスレッド生成して書こうとしたら、AsyncTask で処理をしろと神様(Android SDK)がおっしゃってる。

案の定Web上には、中途半端なソースしかない。
サンプルを参考にしながら使い回しができるクラスを書いた。

SandBoxAsyncTask.java

/**
 * 非同期タスクでSandBoxにアクセスする
 * @author Ryan's Factory
 * @since 2014/10/01
 */
public class SandBoxAsyncTask extends AsyncTask<SandBox.Requestor, Void, String> {

	/**
	 * コンテキスト
	 */
	private Context context;
	/**
	 * コールバック
	 */
	private SandBoxAsyncTaskCallback callback;
	/**
	 * 通信処理クラス
	 */
	private SandBox sandbox;
	/**
	 * コンストラクタ
	 */
	@SuppressWarnings("unused")
	private SandBoxAsyncTask() {};
	/**
	 * コンストラクタ
	 * @param context コンテキスト
	 * @param sandbox 通信処理クラス
	 * @param callback コールバック
	 */
	public SandBoxAsyncTask(Context context, SandBox sandbox, SandBoxAsyncTaskCallback callback) {
		this.context = context;
		this.sandbox = sandbox;
		this.callback = callback;
	}
	/**
	 * 通信処理を行なう前に呼び出される
	 */
	@Override
	protected void onPreExecute() {
		callback.onPreExecuteSandBox(context,sandbox.getProgress());
	}
	/**
	 * 通信処理を呼び出す
	 */
	@Override
	protected String doInBackground(SandBox.Requestor... params) {
		return sandbox.request(params);
	}
	/**
	 * 通信処理が完了した
	 */
	@Override
	protected void onPostExecute(String result) {
		if( sandbox.isSuccess() ) {
			// 取得できた
			callback.onSuccessSandBox(result,context,sandbox.getProgress());
		} else {
			// 取得できなかった
			callback.onFailedSandBox(sandbox.getErrorCode(),context,sandbox.getProgress());
		}
	}
}
AsyncTask<SandBox.Requestor, Void, String>

第一パラメータ SandBox.Requestor:通信処理を実行する関数に渡されるパラメータ

サンドボックスクラス内で宣言する。

第二パラメータ Void:処理中に何かしたいとき中断とか、だけど実装しないのでVoidにする

第三パラメータ String:処理終了後にわたされるパラメータ

JSONデータが目的なのでStringにする

インターフェイスについては次へ