ASP.NET上のWebサービスをAndroidから使ってみるテスト。
いろいろハマったので、メモ。
で、Android側はksoap2-android 2.5.2を使用します。
■ksoap2-androidの準備
こちらからjarをダウンロードして、Javaのビルドパスに追加
http://code.google.com/p/ksoap2-android/
■インターネット接続を許可
AndroidManifest.xmlに以下の行を追加。
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
■Webサービス側の実装(C#)
[WebMethod]
public SearchResult SearchProduct(SearchCondition searchCondition);
こんなメソッドをAndroidから呼び出してみる。
■AndroidからSOAPでWebサービスを実行するサンプル(Java)
private static final String SOAP_ACTION = "http://tempuri.org/SearchProduct";
private static final String METHOD_NAME = "SearchProduct";
private static final String NAMESPACE = "http://tempuri.org/";
private static final String URL = "http://yourdomain/path/AndroidService.asmx";
private static final String SEARCH_CONDITION = "SearchCondition";
private static final String DEBUG = "debug";
public SearchProduct(SearchCondition condition) {
try {
SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME);
// 呼出し元のコードを掲載するのがメンドイので、ここで設定しちゃってます
condition = new SearchCondition(1,"","TEST",0,10,1);
// メソッドの引数を設定
// request.addProperty(SEARCH_CONDITION, condition);
request.addProperty("searchCondition", condition);
SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
envelope.dotNet = true;
envelope.setOutputSoapObject(request);
envelope.addMapping(NAMESPACE, SEARCH_CONDITION, new SearchCondition().getClass());
// AndroidHttpTransportは非推奨
HttpTransportSE transport = new HttpTransportSE(URL);
transport.call(SOAP_ACTION, envelope);
SoapObject searchResult = (SoapObject)envelope.getResponse();
} catch (Exception e) {
Log.v(DEBUG, e.getMessage());
}
}
WebMethodの引数となるSearchConditionオブジェクトは、こんな感じ。
public class SearchCondition implements KvmSerializable {
private int searchDiv;
private String categoryCode;
private String searchKeyword;
private int rankID;
private int pageSize;
private int pageIndex;
public SearchCondition(){}
public SearchCondition(int searchDiv, String categoryCode, String searchKeyword, int rankID, int pageSize, int pageIndex) {
this.searchDiv = searchDiv;
this.categoryCode = categoryCode;
this.searchKeyword = searchKeyword;
this.rankID = rankID;
this.pageSize = pageSize;
this.pageIndex = pageIndex;
}
// KvmSerializableで要求されるインターフェイスを実装
public Object getProperty(int arg0) {
switch(arg0) {
case 0:
return searchDiv;
case 1:
return categoryCode;
case 2:
return searchKeyword;
case 3:
return rankID;
case 4:
return pageSize;
case 5:
return pageIndex;
default:
return null;
}
}
public int getPropertyCount() {
return 6;
}
public void getPropertyInfo(int index, Hashtable arg1, PropertyInfo info) {
switch(index) {
case 0:
info.type = PropertyInfo.INTEGER_CLASS;
info.name = "SearchDiv";
break;
case 1:
info.type = PropertyInfo.STRING_CLASS;
info.name = "CategoryCode";
break;
case 2:
info.type = PropertyInfo.STRING_CLASS;
info.name = "SearchKeyword";
break;
case 3:
info.type = PropertyInfo.INTEGER_CLASS;
info.name = "RankID";
break;
case 4:
info.type = PropertyInfo.INTEGER_CLASS;
info.name = "PageSize";
break;
case 5:
info.type = PropertyInfo.INTEGER_CLASS;
info.name = "PageIndex";
break;
}
}
public void setProperty(int index, Object value) {
switch(index) {
case 0:
this.searchDiv = Integer.parseInt(value.toString());
break;
case 1:
this.categoryCode = value.toString();
break;
case 2:
this.searchKeyword = value.toString();
break;
case 3:
this.rankID = Integer.parseInt(value.toString());
break;
case 4:
this.pageSize = Integer.parseInt(value.toString());
break;
case 5:
this.pageIndex = Integer.parseInt(value.toString());
break;
}
}
}
戻り値の処理は端折っていますが、メソッドのパラメータや戻り値がプリミティブな型ではなく、クラスだったりすると、かなり面倒臭くなります。普通にRESTなインターフェイスのほうがいいですね。

コメント
すみません、質問です。
本ページを参考にWCFサービスとAndroidでのコードをくんでみました。
そこで戻り値のクラスはデータが設定されて来るのですが、「Webサービス側の実装(C#)」の「searchCondition」に当たるクラスがnullしか渡されないのです。
何かクラスに対して属性の設定が必要なのでしょうか?
確かに、このソースだけで動かしみると、nullになりますね。申し訳ございません…。
transport.debug = true;
を指定して、call後にtransport.requestDumpを出力してみたりしてみましたが、問題になりそうな点が見つからないので、ちょっと調べてみます。
JAVA側のSEARCH_CONDITIONの定義を "SearchCondition"から"searchCondition"に変えると動作が変わりませんか?
こちらでは動作がNULLが渡るから、シリアライズのエラーに変わりました。
nullになる理由を考えていました
推測ですがWEBサービスのメソッド引数として¥"SearchCondition¥"が指定されていますが
JAVA側データをデシリアライズしたデータに一致する物がない為、null状態になるのではないでしょうか。
そう考えるとC#側の「SearchCondition 」クラスにKvmSerializable相当の機能を実装しないと
KSOAPからデータを受け取れないのではないでしょうか。
少し、掲載したソースを修正しました。修正箇所は2箇所です。
1)addPropertyのプロパティ名の先頭を小文字に。
request.addProperty("searchCondition", condition);
2)引数のマッピングを追加
envelope.addMapping(NAMESPACE, SEARCH_CONDITION, new SearchCondition().getClass());
これで、サーバー側に値が渡ることを確認できました。
…といっても、やはり面倒なので、新しく作成したサーバー側のメソッドの引数渡しはプリミティブな型にしちゃっています。
ちなみに、addPropertyの名前ですがWSDLを見たときに
<s:element minOccurs="0" maxOccurs="1" name="searchCondition" type="tns:SearchCondition"/>
となっていたので、そのname属性に合わせて直しています。
addMappingに追加ついては、黒犬さんのご指摘の通りです。これをやらないとenvelopeからSearchConditionのクラス、メンバーの型情報が抜け落ちてしまっていました。
動かないソースを垂れ流して申し訳ありません。ご指摘ありがとうございました。
早速試してみます、対応ありがとうございました。
こちらの修正したコードで試してみたところ、シリアライズでエラーなっているようです。
こういったエラーが出た場合は何処を疑えば良いでしょうか
ご存知でしたら教えて頂けないでしょうか。
・エラーログより
操作 ‘メソッド名’ の要求メッセージの本文をシリアル化解除しているときにエラーが発生しました。
OperationFormatter で、無効なメッセージ本文が検出されました。
名前が ‘メソッド名’ で名前空間が ‘http://10.0.2.2:3756/Service.svc’ のノード型 ‘Element’ が
検出される必要がありますが、名前が ‘メソッド名’ で
名前空間が ‘http://10.0.2.2:3756/Service.svc/’ のノード型 ‘Element’ が検出されました。
サンプルのソースはWCFサービスではないので、詳しい原因はわかりませんが、ReadQuotaに引っかかったり(メッセージが途中で切れている)していないでしょうか?
WCFサービスだとサーバー側のconfigを設定するだけで簡単にログが取れるので、受信しているメッセージのログを取ってみると原因を特定しやすいと思います。