はじめに
.NET MAUIでAndroid向けのシンプルな録音アプリを作り、Google Play Consoleのクローズドテストへ登録するところまで進めました。
アプリを作ること自体よりも、Google Play Consoleのアカウント準備、署名、ストア掲載情報、権限申告、テスター要件など、公開準備に多くの作業が必要でした。
この記事では、実際に行った手順と、途中で発生したトラブルをまとめます。
開発したアプリ
主な機能は次のとおりです。
- マイク録音
- m4a形式で端末内へ保存
- 録音経過時間の表示
- バックグラウンド録音
- 録音ファイルの一覧表示
- 再生、停止、削除
- 端末の共有・保存機能を利用したファイル出力
開発環境は次の構成です。
- .NET SDK 9
- .NET MAUI
- Target Framework:
net9.0-android - Android APIレベル:35
- Visual Studio
- Android実機
1. .NET MAUIプロジェクトの作成
リポジトリ直下へプロジェクトを作成しました。
dotnet new maui -n SimpleLongRecorder -o .
Android向けにビルドします。
dotnet build .\SimpleLongRecorder.csproj `
-f net9.0-android `
-c Debug
最初に実機で起動確認をしてから、録音機能を追加しました。
2. Androidの録音権限
通常の録音には、AndroidManifest.xmlへ次の権限が必要です。
<uses-permission android:name="android.permission.RECORD_AUDIO" />
アプリ側では、録音開始前にマイク権限を確認します。
var status =
await Permissions.CheckStatusAsync<Permissions.Microphone>();
if (status != PermissionStatus.Granted)
{
status =
await Permissions.RequestAsync<Permissions.Microphone>();
}
権限が拒否された場合は録音を開始せず、ユーザーへ説明を表示します。
3. MediaRecorderによるm4a録音
Android側ではAndroid.Media.MediaRecorderを使用しました。
初期化順序は次のとおりです。
mediaRecorder.SetAudioSource(AudioSource.Mic);
mediaRecorder.SetOutputFormat(OutputFormat.Mpeg4);
mediaRecorder.SetAudioEncoder(AudioEncoder.Aac);
mediaRecorder.SetOutputFile(outputPath);
mediaRecorder.Prepare();
mediaRecorder.Start();
保存先にはFileSystem.AppDataDirectoryを使用しました。
ファイル名の例です。
recording_20260717_103000.m4a
停止時は、例外が発生した場合でも必ずリソースを解放します。
try
{
mediaRecorder.Stop();
}
finally
{
mediaRecorder.Reset();
mediaRecorder.Release();
mediaRecorder.Dispose();
}
4. バックグラウンド録音
アプリが前面にある間は録音できても、ホーム画面へ移動すると録音が継続しない問題が発生しました。
Androidでバックグラウンド録音を行うには、Foreground Serviceが必要です。
AndroidManifest.xmlへ次の権限を追加しました。
<uses-permission
android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission
android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE" />
<uses-permission
android:name="android.permission.POST_NOTIFICATIONS" />
Serviceにはマイクを使用するForeground Serviceであることを指定します。
[Service(
Exported = false,
ForegroundServiceType = ForegroundService.TypeMicrophone)]
public sealed class RecordingForegroundService : Service
{
}
録音中はForeground Serviceの通知を表示します。
Androidでは、この通知は単なる装飾ではなく、バックグラウンドでマイクを利用していることをユーザーに知らせるために必要です。
今回は通知に独自の停止ボタンなどは追加せず、「録音中」であることだけを表示する仕様にしました。停止操作はアプリ画面から行います。
5. 録音ファイルの再生と削除
録音ファイルはFileSystem.AppDataDirectory配下から、拡張子が.m4aのファイルだけを取得します。
再生にはAndroid.Media.MediaPlayerを使用しました。
var player = new MediaPlayer();
player.SetDataSource(filePath);
player.Prepare();
player.Start();
削除処理では、任意のファイルを削除できないように、次の条件を確認します。
- AppDataDirectory配下である
- 拡張子が
.m4aである - 実際にファイルが存在する
現在再生しているファイルを削除する場合は、再生を停止してから削除します。
6. Google Play Console用アカウントの準備
開発者アカウント用と、ユーザーへ公開する問い合わせ用のGoogleアカウントを分けました。
例:
- Play Console所有者用:非公開
- アプリ問い合わせ用:Google Playに公開
所有者アカウントには、パスキーと2段階認証を設定しました。
個人アカウントでは、本人確認、連絡先電話番号の確認、実機確認などが必要になります。
7. パッケージ名
パッケージ名は、アプリをGoogle Playへ登録した後は変更できません。
今回は次の形式にしました。
jp.example.simplerecorder
独自ドメインを所有していなくても、技術的にはこの形式を使用できます。ただし、他者と衝突しない一意な名前にする必要があります。
将来ブランド名や組織名を変える可能性も考えて、登録前に決めておくことが重要です。
8. プライバシーポリシー
録音アプリはマイクを使用するため、プライバシーポリシーを準備しました。
Google Sitesで公開ページを作り、Play ConsoleへURLを登録しました。
記載した主な内容は次のとおりです。
- マイクを録音目的だけに使用すること
- 録音データは端末内に保存されること
- 開発者のサーバーへ送信しないこと
- 広告やアクセス解析を使用していないこと
- 問い合わせ先
Google Sitesは、閲覧制限をかけず、誰でもアクセスできる状態で公開する必要があります。
9. ストア掲載情報
最低限、次の素材を準備しました。
- アプリ名
- 簡単な説明
- 詳しい説明
- 512×512ピクセルのアプリアイコン
- 1024×500ピクセルのフィーチャーグラフィック
- スマートフォンのスクリーンショット
- 問い合わせ用メールアドレス
- プライバシーポリシーURL
説明文では、実装済みの機能だけを書きます。
将来追加予定のクラウド保存などを、現在利用できる機能のように書かないよう注意しました。
10. Play Consoleの申告
今回のアプリでは、主に次の項目を設定しました。
- ログイン機能:なし
- 広告:なし
- 健康アプリ:該当なし
- 金融機能:なし
- 行政アプリ:該当なし
- ターゲットユーザー:13歳以上
- データセーフティ:対象データを外部へ収集・共有しない
端末内に保存され、アプリ外へ自動送信されない録音データについて、実際の処理内容と矛盾しないように回答します。
将来、Google Drive保存、広告、アクセス解析などを追加した場合は、データセーフティとプライバシーポリシーの更新が必要です。
11. Foreground Service権限の申告
FOREGROUND_SERVICE_MICROPHONEを使用すると、Play Consoleで利用目的の申告が必要になります。
今回の用途は「バックグラウンドでの音声入力」です。
また、実際に権限を使用する様子を示す動画URLが必要でした。
動画では次の流れを撮影しました。
- アプリを起動
- 録音を開始
- ホーム画面へ移動
- 録音中の通知を表示
- アプリへ戻る
- アプリ画面で録音を停止
動画はYouTubeへ「限定公開」でアップロードし、そのURLをPlay Consoleへ登録しました。
「非公開」では審査担当者が閲覧できない可能性があるため、限定公開にします。
12. AABのRelease署名
Google PlayへアップロードするAABは、Debug署名では受け付けられません。
アップロード用キーストアを作成します。
keytool -genkeypair `
-v `
-keystore simple-recorder-upload.jks `
-alias simplerecorder `
-keyalg RSA `
-keysize 2048 `
-validity 10000
キーストアとパスワードはリポジトリ外へ保存します。
次のものはGitへ登録しません。
-
.jksファイル - パスワード
- 秘密鍵情報
- 個人情報を含む設定ファイル
Release AABの作成例です。
dotnet publish .\SimpleLongRecorder.csproj `
-f net9.0-android `
-c Release `
-p:AndroidPackageFormats=aab `
-p:AndroidKeyStore=true `
-p:AndroidSigningKeyStore="C:\secure\simple-recorder-upload.jks" `
-p:AndroidSigningKeyAlias=simplerecorder `
-p:AndroidSigningStorePass="file:C:\secure\password.txt" `
-p:AndroidSigningKeyPass="file:C:\secure\password.txt"
生成されたAABをPlay Consoleへアップロードします。
13. Debug署名のAABをアップロードして失敗
最初は、生成されたAABをそのままアップロードし、次のエラーになりました。
アップロードされたAPKまたはAndroid App Bundleが
デバッグモードで署名されています。
原因はDebug署名です。
解決方法は、アップロード用キーストアを作り、Release構成で署名済みAABを生成することです。
ファイル名にSignedと付いていても、必ずしも公開用署名とは限りません。証明書の内容を確認する必要があります。
jarsigner -verify -verbose -certs app-Signed.aab
証明書がAndroid Debugになっていないことを確認します。
14. バージョンコードを再利用できない
Play Consoleへ一度アップロードしたAABは、未公開リリースであっても同じバージョンコードを再利用できない場合があります。
初回をバージョンコード1で登録した後に修正版を作ったため、プロジェクトのバージョンコードを2へ上げました。
<ApplicationDisplayVersion>1.0</ApplicationDisplayVersion>
<ApplicationVersion>2</ApplicationVersion>
-
ApplicationDisplayVersion:ユーザーに見えるバージョン -
ApplicationVersion:Google Playが比較する整数のバージョンコード
修正版AABは、以前より大きいバージョンコードでアップロードします。
15. NuGetのTLS接続エラー
環境によっては、NuGetへのTLS接続に失敗し、復元やビルドができないことがありました。
この場合、むやみにNuGetソースを変更したり、長時間再試行したりせず、まずユーザー環境で通常の復元を試しました。
dotnet restore .\SimpleLongRecorder.csproj
ソースコードの問題とネットワーク環境の問題を分けて考えることが重要です。
16. MAUI Resizetizerのtargetsファイル破損
途中で次のようなビルドエラーが発生しました。
Microsoft.Maui.Resizetizer.After.targetsを読み込めませんでした。
Name cannot begin with the '1' character...
Visual Studioでは、1つのXML破損から複数のエラーが表示されました。
原因はソースコードではなく、グローバルNuGetキャッシュ内のパッケージ破損でした。
対象例:
%USERPROFILE%\.nuget\packages\
microsoft.maui.resizetizer\9.0.120
通常は、Visual StudioとMSBuildを終了して、該当するパッケージだけを削除し、復元します。
dotnet build-server shutdown
dotnet restore .\SimpleLongRecorder.csproj
Visual StudioがDLLを使用中の場合、パッケージを削除できません。保存してVisual Studioを閉じてから作業する必要があります。
今回は一時NuGetフォルダーへ正常なパッケージを復元し、破損した設定ファイルを修復することで解決しました。
修復後のビルド結果は次のとおりでした。
ビルドに成功しました。
0 個の警告
0 エラー
17. クローズドテスト
新しく作成した個人のPlay Consoleアカウントでは、製品版へ進む前にクローズドテストが必要です。
現在の要件は次のとおりです。
- 12人以上のテスター
- 14日間連続でオプトイン状態を維持
- クローズドテストのリリースを実際に公開する
AABをアップロードしただけでは、テスト期間は始まりません。
テスターが参加リンクを開き、テストへ参加してからカウントされます。
14日経過後、Play Consoleから製品版へのアクセスを申請します。
申請時には、次のような内容を回答します。
- テスターがどの機能を確認したか
- どのようなフィードバックを得たか
- テストで見つかった問題
- 問題をどのように修正したか
- 製品版として公開できると判断した理由
18. テストで確認してもらう内容
シンプルな録音アプリでも、次の確認項目があります。
- 初回のマイク権限要求が分かりやすいか
- 録音を開始・停止できるか
- ホーム画面へ移動しても録音が続くか
- 録音中の通知が表示されるか
- 長時間録音で停止しないか
- 複数回続けて録音できるか
- 保存されたファイルを再生できるか
- ファイルを削除できるか
- 他のアプリやPCへ保存できるか
- 録音ホールドが誤操作防止に役立つか
- 画面表示やボタンの説明が分かりやすいか
フィードバック例:
録音開始と停止は分かりやすかった。
バックグラウンドでも録音が継続した。
録音ファイルの保存場所が分かりにくかった。
長時間録音時にも経過時間を確認できた。
実際に得たフィードバックだけを記録し、申請時に使用します。
19. 事前に準備しておくもの
Google Play登録前に、次を準備しておくと進めやすくなります。
- Play Console所有者用Googleアカウント
- 公開問い合わせ用メールアドレス
- 2段階認証またはパスキー
- 本人確認書類
- 確認可能な電話番号
- Android実機
- 一意なパッケージ名
- アップロード用キーストア
- キーストアの安全なバックアップ
- プライバシーポリシーURL
- 512×512のアプリアイコン
- 1024×500のフィーチャーグラフィック
- スマートフォンのスクリーンショット
- Foreground Service利用説明動画
- 12人以上のテスター
- テスト項目とフィードバック記録
20. 今回学んだこと
- アプリ実装よりGoogle Play公開準備の方が作業量が多い場合がある
-
- マイクのForeground ServiceにはPlay Consoleで追加申告が必要
- バックグラウンド録音では通知表示が必須
- クローズドテストがコミュ障には大問題
とりあえずPLAYにあげてみるかということで簡単な無料アプリにしたのですが、クローズドテストを考えるともうすこし高機能な収益を見込めるようなものでないと厳しいかなと感じました。