【Android】同期/非同期のHTTP通信クラス | 超キレやすいプログラマのブログ

超キレやすいプログラマのブログ

キレてキレてキレまくる

Androidの通信処理って、なんでこんなに面倒なソース書かなあかんのや!?

Android3.0からメインスレッド(UIスレッド)からの通信が禁止された。(やろうとするとNetworkOnMainThreadExceptionが発生する)
また、Androidで画面のViewを操作しようと思ったら、UIスレッドで操作しないとエラーになる。
なので、

「ボタンクリックされたら通信Aを行い、その結果がOKだったら通信Bを行って、画面のTextViewにレスポンスを表示する」

といったことをやろうとすると、やたらとネストしたソースを書く必要が出てきて超見にくくて超面倒くさいソースを書くことになって、俺の怒りは爆発寸前だ。



あまりの怒りに乱文を記載してしまったが、とにもかくにもAndroid3.0以上でもUIスレッドと同期してHTTP通信を行えるクラス「Http.java」を作成してみた。(もちろん非同期通信にも対応)



【機能】
・Android3.0以上にも対応
・一般的な非同期通信用として「request」メソッドを実装する。
・同期通信用の「requestSync」メソッドを実装する。
・POSTパラメータには、文字と画像(jpeg)を指定可能。
・レスポンスは、文字と画像に対応。


【プログラム】
・Http.java(本体)
・StringResponseHandler.java(レスポンスが文字列の場合の受信用ハンドラ)
・BitmapResponseHandler.java(レスポンスが画像の場合の受信用ハンドラ)



【実装例1】文字列と画像を送信し、文字列を受信する。(非同期)

// 文字列と画像を送信し、文字列を受信する。
Http.Request request = new Http.Request();
request.url = "http://xxxxxxx.com/test1.php";
request.params.add(new Http.Param(Http.Param.TYPE_STRING, "id", "文字列"));
request.params.add(new Http.Param(Http.Param.TYPE_IMAGE, "img", "画像ファイルのパス"));
Http.request(request, new StringResponseHandler() {

@Override
public void onFinish(Http.Response response) {
// HTTPステータスコードが200
if (response.code == 200) {
// 受信した文字列を取得
String str = (String) response.value;
}
}
});



【実装例2】文字列と画像を送信し、画像を受信する。(非同期)

// 文字列と画像を送信し、文字列を受信する。
Http.Request request = new Http.Request();
request.url = "http://xxxxxxx.com/test1.php";
request.params.add(new Http.Param(Http.Param.TYPE_STRING, "id", "文字列"));
request.params.add(new Http.Param(Http.Param.TYPE_IMAGE, "img", "画像ファイルのパス"));
Http.request(request, new BitmapResponseHandler() {

@Override
public void onFinish(Http.Response response) {
// HTTPステータスコードが200
if (response.code == 200) {
// 受信した画像を取得
Bitmap image = (Bitmap) response.value;
}
}
});



【実装例3】文字列と画像を送信し、文字列を受信する。(同期)

// 文字列と画像を送信し、文字列を受信する。
Http.Request request = new Http.Request();
request.url = "http://xxxxxxx.com/test1.php";
request.params.add(new Http.Param(Http.Param.TYPE_STRING, "id", "文字列"));
request.params.add(new Http.Param(Http.Param.TYPE_IMAGE, "img", "画像ファイルのパス"));
Http.Response response = Http.requestSync(request, StringResponseHandler.getInstance());
// HTTPステータスコードが200
if (response.code == 200) {
// 受信した文字列を取得
String str = (String) response.value;
}



【実装例4】文字列と画像を送信し、画像を受信する。(同期)

// 文字列と画像を送信し、文字列を受信する。
Http.Request request = new Http.Request();
request.url = "http://xxxxxxx.com/test1.php";
request.params.add(new Http.Param(Http.Param.TYPE_STRING, "id", "文字列"));
request.params.add(new Http.Param(Http.Param.TYPE_IMAGE, "img", "画像ファイルのパス"));
Http.Response response = Http.requestSync(request, BitmapResponseHandler.getInstance());
// HTTPステータスコードが200
if (response.code == 200) {
// 受信した画像を取得
Bitmap image = (Bitmap) response.value;
}



非同期の場合は「request」、同期の場合は「requestSync」メソッドを使用する。
いずれも第2引数に受信用ハンドラを指定する。ハンドラは返されるデータの種類に適したものを選択する必要がある。

非同期の場合は、レスポンスが返されるとハンドラのonFinishメソッドが呼ばれる。
同期の場合は、レスポンスが返されるまで「requestSync」メソッドは終了せず、レスポンスが返ってくるとResponseオブジェクトを返して処理を続行する。





言っておくが俺は3流プログラマなのでバグとか色々あるかもしれない。
しかも、面倒なのでほとんど動作確認していない。
利用は自己責任で。

あと、httpmine-x.x.x.jarが必要。
http://hc.apache.org/downloads.cgiからHttpClientをダウンロードして解凍すればjarファイルが入ってると思うので、それをプロジェクトのlibフォルダにコピーしてくれ。

Http.java

package com.example.http;

import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;

import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.DefaultHttpClient;

import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;

public class Http {

/**
* リクエスト情報クラス
*/
public static class Request {
// リクエストURL
public String url = null;
// POSTパラメータ
public List<Param> params = new ArrayList<Param>();
// 保持オブジェクト
public Object keepObject = null;
}

/**
* レスポンス情報クラス
*/
public static class Response implements Serializable {

private static final long serialVersionUID = 1L;

// HTTPレスポンスコード
public Integer code = null;
// レスポンス内容
public Object value = null;
// リクエスト時の保持オブジェクト
public Object keepObject = null;
}

/**
* POSTパラメータクラス
*/
public static class Param {

public static final int TYPE_STRING = 1;
public static final int TYPE_IMAGE = 2;

private int type;
private String key;
private String value;

public Param(int type, String key, String value) {
this.type = type;
this.key = key;
this.value = value;
}
}

/**
* リクエスト実行クラス
*/
private static class HttpRequest extends AsyncTask<Void, Void, Void> {

// URL文字列
private String url = null;
// POSTパラメータ
private List<Param> params = new ArrayList<Param>();
// レスポンスハンドラ
private ResponseHandlerBase handler = null;
// 保持オブジェクト
private Object keepObject = null;
// レスポンスオブジェクト
private Response ret = null;
// interruptするスレッド
private Thread interruptThread = null;

@Override
protected Void doInBackground(Void... params) {

// URI構築
URI uri = null;
try {
uri = new URI(url);
} catch (URISyntaxException e) {
e.printStackTrace();
return null;
}

// GET/POSTリクエスト作成
HttpUriRequest request;
if (this.params != null && this.params.size() > 0) {
HttpPost r = new HttpPost(uri);
MultipartEntity entity = new MultipartEntity();
for (Param p : this.params) {
switch (p.type) {
case Param.TYPE_STRING:
// 文字パラメータの場合
try {
entity.addPart(p.key, new StringBody(p.value,
"text/plain", Charset.forName("UTF-8")));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
break;
case Param.TYPE_IMAGE:
// 画像パラメータの場合
entity.addPart(p.key, new FileBody(new File(p.value),
"image/jpg"));
break;
}
}
r.setEntity(entity);
request = r;
} else {
HttpGet r = new HttpGet(uri);
request = r;
}

// リクエストを実行
DefaultHttpClient httpClient = new DefaultHttpClient();
try {
httpClient.execute(request,
new org.apache.http.client.ResponseHandler<Void>() {

// HTTPレスポンスから,受信文字列をエンコードして文字列として返す
@Override
public Void handleResponse(HttpResponse response)
throws IOException {

if (ret == null) {
ret = new Response();
}

// ret = new Http.Response();
ret.code = response.getStatusLine()
.getStatusCode();
// 正常に受信できた場合は200
if (ret.code == 200) {
ret.value = handler
.createObjectFromResponse(response);
}

// 保持オブジェクトを継承
ret.keepObject = keepObject;

// スレッドが指定されている場合はinterrupt
if (interruptThread != null) {
interruptThread.interrupt();
}

return null;
}

});
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {

// 通信のshutdown
if (httpClient != null) {
try {
httpClient.getConnectionManager().shutdown();
} catch (Exception e) {
e.printStackTrace();
}
}
}

return null;

}

// タスク終了時
protected void onPostExecute(Void unused) {

// 受信結果をUIに渡すためにまとめる
Message message = new Message();
Bundle bundle = new Bundle();
bundle.putSerializable("http_response", ret);
message.setData(bundle);

// 受信結果に基づいてUI操作させる
handler.sendMessage(message);
}

}

/**
* レスポンス実行ベースクラス
*/
public static abstract class ResponseHandlerBase extends Handler {

// コンストラクタ
public ResponseHandlerBase() {
}

// リクエストタスク完了時にコールされる
public void handleMessage(Message msg) {
try {
onFinish((Response) msg.getData().get("http_response"));
} catch (Exception e) {
e.printStackTrace();
}
}

// レスポンス取得時にオブジェクトに変換するクラス
public abstract Object createObjectFromResponse(HttpResponse response);

// レスポンス完了時に呼ばれるクラス
public abstract void onFinish(Response response);

}

/**
* 同期リクエスト
*/
public static final Response requestSync(Request request,
ResponseHandlerBase handler) {

// レスポンス格納用
Response resp = new Response();

// リクエスト構築
HttpRequest httpReq = new HttpRequest();
httpReq.url = request.url;
httpReq.handler = handler;
if (request.params != null) {
httpReq.params.addAll(request.params);
}
httpReq.keepObject = request.keepObject;
httpReq.ret = resp;
httpReq.interruptThread = Thread.currentThread();

// リクエスト実行
httpReq.execute();

// レスポンスが返るまで待機
while (true) {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
// レスポンスが返った
break;
}
}

// レスポンスを返す
return resp;
}

/**
* 非同期リクエスト
*/
public static final void request(Request request,
ResponseHandlerBase handler) {

// リクエスト構築
HttpRequest httpReq = new HttpRequest();
httpReq.url = request.url;
httpReq.handler = handler;
if (request.params != null) {
httpReq.params.addAll(request.params);
}
httpReq.keepObject = request.keepObject;
httpReq.ret = null;
httpReq.interruptThread = null;

// リクエスト実行
httpReq.execute();
}

}


StringResponseHandler.java

package com.example.http.response;

import java.io.IOException;

import org.apache.http.HttpResponse;
import org.apache.http.ParseException;
import org.apache.http.util.EntityUtils;

import com.example.http.Http;
import com.example.http.Http.Response;

public abstract class StringResponseHandler extends Http.ResponseHandlerBase {

// 受信データをオブジェクトに変換
@Override
public Object createObjectFromResponse(HttpResponse response) {
try {
return EntityUtils.toString(response.getEntity(), "UTF-8");
} catch (ParseException e) {
} catch (IOException e) {
}
return null;
}

// 空のインスタンスを返す
public static final StringResponseHandler getInstance() {
return new StringResponseHandler() {
@Override
public void onFinish(Response response) {
}
};
}

}


BitmapResponseHandler.java

package com.example.http.response;

import java.io.IOException;

import org.apache.http.HttpResponse;
import org.apache.http.util.EntityUtils;

import android.graphics.BitmapFactory;

import com.example.http.Http;
import com.example.http.Http.Response;

public abstract class BitmapResponseHandler extends Http.ResponseHandlerBase {

// 受信データをオブジェクトに変換
@Override
public Object createObjectFromResponse(HttpResponse response) {
byte[] bin;
try {
bin = EntityUtils.toByteArray(response.getEntity());
} catch (IOException e) {
return null;
}
BitmapFactory.Options options = new BitmapFactory.Options();
return BitmapFactory.decodeByteArray(bin, 0, bin.length, options);
}

// 空のインスタンスを返す
public static final BitmapResponseHandler getInstance() {
return new BitmapResponseHandler() {
@Override
public void onFinish(Response response) {
}
};
}

}