Yahoo!のWebAPIをAndroidから使ってみよう
演習の概要
Web2.0といわれて久しいですが、最近はあまりこの言葉を聞くこともなくなってきました。おそらく、あまりに浸透しすぎて、アピールしない言葉となってしまったからでしょう。さまざまなネットサービスが、WebAPIを提供していますが、今回は、Yahoo!のショッピングAPIをAndroidから使う演習をし ます。
- Yahoo!ショッピングAPI http://developer.yahoo.co.jp/webapi/shopping/
■ 完成イメージ
完成イメージは、下の図のようなものです。商品のバーコードをAndroidのカメラ機能を使って読み取り、その値をショッピングAPIに送り、戻り値として商品 名を取得します。
図 完成イメージ
ちなみに、画面イメージは以下のようなものです。上のボタンを押して、バーコードを読み取り、EditTextに書き込み、その値をショッピングAPIに送りま す。ということで、とても単純です。
図 画面イメージ
(1)バーコード読取部の作成
バーコードの読み取り自体は、他のアプリケーションを利用します。ここでは、ZxingのQRコードリーダーを利用します。使用するAndroid端末にインス トールしていない場合には、GooglePlayからインストールしておいてください。
動作は簡単です。ボタンを押したときに、intentを使ってQRコードリーダーを呼び出して、戻り値として、読み取り値であるバーコードの値を受け取るだけです。
では、まず、新規のAndroidプロジェクトを作成し、ボタン、EditText、TextViewを配置してみます。今回は、LinearLayoutにそのまま配置しましょう。プロジェクトは、YahooShopAPIとしておきます。パッケージ名は適宜つけてください。下に layoutとstringの該当部分を記載します。
string.xmlも変更します。
WebAPIを利用した商品検索 Settings バーコード読取 商品検索
では、Androidのプログラムに入りましょう。まず、今回使用する画面上のコンポーネント4つをクラス変数としてMainActivity classに定義しておきます。
public class MainActivity extends Activity { private Button b1; private Button b2; private TextView tv; private EditText ed; (以下略)
つづいて、最初に起動されるonCreateメソッドの中で、これらのコンポーネントを画面上のコンポーネントと結び付けます。
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); b1 = (Button)findViewById(R.id.btn1); b2 = (Button)findViewById(R.id.btn2); tv = (TextView)findViewById(R.id.tv1); ed = (EditText) findViewById(R.id.et1); (以下略)
次に、バーコード読取ボタンを押下した際の処理を書きましょう。ボタンを押したときの処理は、View.onClickListenerを使用することで実現できます。実装の仕方は、2種類あるようなのですが、ここでは、onClickメソッドを作らない方法で実装します。ボタンが押されたら、インテントとして、今回使用するZxingを呼び出して、戻り値をstartActivityForResultメソッドを利用して取得するという流れになります。
また、ここでは、Zxingがない場合には、Toastを表示して、警告を出すようにします。また、インテントを呼び出した先で、分岐処理ができるように、呼び出し先でコードを指定します。ここでは、固定値として、REQUEST_CODE_ITEMを使用します。この変数についても、MainActivity.classに定義しておきます。
b1.setOnClickListener(new View.OnClickListener(){ public void onClick(View v) { // バーコードリーダーとして、zxingのバーコードリーダーを使用する Intent intent = new Intent("com.google.zxing.client.android.SCAN"); try{ startActivityForResult(intent, REQUEST_CODE_ITEM); }catch (ActivityNotFoundException e){ Toast.makeText(MainActivity.this, "not found Barcode Scanner", Toast.LENGTH_SHORT ).show(); } } });
このコードを書くと、いくつかライブラリが足りないと警告が出ると思いますが、Eclipseの指示に従って、必要なライブラリをインポートしておきます。Zxingからバーコードを読み取るためには、startActivityForResultをよぶだけでは十分ではなく、もう一つ、実際に値を取得する、onActivityResultメソッドを作成する必要があります。引数は、int型のリクエストコード用の変数、int型のリザルトコード用の 変数、そして、Intent型変数です。ここで、リクエストが、先の値のとおりで、読み取った結果がOKの場合に、結果を取得(getStringExtraメソッド)を呼んで、その結果をEditTextに表示することとします。
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { switch(requestCode){ case REQUEST_CODE_ITEM: if(resultCode == Activity.RESULT_OK){ String bId; // 読み取りバーコード値 bId = data.getStringExtra("SCAN_RESULT"); ed = (EditText) findViewById(R.id.et1); ed.setText(bId); } break; default: break; }
さて、ここまで、バーコードの読み取りはできたはずです。実際に、バーコードを読んでみましょう。
図 読み取り画面
図 読み取り結果
確かに読み取れましたね。
(2)Yahoo!ショッピングAPIを利 用して商品名を取得する部分の作成
では、続いて、WebAPIを使用する部分を作成しましょう。まず、Yahoo!デベロッパーネットワークにアクセスして、WebAPIを使用するための登録をし ます。
ここでは、登録についての詳細は割愛します。アクセスするために必要なアプリケーションIDを取得しておいてください。
ここではいくつか新しいことを理解する必要があります。まず、ウェブにアクセスするやり方、つまり、HTTP通信の仕方です。今回作成するAndroidアプリケーションは画面操作を中心に動くアプリケーションですが、通信の部分は画面の動きとは、同期しないで動かす必要があります。このように、画面と処理を非同期で動かしたいときには、AsyncTaskというクラスを使用します。そこで、今回は、AsyncTaskクラスを拡張したWebTaskとい うクラスを新たに作成します。
WebTaskの仕事は、HTTP通信をして、YahooショッピングAPIから、一連のデータを受け取り、必要な文字列(商品名)を画面に表示するというものです。AsyncTaskでは、タスクに引数を送ることができます。そして、処理としては、少なくとも、非同期に行う処理(doInBackground)と、終了したときの処理(onPostExecute)を記述する必要があります。そして、doInBackground終了したときの結果の値を、onPostExecuteに引き渡すこともできます。
ここでは、WebTaskに渡す文字列として、WebAPIのURLを、そして、doBackgroundで取得した結果をonPostExecuteに渡して、画面に表示するようにしましょう。WebTask.javaをMainActivity.javaと同じパッケージの中に作成します。そして、WebTaskクラスを次のようにします。importなどは省略していますが、適宜追加してください。
public class WebTask extends AsyncTask<String, Void, String>{ private TextView tv; // コンストラクター public WebTask(TextView tv){ super(); this.tv=tv; } @Override protected String doInBackground(String... params){ String uri=params[0]; // 引数の取得 String str=""; // 文字列定義 return str; // 名前もしくは、エラーを戻す } // 終了時の処理 protected void onPostExecute(String str){ tv.setText(str); // textViewに読み取り結果を表示する } }
AsyncTask<String, Void, String>{ とありますが、最初のStringは引数として受け取るのが文字列(URL)であること、3つ目のStringは、onPostExecuteに渡す変数が文字列であることを示しています。また、コンストラクターに、TextViewを引数としてもたせています。こうすることで、起動側のMainActivityクラス側のTextViewのインスタンスを引き継ぐことができます。ここでは、何も処理をしていませんが、ここまで正常に動作するかを確認するために、呼び出し側(MainActivity.java)の処理を書いて見ましょう。このときに、MainActivity側にWebTaskのインスタンスを作成することを忘れないようにしてください。
WebTask task;
作成するコードの方は以下のようになります。
b2.setOnClickListener(new View.OnClickListener(){ public void onClick(View v) { try{ String uri = "http://hogehoge.co.jp"; task=new WebTask(tv); task.execute(uri); }catch(Exception e){ // エラー発生時 Toast.makeText(MainActivity.this, "取得エラー", Toast.LENGTH_LONG).show(); } } });
さて、何も起こりませんが、エラーなく動いたでしょうか。では、次に、呼び出し側でURLを作成してみます。URLはUriクラスを使用します。Yahooショッ ピングAPIのところに、使用方法が書いてありますが、基本的には、
- URL+appid + クエリ
です。ここで、どうやって、返事をもらうかを考えねばなりません。xml形式でもよいのですが、それよりも処理が軽い、jsonをここでは利用します。ということ で、URLは、
- http://shopping.yahooapis.jp/ShoppingWebService/V1/json/itemSearch
になります。ただし、実際には、Uriクラスの作法に従って作成します。ここでは、バーコードデータをEditTextから取得しましょう。もし、空の場合には、TextViewに何もないと表示するようにします。いかに、onClickの中だけ書き換えたものを示しておきます。appidは自分のものを使ってください。
public void onClick(View v) { try{ String bId=ed.getText().toString(); if(!bId.equals("")){ String scheme = "http"; String authority = "shopping.yahooapis.jp"; String path = "/ShoppingWebService/V1/json/itemSearch"; Uri.Builder uriBuilder = new Uri.Builder(); uriBuilder.scheme(scheme); uriBuilder.authority(authority); uriBuilder.path(path); uriBuilder.appendQueryParameter("appid", "自分のappid"); uriBuilder.appendQueryParameter("jan", bId); String uri = uriBuilder.toString(); task=new WebTask(tv); task.execute(uri); }else{ tv.setText("バーコードを読み取ってください。"); } }catch(Exception e){ // エラー発生時 Toast.makeText(MainActivity.this, "取得エラー", Toast.LENGTH_LONG).show(); } }
さて、これで、呼び出し側はできました。呼ばれる側のWebTask.javaに戻ります。
呼ばれる側では、HTTP通信処理と、JSONの処理をしなければなりませんので、結構大変です。まずは、HTTP処理のほうを行います。HTTPのGET通信をする際には、HttpGETクラス、HttpResponseクラス、HttpClientクラスを用いて以下のように書きます。また、書き忘れましたが、
- uri = params[0];
の部分で、呼び出し側の引数を取り出します。また、インターネット通信をするためには、Manifestに以下の許可を追加する必要があります。
<uses-permission android:name="android.permission.INTERNET"/>
HTTP通信の処理を加えた非同期処理は以下のようになります。
@Override protected String doInBackground(String... params){ String uri=params[0]; // 引数の取得 String str=""; // 文字列定義 // http通信用 HttpGet request= new HttpGet(uri); HttpResponse httpResponse=null; HttpClient httpClient = new DefaultHttpClient(); try{ httpResponse=httpClient.execute(request); }catch(Exception e){ str=e.toString(); } (後省略)
これで、httpResponseにWebAPIからの結果が入ってくるのですが、HTTP通信の場合には、通信がOKかNGかの値も送られてきますので、それによって分岐処理を書くのが一般的です。ここでは、ひとまず、OKだったら、TextViewに「HTTP通信OK」と表示し、だめなら、「HTTP通信エラー」を書くことにしましょう。
// HTTPの結果取得 int status = httpResponse.getStatusLine().getStatusCode(); if(HttpStatus.SC_OK==status){ // 結果がOKのとき try{ str="HTTP通信OK"; }catch(Exception e){ str=e.toString(); } }else{ str="HTTP通信エラー"; }
図 通信確認
このように表示されたでしょうか。
次がいよいよJSONの処理です。JSONの細かい説明は、他の解説書を参照してください。ここでは、httpResponseの中身をByteArrayOutputStreamに読み取り、それを文字列に表示するという方法で、まずは、受け取ったJSON形式のデータを取得します。
JSON形式のデータを見てみるために、まずは、以下のようにしてみましょう。
// 戻ってきた結果を文字列(JSON形式)をパースして名前を取り出す ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); httpResponse.getEntity().writeTo(outputStream); String data; data=outputStream.toString(); str=data; // とりあえず、JSONを見てみる
図 戻り値
と、このような感じで、何かわけのわからない文字が表示されたと思います。でも、よく見ると、ResultSetとか、Resultとか書いてあるように、何かの結果が返ってきていることがわかります。実際の構造は、以下のページのレスポンスフィールドという項目を見ると書いてあります。
ここからは、上記のウェブなどを参照しながら商品名を取り出すところなのですが、上記のウェブを見る限り、
- ResultSet > Result > Hit > Name
で、商品名が取り出せるはずなのですが、なぜか、そのようにやっても取り出せませんでした。これは、繰り返しデータを取り出すときには、JSONArrayを使用すればよいはずなのですが(Hit以下)、現在のレスポンスでは、key番号という番号が入っていて、JSONArrayではうまく取り出せないようでした。そこで、苦肉の策として、key番号の最初の0をノードとみなして、以下のような処理を書きました。無論、これは正しい取り 出し方ではないと思います。正しい取り出し方が、わかる人がいたら教えてください。
// 戻ってきた結果を文字列(JSON形式)をパースして名前を取り出す ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); httpResponse.getEntity().writeTo(outputStream); String data; data=outputStream.toString(); JSONObject rootObject = new JSONObject(data); JSONObject resultsetObject = rootObject.getJSONObject("ResultSet"); JSONObject result0Object = resultsetObject.getJSONObject("0"); // 便宜上 JSONObject resultObject = result0Object.getJSONObject("Result"); JSONObject itemObject = resultObject.getJSONObject("0"); // 便宜上 str=itemObject.getString("Name");
では、ここまで作成したところで、もう一度動かしてみましょう。
図 検索結果
さて、商品名が呼び出せたでしょうか。
(この例では、一本のペットボトルを読んだのに、24本と出ました…データの登録ミスでしょうか??)
(ページの先頭へ)