前回、非同期通信を行う為のAsyncTaskについての記事を書いたのですが、
今回はネットワーク通信用ライブラリ:Volleyについての備忘録です。
Google I/O 2013 : Volley: Easy, Fast Networking for Android
公式リポジトリ : https://android.googlesource.com/platform/frameworks/volley/
Volleyのライブラリを利用するためには、Gitを使ってプロジェクトを取得し、Androidプロジェクトとしてインポートする必要があります。
1 |
git clone https://android.googlesource.com/platform/frameworks/volley |
サーバーに画像をアップロードさせたいので、今回も multipart の場合についてです。
RequestQueue
リクエストを管理するキューは原則として1つだけとのことなので、シングルトンで生成します。
Volley.newRequestQueueの第2引数にHurlStackインスタンスを渡す際にSSLSocketFactoryを指定できるので、デバッグ時にはテスト用のSSLSocketFactoryを指定できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public class VolleyHelper { private static final Object sLock = new Object(); private static RequestQueue sRequestQueue; /* * RequestQueueのシングルトン生成 */ public static RequestQueue getRequestQueue(final Context context) throws IOException { synchronized(sLock) { if ( sRequestQueue == null ) { sRequestQueue = Volley.newRequestQueue(context, new MyHurlStack(null, MySSLSocketFactory.getSSLContext().getSocketFactory())); } return sRequestQueue; } } } |
<参考>
・[Android Developers] Setting Up a RequestQueue
Request
Multipart用のRequestクラスです。
サーバーからのレスポンスをJSONで受信するので、JsonRequestクラスを継承しています。
マルチパート形式に対応するためには、以下のライブラリの追加が必要です。
・httpmime-x.x.x.jar
http://hc.apache.org/downloads.cgi ⇒ HttpClient x.x.x (GA) の項目よりダウンロード
(今回私は、httpmime-4.3.3.jarとhttpcore-4.3.2.jarを追加しました。)
※buildMultipartEntity()メソッド内でboundaryの設定を行っていますが(22行目)、
設定が無い場合には MultipartEntityBuilder側でgenerateBoundary()で生成されます。
ただ、何が悪いのかまだ不明ですが、boundaryを設定しないと、サーバー側に渡されるパラメータが不正なものになってしまい、処理が失敗するので、boundaryを設定しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
/* * Multipart用のRequestクラス */ public class MultipartRequest extends JsonRequest<JSONObject> { private final Map<String, String> mStringParts; private final Map<String, File> mFileParts; private MultipartEntityBuilder mBuilder = MultipartEntityBuilder.create(); public MultipartRequest(String url, Map<String, String> stringParts, Map<String, File> fileParts, Response.Listener<JSONObject> listener, Response.ErrorListener errorListener) { super(Method.POST, url, null, listener, errorListener); mStringParts = stringParts; mFileParts = fileParts; buildMultipartEntity(); } private void buildMultipartEntity() { mBuilder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE); mBuilder.setBoundary("_____"+Long.toString(System.currentTimeMillis())+"_____"); mBuilder.setCharset(Consts.UTF_8); for ( Map.Entry<String, String> entry : mStringParts.entrySet() ) { mBuilder.addTextBody(entry.getKey(), entry.getValue()); } for ( Map.Entry<String, File> entry : mFileParts.entrySet() ) { ContentType imageContentType = ContentType.create("image/jpeg"); mBuilder.addBinaryBody("upfile", entry.getValue(), imageContentType, entry.getKey()); } } @Override public String getBodyContentType() { return mBuilder.build().getContentType().getValue(); } /* @Override public byte[] getBody() { ByteArrayOutputStream bos = new ByteArrayOutputStream(); try { mBuilder.build().writeTo(bos); } catch (IOException e) { e.printStackTrace(); } return bos.toByteArray(); } */ public HttpEntity getEntity() { return mBuilder.build(); } @Override protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) { try { String jsonString = new String(response.data, HttpHeaderParser.parseCharset(response.headers)); return Response.success(new JSONObject(jsonString), HttpHeaderParser.parseCacheHeaders(response)); } catch (UnsupportedEncodingException e) { return Response.error(new ParseError(e)); } catch (JSONException je) { return Response.error(new ParseError(je)); } } } |
Multipart用HurlStack
Multipartの場合だけ独自の処理を行うようにしたHurlStakを継承したクラスを作成します。
Out of memoryを避けるために、MultipartRequestクラスでMultipartEntityを返すメソッド(getEntity())を実装し、そのメソッドを使ってMultipartEntityを設定します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
public class MyHurlStack extends HurlStack { private static final String HEADER_CONTENT_TYPE = "Content-Type"; public MyHurlStack(UrlRewriter urlRewriter, javax.net.ssl.SSLSocketFactory sslSocketFactory) { super(urlRewriter, sslSocketFactory); } @Override public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders) throws AuthFailureError, IOException { if ( ! (request instanceof MultipartRequest) ) { return super.performRequest(request, additionalHeaders); } HttpPost httpRequest = new HttpPost(request.getUrl()); httpRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType()); setMultipartBody(httpRequest, request); addHeaders(httpRequest, additionalHeaders); addHeaders(httpRequest, request.getHeaders()); HttpParams httpParams = httpRequest.getParams(); int timeoutMs = request.getTimeoutMs(); HttpConnectionParams.setConnectionTimeout(httpParams, 5000); HttpConnectionParams.setSoTimeout(httpParams, timeoutMs); SchemeRegistry registry = new SchemeRegistry(); registry.register(new Scheme("http", new PlainSocketFactory(), 80)); registry.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443)); ThreadSafeClientConnManager manager = new ThreadSafeClientConnManager(httpParams, registry); HttpClient httpClient = new DefaultHttpClient(manager, httpParams); return httpClient.execute(httpRequest); } private void addHeaders(HttpPost httpRequest, Map<String, String> headers) { for ( String key : headers.keySet() ) { httpRequest.setHeader(key, headers.get(key)); } } private static void setMultipartBody(HttpEntityEnclosingRequestBase httpRequest, Request<?> request) throws AuthFailureError { if ( request instanceof MultipartRequest ) { httpRequest.setEntity(((MultipartRequest) request).getEntity()); } } } |
リクエスト発行
MultipartRequestを使用したリクエストを発行します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
Context context = getActivity().getApplicationContext(); RequestQueue rQueue = VolleyHelper.getRequestQueue(context); Map<String, String> stringParts = new HashMap<String, String>(); stringParts.put("key", "value"); Map<String, File> fileParts = new HashMap<String, File>(); File file = new File("imagefile.jpg"); fileParts.put("file", file); MultipartRequest postRequest = new MultipartRequest("https://example.com/fileupload.php", stringParts, fileParts, new Listener<JSONObject>() { @Override public void onResponse(JSONObject jsonResponse) { } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { } }); rQueue.add(postRequest); rQueue.start(); |
<参考にしたサイト>
・FLY1TKG BLOG 「Volleyでmultipart/form-dataを送信する」