Android 4.0 で、何も考慮せずに HTTP通信しようとすると強制停止されてできない。Android3.0 から
そうなってる。
3.0 以降でもHTTP通信を実行したければ、スレッドでHTTP通信しなければならない。
AsyncTask を使うのが常套手段と思ったが、あえて ExecutorService で書いてみた。
テスト用に HTTP-GET に対して、Wicket で JSON形式のデータを応答するものを用意する。
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.request.handler.resource.ResourceStreamRequestHandler;
import org.apache.wicket.request.resource.ContentDisposition;
import org.apache.wicket.util.resource.StringResourceStream;
import org.apache.wicket.protocol.http.servlet.ServletWebRequest;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
/**
* Userという任意クラスで、UserAgent とHTTP-GETの時刻をJSON形式で返す。
* JsonOutPage.html は、
* <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "">http://www.w3.org/TR/html4/loose.dtd">
* という1行だけの記述で用意する。
*/
public class JsonOutPage extends WebPage{
public JsonOutPage(){
GsonBuilder builder = new GsonBuilder();
builder.setDateFormat("yyyy/MM/dd HH:mm:ss");
String userAgent = *1.getHeader("User-Agent");
User user = new User(userAgent);
Gson gson = builder.create();
getRequestCycle().scheduleRequestHandlerAfterCurrent(
new ResourceStreamRequestHandler(new StringResourceStream(
gson.toJson(user),"application/json; charset=utf-8")
).setContentDisposition(ContentDisposition.ATTACHMENT)
);
}
}
public class User{
public String userAgent;
public Date date;
public User(String userAgent){
this.userAgent = userAgent;
this.date = new Date();
}
}
Wicket の Application クラスで、mount を書いて置く。
以下のようなメソッドを用意して、
private void mount(String mountPath, Class<? extends WebPage> pageClass){
getRootRequestMapperAsCompound().add(new MountedMapper(mountPath,pageClass));
}
Application クラスで、
mount("/jsontest",JsonOutPage.class);
とWebアプリの相対 url に上の Page クラスを指定する。
Webサーバーがこれで準備OK、、、次に Android.....
テストなので、ボタンクリックしたら、TextView に受信する JSON データを表示するものを
作ってみる。
java.util.concurrent.ExecutorService の submit 実行、Future でデータを受け取るように
してみる。したがって Callable の実装クラスで HttpClient を実行mJSON 形式の変換は、
Google の GSON に任せる。
import java.io.ByteArrayOutputStream;
import java.util.concurrent.Callable;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;
import android.net.http.AndroidHttpClient;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
/**
* JsonHttpGetter.java
* コンストラクタで、URL文字列と Type と一致する JSON データ格納クラスを指定する
*/
public class JsonHttpGetter<T> implements Callable<T>{
private String url;
private Class<T> type;
public JsonHttpGetter(String url,Class<T> type){
this.url = url;
this.type = type;
}
@Override
public T call() throws Exception{
String responseText = null;
AndroidHttpClient client = AndroidHttpClient.newInstance("Android");
HttpUriRequest httpUriRequest = new HttpGet(url) ;
HttpResponse response = client.execute(httpUriRequest);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
response.getEntity().writeTo(byteArrayOutputStream);
if (response.getStatusLine().getStatusCode()==HttpStatus.SC_OK){
responseText = byteArrayOutputStream.toString();
}
byteArrayOutputStream.close();
GsonBuilder builder = new GsonBuilder();
Gson gson = builder.create();
return gson.fromJson(responseText,this.type);
}
}
これを、OnClickListener の onClick の中で以下のように実行する
*2.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v){
TextView textView = (TextView)findViewById(R.id.textView1);
ExecutorService executorService = Executors.newSingleThreadExecutor();
// Web側と同じクラス User で指定する。
Future<User> future = executorService.submit(
new JsonHttpGetter<User>("http://xxx.xxx.xxx.xxx/sample/jsontest",User.class)
);
try{
User user = future.get();
SimpleDateFormat simpleFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
textView.setText("["+simpleFormat.format(user.date)+"]["+user.userAgent+"]");
}catch(InterruptedException e){
Log.d("sample",e.getMessage(),e);
}catch(ExecutionException e){
Log.d("sample",e.getMessage(),e);
}
}
});
TextView には、AndroidHttpClient の newInstance で指定した名前で
[yyyy/MM/dd HH:mm:ss][Android]
と表示される。JsonHttpGetter の
org.apache.http.client.HttpClient を使うと User-Agent の表示は、
Apache-HttpClient/UNAVAILABLE(java 1.4)
になるはずだ。
Android 2.3.3 でもこの ExecutorService の submit 実行、Future でデータを受け取る方法で
動いた。
ここで書いた JsonHttpGetter を見て、
public abstract AbstractHttpGet<T> implements Callable<T> {
public abstract T convert(String str);
:
:
のように書いて
call() メソッドは、AbstractHttpGet の中で実装して HTTP-GET の結果でconvertメソッドを
実行して call() メソッドの返り値にしても良いのではと考えた。