2014-05-03
Bolts-Androidで連続したHTTPリクエストをウォーターフォールにする
Facebook (Parse) が開発している Bolts-Android は asyn.js のように、ネストしたコールバック呼び出しをウォーターフォールで記述できるライブラリである。
コールバックのネストをウォーターフォールで記述できるというのもありがたいが、処理をTaskに分割することで、非同期リクエストを組み合わせて使うことも簡単になっている。
Boltsを使わない、ネストした一連のHTTPリクエストは以下のようになる。ここではandroid-async-httpを使った。
private void nestedRequests() { final TextView textView = (TextView) findViewById(R.id.body); textView.append("nested\n"); final long t0 = System.currentTimeMillis(); client.get(kApiBase, new RequestParams("q", "apple"), new AsyncHttpResponseHandler() { @Override public void onSuccess(int statusCode, Header[] headers, byte[] content) { assert statusCode == 200; // skip error handling textView.append("apple : " + new String(content) + "\n"); client.get(kApiBase, new RequestParams("q", "banana"), new AsyncHttpResponseHandler() { @Override public void onSuccess(int statusCode, Header[] headers, byte[] content) { assert statusCode == 200; // skip error handling textView.append("banana : " + new String(content) + "\n"); client.get(kApiBase, new RequestParams("q", "beef"), new AsyncHttpResponseHandler() { @Override public void onSuccess(int statusCode, Header[] headers, byte[] content) { assert statusCode == 200; // skip error handling textView.append("beef : " + new String(content) + "\n"); client.get(kApiBase, new RequestParams("q", "xxx"), new AsyncHttpResponseHandler() { @Override public void onSuccess(int statusCode, Header[] headers, byte[] content) { assert statusCode == 200; // skip error handling textView.append("xxx : " + new String(content) + "\n"); textView.append("elapsed (nested) : " + (System.currentTimeMillis() - t0) + "ms\n"); waterfallRequests(); } }); } }); } }); } }); }
これはREST APIを使うAndroidアプリだと決して誇張ではない。ネストそのものはメソッドを分割することで減らすことができるが、それだと呼び出し順が固定されるので柔軟性に乏しく本質的な改善とはいえない。
これが、Boltsをつかうと以下のようになる。まず、非同期リクエストを呼んで Task<T>
を生成するメソッドをつくる。この Task は Future と概念的には同じで、生成された当初は空で非同期処理が終わるって中身がセットされると状態が変化するコンテナのようなオブジェクトである。このTaskを continueWith() / continueWithTask() でつなげて実行すれば、非同期リクエストをウォーターフォールで実行できるというわけだ。
private Task<String> getApiAsync(String word) { final Task<String>.TaskCompletionSource taskSource = Task.create(); RequestParams params = new RequestParams(); params.put("q", word); client.get(this, kApiBase, params, new AsyncHttpResponseHandler() { @Override public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) { String s = new String(responseBody); if (statusCode == 200) { taskSource.setResult(s); } else { taskSource.setError(new HttpResponseException(statusCode, s)); } } }); return taskSource.getTask(); } private void waterfallRequests() { final TextView textView = (TextView) findViewById(R.id.body); textView.append("waterfall\n"); final long t0 = System.currentTimeMillis(); getApiAsync("apple").continueWithTask(new Continuation<String, Task<String>>() { @Override public Task<String> then(Task<String> task) throws Exception { textView.append("apple : " + task.getResult() + "\n"); return getApiAsync("banana"); } }).continueWithTask(new Continuation<String, Task<String>>() { @Override public Task<String> then(Task<String> task) throws Exception { textView.append("banana : " + task.getResult() + "\n"); return getApiAsync("beef"); } }).continueWithTask(new Continuation<String, Task<String>>() { @Override public Task<String> then(Task<String> task) throws Exception { textView.append("beef : " + task.getResult() + "\n"); return getApiAsync("xxx"); } }).continueWith(new Continuation<String, Void>() { @Override public Void then(Task<String> task) throws Exception { textView.append("xxx : " + task.getResult() + "\n"); textView.append("elapsed (serial): " + (System.currentTimeMillis() - t0) + "ms\n"); return null; } }); }
これなら見通しがいいし、順番を入れ替えることも簡単にできる。
- 20 http://t.co/1AcHGvTMOs
- 6 http://pipes.yahoo.com/pipes/pipe.info?_id=e4c70514b5136c08ae93591f390be2e2
- 2 http://b.hatena.ne.jp/
- 2 http://b.hatena.ne.jp/bookmarklet?url=http://d.hatena.ne.jp/gfx/20140503/1399127545&btitle=Bolts-Androidで連続したHTTPリクエストをウォー?%8
- 2 http://feedly.com/index.html
- 2 http://longurl.org
- 1 http://b.hatena.ne.jp/entry/d.hatena.ne.jp/gfx/20140406/1396770464
- 1 https://www.google.co.jp/