読者です 読者をやめる 読者になる 読者になる

Hachirog

都内で働いているWebエンジニアのブログです

Androidのアプリ内課金を試してはまる

近況

職場が変わり、Javaの開発から離れ、最近はもはやWebサイトの開発の業務さえ なくなってしまいました。さみしい。

そんな中Androidのネイティブアプリの開発案件が。 Javaの開発経験者だからということで振られたものの、アプリ開発の実業務は今回が初になります。とりあえずやってみたんですが、課金アイテムの購入を試そうとしてドハマりしてしまいました。

結論としてはドキュメントをちゃんと読みましょう、という話なんですが はまったポイントを書き残したいと思います。

課金アイテムの購入ってテストできるのか

アプリ内課金を実装して試している開発段階で、実際の請求が発生したらたまりません。 課金をテストできる仕組みはやはりちゃんとあるみたいですね。

http://developer.android.com/google/play/billing/billing_testing.html

テストのやり方には2つの方法があるようで

  • 開発中のアプリをalpha/beta版に登録する
  • Static Responsesを使う

前者の方法だとアプリ登録の手間があるので、手軽ではありません。

Static Responsesを利用する方法ではGoogle Play側で用意されている テスト用のプロダクトIDをリクエストします。まず課金の実装を試すなら こちらがおすすめとドキュメントにも書いてあります。

アプリ内課金を試す

実装方法の説明はこちら

http://developer.android.com/google/play/billing/billing_integrate.html

ドキュメントに沿って開発していたんですが、所々つまづいてしまい・・・

AIDLの追加

IInAppBillingService.aidlを追加せよとあります。 AIDLファイルというものは今回お初にお目にかかったのですが、 調べてみるとプロセス間通信を実現するためのものとか。

そしてプロジェクトへの追加の仕方が独特

  1. SDK ManagerでGoogle Play Billing Libraryをダウンロード
  2. IInAppBillingService.aidlファイルをコピー
  3. プロジェクト内にパッケージ(com.android.vending.billing)をつくる
  4. コピーしたファイルをペースト
  5. ビルドする(IInAppBillingService.javaができる)

依存関係を追記すればOK、というわけにはいかないんですね。 しかもドキュメントの手順説明がEclipse向けに書いてあって少し戸惑いました。 (正式サポートがきれるっていう話だからAndroid Studioで開発したのに・・・。 少し勝手が違いましたが、Android Studioでも組み込めました)

実装する。そしてはまる

ドキュメントに実装例が記載されています。 そちらにしたがって実装、エミュレータで実行してみたものの NullPointerExceptionが発生してしまい、うまくいきません。 実装をチェックしても間違っていそうな箇所はなく 原因を検索してみたものの、これといった情報にはヒットせず。

IInAppBillingService mService;

ServiceConnection mServiceConn = new ServiceConnection() {
   @Override
   public void onServiceDisconnected(ComponentName name) {
       mService = null;
   }

   @Override
   public void onServiceConnected(ComponentName name,
      IBinder service) {
       mService = IInAppBillingService.Stub.asInterface(service);
   }
};

どうもonServiceConnectedが呼び出されず、mServiceがnullのまま。 StackOVerflowにも同じ悩みを抱えている方がいるようですが、 回答の内容を試してみたものの結局解決にいたりませんでした。

http://stackoverflow.com/questions/10635512/android-in-app-billing-null-pointer-exception

半日くらい右往左往していたところ、公式ドキュメントをよく読むとこんな記述が!

To test In-app Billing in an application you must install the application on an Android-powered device.

アプリ内課金は実機でテストしないとダメでした・・・。 エミュレータではテストできません!

ドキュメントをよく読んでなかった私が悪いのですが、 「アプリ内課金はエミュレータでテストできません」とか エラーメッセージが出てくれたら嬉しいんだけどなあ。

  • USBケーブル経由で実機にアプリを入れる
  • 実機には開発者アカウントまたはテストアカウントでログインする
  • 実機のGoogle Play Serviceのバージョンを5.0.12以上にする

これで、サンプルアプリがNullPointerExceptionで落ちずに めでたくアイテム購入のダイアログが出てきました。

Unexpected response code 500 が出たので待つ

ひとまず購入処理をひと通り試すことができました。 安心したところでリファクタリングし、そして再度購入処理を試しました。 しかし失敗。

コンソールには

BasicNetwork.performRequest:Unexpected response code 500

と出ています。 500というとInternal Server Errorですね。 Google Play側のサーバが不調なのか、ということで待機。 結局回復せず、続きは翌日に持ち越しました。 そして日をまたいでからまた試してみるも失敗。

ここまでサーバの調子が悪いのはおかしい、ということで調べてみるとこんな記述が。

http://developer.android.com/google/play/billing/api.html#consume

Once an in-app product is purchased, it is considered to be "owned". In-app products in the "owned" state cannot be purchased from Google Play.

一度購入したアイテムは消費処理をしないと、再度購入することができないようです そこでconsumePurchaseメソッドを実行してから、 アイテムを再度購入する、という処理にしてみると無事成功!

これもドキュメントを読んでいなかった私が悪いんですが、 そもそもこのレスポンスコード500って何なんですかね・・・。 400(Bad Request)とか403(Forbbiden)あたりだったら早く原因調査に乗り出したのに。

 まとめ

  • Androidの課金テストには実機が必要。エミュレータで出来ない。
  • 一度買ったアイテムは消費しないと再購入できない。
  • ドキュメントはすみずみまで読まないとだめ。

以上、Androidのアプリ内課金を試す際の覚書でした。

Reveling Prototype Patternについて

前回はReveling Module Patternについて書きました。JavaScriptでもprivateとpublicの領域を明快に分けられるという利点がありましたが、複数のインスタンスを作る際に、privateな関数もpublicな関数ももろもろが複製されるので、あまり効率が良くなさそうというのが懸念でした。

そこで、その点を補ったのがReveling Prototype Pattern(リビーリングプロトタイプパターン)ということで、紹介記事を読んで試してみました。

Revealing Prototype Pattern - Techniques, Strategies and Patterns for Structuring JavaScript Code - Dan Wahlin

Revealing Prototype Patternによるオブジェクトの作り方

//最初にコンストラクタを定義
var MessageCard = function(decorationChar) {
  this.decoration = decorationChar;
  this.history = [];
}

//プロトタイプに機能を追加
MessageCard.prototype = function() {
  function create(to, message){
    var head = createHead(to);
    var body = createBody(message, this);
    var contents = head + body;
    this.history.push(contents);
    return contents;
  }

  function createHead(to){
    return 'Dear ' + to + '\n';
  }

  function createBody(message, thisObj){
    var line = createLine(message.length + 4, thisObj.decoration) + '\n';
    var body = line;
    body += thisObj.decoration + ' ' + message + ' ' + thisObj.decoration + '\n';
    body += line;
    return body;
  }

  function createLine(length, char){
    var line = '';
    for(var i = 0; i < length; i++){
      line += char;
    }
    return line;
  }

  function getHistory(index){
    return this.history[index];
  }

  return {
    create: create,
    getHistory: getHistory
  };

}();

var messageCard = new MessageCard('+');

console.log(messageCard.create('tanaka', 'hello'));
/*
Dear tanaka
+++++++++
+ hello +
+++++++++
*/

console.log(messageCard.getHistory(0));
/*
Dear tanaka
+++++++++
+ hello +
+++++++++
*/

最初にコンストラクタを用意して、そのプロトタイプを拡張するみたいですね。 少々厄介なところがthisの参照を引き回す箇所がある点です。

  function create(to, message){
    var head = createHead(to);
    var body = createBody(message, this); //thisを引き回す
    var contents = head + body;
    this.history.push(contents);
    return contents;
  }

  function createBody(message, thisObj){ //オブジェクトの参照を引数から受け取る
    var line = createLine(message.length + 4, thisObj.decoration) + '\n';
    var body = line;
    body += thisObj.decoration + ' ' + message + ' ' + thisObj.decoration + '\n';
    body += line;
    return body;
  }

これを次のように書くとうまくいきません。createメソッドからcreateBodyメソッドを呼んだときの this.decorationはundefinedになります。JavaScriptのthisの難しいところですね。

  function create(to, message){
    var head = createHead(to);
    var body = createBody(message);
    var contents = head + body;
    this.history.push(contents);
    return contents;
  }

  function createBody(message){
    //ここでのthisはこのオブジェクトを参照していない
    var line = createLine(message.length + 4, this.decoration) + '\n';
    var body = line;
    body += this.decoration + ' ' + message + ' ' + this.decoration + '\n';
    body += line;
    return body;
  }

まとめ

Revealing Prototype Patternはプロトタイプを使って実行上の効率が良くなるものの、thisの参照に気を払わなければならない箇所があり、気をつけないと実装ミスにつながりそうだなと感じました。(JavaScriptに慣れていればそんなことはないのかもしれませんね・・・。)

一方、Revealing Module Patternの方が直観に反せずコーディングできそうです。なので、オブジェクトを山のように作る事がなければRevealing Module Patternでもいいのではないでしょうか。例えばTodoアプリを作るとして、1画面に1000個のTodoを表示させることはないと思います。複数ユーザの処理をさばくサーバーサイドはともかく、クライアントサイドに関してはRevealing Prototype Patternにしようが、Revealing Module Patternにしようが、さほどパフォーマンス的に気になるレベルではないと思いますし、好みの方を選択すればいいのかなと思いました。

Revealing Module Patternで盲点だったこと

これまで業務ではJavaScriptを場当たり的にコーディングしてきたので、今年は基本的なところから見直そうと思っています。JavaScriptの実装でいつもに気になっていたのがオブジェクトの生成で、どういった書き方が定石なのか、本で調べてもネットで調べてもいろいろ意見があって悩ましいところでした。

その中で、良さそうと思ったのが『JavaScriptデザインパターン』の中でも紹介されている「リビーリングモジュールパターン(Revealing Module Pattern)」です。

JavaScriptデザインパターン

JavaScriptデザインパターン

Revealing Module Patternによるオブジェクトの作り方

JavaScriptデザインパターン』にならったRevealing Module Pattern

var messageCard = function(){

  var decoration = '+';
  var history = [];

  function create(to, message){
    var head = createHead(to);
    var body = createBody(message);
    var contents = head + body;
    history.push(contents);
    return contents;
  }

  function createHead(to){
    return 'Dear ' + to + '\n';
  }

  function createBody(message){
    var line = createLine(message.length + 4, decoration) + '\n';
    var body = line;
    body += decoration + ' ' + message + ' ' + decoration + '\n';
    body += line;
    return body;
  }

  function createLine(length, char){
    var line = '';
    for(var i = 0; i < length; i++){
      line += char;
    }
    return line;
  }

  function getHistory(index){
    return history[index];
  }

  return {
    create: create,
    getHistory: getHistory
  };

}();


console.log(messageCard.create('tanaka', 'hello!'));
/*
Dear tanaka
++++++++++
+ hello! +
++++++++++
*/

console.log(messageCard.getHistory(0));
/*
Dear tanaka
++++++++++
+ hello! +
++++++++++
*/

いくつかfunctionが宣言されていますが、最終的に一番下でreturnしているオブジェクトが外部に公開する部分となり、それ以外は内部的に扱われるprivateな部分となります。JavaScriptにはpublicやprivateなどの修飾子はありませんが、この手法をとれば実現ができます。

JavaScriptでprivateを実現するのは不自然という意見もありますが、内部的な参照のみを意図しているものを不用意に外部にさらすのはカプセル化の観点からどうなのと思う所があります。なので明瞭にpublicとprivateを分けられるRevealing Module Patternは素晴らしい実装パターンだと思いました、が・・・

Revealing Module Patternで複数のオブジェクトをつくる

しかし、上記の実装では複数のオブジェクトをつくることができません。messageCardが即時関数の返却値となっているためです。シングルトンなオブジェクトの生成方法としては全く問題ないのですが、messageCardを何個も用意したいケースではどうしたらいいのかなと思っていました。

調べたところ、その点に言及した記事を見つけました。 http://weblogs.asp.net/dwahlin/archive/2011/09/05/creating-multiple-javascript-objects-when-using-the-revealing-module-pattern.aspx

複数オブジェクト生成に対応したRevealing Module Pattern

var MessageCard = function(){

//省略

}; //即時関数として実行しない!

//異なるインスタンスのMessageCardが生成できる
var messageCard1 = MessageCard();
var messageCard2 = MessageCard();

単純に即時関数にしなければいい訳ですね!『JavaScriptデザインパターン』の内容を鵜呑みにしていて、これは盲点でした。

複数のオブジェクト生成時の問題点

ただし、これはこれで問題がないわけではありません。 messageCard1とmessageCard2は同じ構造を持つオブジェクトですが、MessageCard関数が呼び出される度にまるまる新規に生成されます。つまり内部で宣言している関数も複製されることになり、メモリの使い方としてはあまり嬉しくありません。 この点は上記の紹介記事にも触れており、筆者はその解決策としてRevealing Prototype Pattern を紹介しています。次回はこれを調べてみたいと思います。

Thymeleafのテンプレート部品化(2)

Java Thymeleaf

前回の続きです。

Thymeleafでページの断片を取り込むにはth:include、th:replaceを利用しますが、そのまま生のテンプレートをブラウザで確認してもページ断片は反映されません。 しかし、テンプレートを直接ブラウザ確認する際でもJavaScriptを利用してページ断片の取り込みをサポートしてくれるThymolというライブラリが紹介されていたので試してみました。

続きを読む

Thymeleafのテンプレート部品化

Java Thymeleaf

前回に引き続きThymeleaf関連のエントリです。今回はテンプレートの共通部分を部品化して使い回す方法について書きました。

ページ断片を取り込む

Webページの共通部分(ヘッダーやフッターなど)を表示させたいとき、JSPではincludeやc:importの機能を利用すれば、作成したページ断片を取り込むことができます。

<%@ include file="header.jspf" %>

一方、Thymeleafではth:includeやth:replaceを利用すれば同様のことが可能です。

続きを読む

Thymeleafを業務で使いたい

Java Thymeleaf

Advent Calendarに参加してみたくてブログを始めてみました!よろしくお願いします!
こちらはJava EE Advent Calendar 2013 - Adventar 20日目の記事です。
前日の記事はAOE Takashiさんの「 Java EE Managed Beanについて (Java EE Advent Calendar2013 19日目) - AOEの日記 」でした。
本日はJava EEと併用するテンプレートとして、しばしば話題にあがるThymeleafについて思うところを書きました。

続きを読む