Google Play Billing 8/9でハマった話
.NET MAUI製のAndroidアプリを、.NET 9から.NET 10へ移行しました。
同時に、Android側の課金処理を
Plugin.InAppBilling から Google Play Billing Clientの直接利用へ変更しました。
そこで問題になったのが、
Android 5.0(API 21)対応を残したまま、Billing 9を使えるのか?
という点です。
結論から書くと、今回の環境ではこうなりました。
Billing 9 + minSdk 23 → ビルド成功
Billing 9 + minSdk 21 → ビルド失敗
Billing 8 + minSdk 21 → ビルド成功
最終的には、
.NET 10
Android API 36
minSdk 21
Billing 8.0.0
署名付きAAB生成成功
という構成にしています。
最終的な構成
今回、最終的に落ち着いた構成はこちらです。
.NET SDK : 10.0.400
.NET MAUI : 10.0.20
TargetFramework : net10.0-android
Compile SDK : Android API 36
Minimum SDK : Android API 21
Billing : Xamarin.Android.Google.BillingClient 8.0.0
Build : GitHub Actions Self-hosted Windows Runner
Output : Signed AAB
プロジェクト側では、Androidの最低APIを21にしています。
21.0
Billing Clientは8.0.0です。
まずBilling 9で試した
最初はBilling 9を使っていました。
.NET 10への移行自体は進みました。
ただし、minSdk 21 のままではビルドでエラーが出ました。
代表的なのがこれです。
uses-sdk:minSdkVersion 21 cannot be smaller than version 23 declared in library
さらに、
AndroidX Lifecycle
AndroidX Fragment
AndroidX Activity
SavedState
などでも、依存関係の競合が発生しました。
Manifest merge時にも、重複系のエラーが出ました。
Namespace 'com.android.billingclient' is used in multiple modules and/or libraries
一度minSdkを23に上げると成功した
原因切り分けのため、一度Androidの最低APIを23へ上げました。
23.0
すると、
.NET 10 + Billing 9 + minSdk 23
の構成では、署名付きAABの生成まで成功しました。
ここはかなり重要でした。
つまり、
Billing 9そのものが.NET 10で使えないわけではありません。
問題は、
Billing 9の依存関係と、minSdk 21を両立できなかったこと
でした。
API 21を残すためBilling 8へ変更
そこで、Billing Clientを9系から8系へ変更しました。
minSdkは21のままです。
21.0
すると、Billing 9使用時に出ていた、
AndroidXの大きな依存関係エラー
Manifest mergeエラー
minSdk 23要求
は解消されました。
今度はBilling 8と9のAPI差分でエラー
ところが、Billing 8へ下げると別の問題が出ました。
Billing 9向けに書いていたコードでは、次のようにしていました。
var productResult =
await billing.QueryProductDetailsAsync(productQuery);
if (productResult.Result.ResponseCode != BillingResponseCode.Ok)
{
return PremiumPurchaseResult.Unavailable;
}
Billing 8では、この部分でコンパイルエラーになりました。
'QueryProductDetailsResult' に 'Result' の定義が含まれていません
つまり、
Billing Library本体の世代差だけでなく、.NET側Binding APIにも差がある
ということです。
そこで、ProductDetails取得部分をBilling 8側に合わせました。
var productResult =
await billing.QueryProductDetailsAsync(productQuery);
var details =
productResult.ProductDetailsList
.FirstOrDefault(x => x.ProductId == ProductId);
if (details is null)
{
return PremiumPurchaseResult.Unavailable;
}
これで次へ進めるようになりました。
.NET 10移行ではTimePickerでも差分が出た
Billing以外にも、.NET 10化でコンパイルエラーが1つ出ました。
以前は、
ClockTime = x.Clock.Time;
としていました。
.NET 10 MAUIでは TimePicker.Time がnullableになったため、
System.TimeSpan? cannot be implicitly converted to System.TimeSpan
というエラーになりました。
ここは既存動作を変えないように、nullable対応だけ追加しました。
.NETのメジャーバージョンを上げるときは、
「ビルドが通らない大きな変更」だけでなく、こうした型の細かい変更も出る
ので注意が必要です。
Self-hosted Runnerでもビルド
今回、GitHub ActionsはWindowsのSelf-hosted Runnerで動かしました。
環境はこんな感じです。
Windows
.NET SDK 10.0.400
.NET MAUI 10.0.20
Android SDK API 36
JDK 21
Workflowでは、
runs-on: [self-hosted, Windows, X64]
を指定しています。
Android SDKの場所も明示しました。
env:
ANDROID_SDK_ROOT: C:\Users\star\AppData\Local\Android\Sdk
ANDROID_HOME: C:\Users\star\AppData\Local\Android\Sdk
Publish時にも指定しています。
dotnet publish .\Project.csproj -f net10.0-android
-c Release -p:AndroidPackageFormat=aab
"-p:AndroidSdkDirectory=$env:ANDROID_SDK_ROOT"
最終的には、
Restore Android Success
Restore upload key Success
Build signed AAB Success
Collect AAB Success
Upload artifact Success
まで通りました。
実際の試行錯誤
今回の流れを時系列で並べると、こうです。
.NET 10 + Billing 9 + API 21
↓
AndroidX / Manifest / minSdkエラー
↓
minSdk 23へ変更
↓
Billing 9で署名付きAABビルド成功
↓
API 21を維持する方針へ戻す
↓
Billing 8へ変更
↓
QueryProductDetailsResultのAPI差分でコンパイルエラー
↓
Billing 8向けに修正
↓
別修正時にenum定義を落としてしまいコンパイルエラー
↓
enumを復元
↓
.NET 10 + Billing 8 + API 21で
署名付きAABビルド成功
途中で Billing 9 + minSdk 23でも成功しているため、
将来minSdkを23以上に上げるなら、
Billing 9へ戻せることも確認できました。
最終結果
最終的には、
.NET 10
Android API 36
minSdk 21
Billing 8.0.0
署名付きAAB
という構成でビルド成功しました。
現時点では、
Billing 8を使う
minSdk 21を維持する
という構成にしています。
将来minSdkを23以上に上げるタイミングで、
Billing 9以降へ移行
する予定です。
まとめ
今回いちばん大きかったのは、
単純に「最新版へ上げればいい」という話ではなかった
という点です。
.NET MAUIでAndroidアプリを更新するときは、
.NETのバージョン
Androidの最低API
Google Play Billingのバージョン
AndroidX依存関係
.NET側Binding API
をセットで見る必要があります。
特にBilling Clientを更新すると、
NuGet上のバージョンだけでは見えないAndroid側の依存関係
でハマることがあります。
今回のように、
必要なminSdkを維持するために、あえてBilling 8を使う
という判断もありだと分かりました。
========================================
追記:その後、現行構成でも再検証した
その後、別の現行プロジェクトでも同じ構成を再検証しました。
まず .NET 9 + Billing Client 8 を試したところ、NuGetの復元自体は完了したものの、AndroidX系パッケージの依存関係が混在し、Releaseビルド時のAOT処理で失敗しました。
主な症状は Xamarin.AndroidX.SavedState などの依存関係を正しく読み込めず、AndroidX関連DLLのAOTコンパイルが失敗するというものでした。
このため、少なくとも今回検証した.NET MAUIの依存構成では、.NET 9 + Billing Client 8 をそのまま組み合わせる方法は採用しませんでした。
そこで、以前この記事で確認していた構成に戻し、
.NET 10
Google Play Billing Client 8.0.0
Android API 36
minSdk 23
で再度ビルドしました。
この構成ではReleaseビルドに成功し、さらにGoogle Play向けのアップロードキーを使用した署名付きAABの生成まで正常に完了しました。
今回の再検証でも、.NET 10 + Billing Client 8 というこの記事の最終構成が有効であることを確認できました。
.NET 10移行時のTimePicker.Timeにも注意
今回の再検証でも、.NET 10への移行によるAPI差分が1点発生しました。
従来は次のコードで問題ありませんでした。
ClockTime = x.Clock.Time,
しかし.NET 10では TimePicker.Time が TimeSpan? となるため、TimeSpan へそのまま代入するとコンパイルエラーになります。
例えば次のように変更します。
ClockTime = x.Clock.Time ?? default,
または、
ClockTime = x.Clock.Time.GetValueOrDefault(),
でも対応できます。
補足
この記事内の「Billing 9 + minSdk 21ではビルドできなかった」という結果は、Google Play Billing 9自体がAndroid 5で絶対に利用できない、という一般論を意味するものではありません。
あくまで、この記事で使用した.NET MAUI、Android Binding、NuGetパッケージの組み合わせで実際に検証した結果です。
同様に、Billing 8についても、使用する.NET SDK、MAUI Workload、AndroidX依存関係によって結果が変わる可能性があります。
ただし今回の再検証では、.NET 10 + Billing Client 8.0.0 でReleaseビルドおよび署名付きAAB生成まで再度成功しました。