GoogleがIn-app Billing Version 2 APIを殺すそうなので、もう長らく更新していなかったAndroidアプリを更新する羽目になっています。
昔version2の頃にアプリ内課金を実装したときは、正直意味が分からないけどサンプル通りに書いたら動いたというレベルだったので、結構苦手意識があります。
ただ、すごくシンプルになったという話も聞くので、これを機にまとめてみようと思います。
サンプルアプリ
TrivialDriveに含まれている、Android Definititon Language(AIDL)ファイルを利用し、インターフェースファイル
IIAppBillingService.javaを作ります。
Android SDK Managerで
Google Play Billing Libraryをインストールします 。
<android-sdk>/extras/google/play_billing/samples/TrivialDrive/からサンプルプロジェクトをインポートします。
TrivialDrivaサンプルの
/srcディレクトリから
IInAppBillingService.aidlファイルをコピーし、自分のアプリの
/srcディレクトリへ貼り付けます。
/genディレクトリ内に
IInAppBillingService.javaが自動的に作成されます。
これはversion2と変わっていないようです。
AndroidManifest.xmlに以下のパーミッションを追加します。
<uses-permission android:name="com.android.vending.BILLING" />
- ServiceConnectionの作成とIInAppBillingServiceへのバインド
接続時と切断時のコールバックメソッドを持つ
ServiceConnectionを実装し、
bindServiceメソッドでバインドします。
ServiceConnectionを実装します。
private IInAppBillingService mIInAppBillingService;
private ServiceConnection mServiceConnection= new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
mIInAppBillingService = null;
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mIInAppBillingService = IInAppBillingService.Stub.asInterface(service);
}
};
IInAppBillingServiceを使用できるようにするため、
Activityの
onCreateメソッドで
bindServiceメソッドを呼びます。
Intentの引数は、クラス名の
IInAppBillingServiceではなく、
InAppBillingServiceなのが紛らわしいですね。
Intent serviceIntent = new Intent("com.android.vending.billing.InAppBillingService.BIND");
bindService(serviceIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
終了処理として、Activityが終了時に
onDestroyメソッドで
unbindServiceメソッドでアンバインドします。
if (mServiceConnection != null) {
unbindService(mServiceConnection);
}
なお、このあたりで大きくハマったのですが、
bindServiceを呼んでから
ServiceConnectionの
onServiceConnectedが呼ばれるまでにはタイムラグがあり、直後に
IInAppBillingServiceをいじくろうとするとぬるぽで死にます。
bindService自体はtrueで返ってくるのに、
IInAppBillingServiceはnullのまま。
時間を計ってみたところ、50~150ミリ秒後ぐらいでまちまちでしたが、何にしろタイムラグがあります。
直後にスレッド止めたりして待ち合わせをしても、ほぼ同時に返ってくるものの結局ぬるぽ。
まぁ何のために
onServiceConnectedメソッドを実装する必要があるのか考えれば、分かりそうなものでしたが。。
ということで、わざわざコールバックメソッドを実装させられている訳なので、
onServiceConnectedからトリガーしてあげればぬるぽにはならない訳です。
version3のAPIをサポートしているか確認します。
int response = mIInAppBillingService.isBillingSupported(3, getPackageName(), "inapp");
if(response == BILLING_RESPONSE_RESULT_OK) {
//OKの場合の処理
}
getPurchasesメソッドを呼ぶと、購入済みアイテムの情報を取得出来ます。
RESPONSE_CODEや、
INAPP_PURCHASE_ITEM_LISTなど、いくつかの情報を含む
Bundleが返されるので、必要に応じて取り出し利用します。
Bundle bundle = mIInAppBillingService.getPurchases(3, getPackageName(), "inapp", null);
if(bundle.getInt("RESPONSE_CODE") == BILLING_RESPONSE_RESULT_OK) {
ArrayList<String> purchases = bundle.getStringArrayList("INAPP_PURCHASE_ITEM_LIST");
for (int i = 0, n = purchases.size(); i < n; i++) {
//purchases.get(i)の中身によって処理を変える。
}
}
getBuyIntentメソッドを呼んで、返された
Bundleから
BUY_INTENTキーの
PendingIntentを取得します。
取得した
PendingIntenで
startIntentSenderForResultメソッドを呼び出します。
getBuyIntentメソッドの第4引数はDeveloper Payloadというやつで、空文字でもnullでもレスポンス時には空文字が返ってきます。
本来は任意の文字列を入れて、海賊版対策に使います。
startIntentSenderForResultメソッドの第2引数はコールバックを受けたときにリクエストを識別するための任意の
int値です。
Bundle buyIntentBundle = mIInAppBillingService.getBuyIntent(3,
getPackageName(), itemId, "inapp", "");
if (buyIntentBundle.getInt("RESPONSE_CODE") == BILLING_RESPONSE_RESULT_OK) {
PendingIntent pendingIntent = buyIntentBundle
.getParcelable("BUY_INTENT");
startIntentSenderForResult(pendingIntent.getIntentSender(),
REQUEST_CODE, new Intent(), Integer.valueOf(0),
Integer.valueOf(0), Integer.valueOf(0));
}
なお、サポート状況の確認から購入処理まで、
IInAppBillingServiceのメソッドを呼ぶ際には、
RemoteExceptionが発生するので、適時
catchします。
あとはActivityのonActivityResultへレスポンスが来るので、適時処理するだけです。
次回は
startIntentSenderForResultのレスポンス部分、
onActivityResultでの処理についてです。