.NET MAUI(net9.0-android)でReleaseのAPKを発行し、Android 12端末へ配布しようとしたら「App not installed as app isn’t compatible with your phone」と表示されてインストールできない。こうした“互換性がない”系のメッセージは原因が複数あり、ログが取れないと迷子になりがちです。本記事では、Android 12以降で必須になったexported設定と、署名済みAPKの選び方を軸に、最短で切り分ける手順をまとめます。
まず結論:直撃しやすい原因は「exported」と「署名」
Android 12(API 31)以降では、アプリの入口(ランチャーActivityなど)を含むコンポーネントに対して、Manifest上で android:exported の明示が実質必須になりました。さらに、配布用に生成されたAPKの中には 未署名のAPK が混ざることがあり、端末側の表示はざっくり「互換性がない」と出てしまう場合があります。
| 優先度 | 確認ポイント | やること | 期待できる効果 |
|---|---|---|---|
| 最優先 | android:exported が未設定 | MainActivity の [Activity] に Exported = true を追加して再ビルド | Android 12端末でのインストール失敗を解消 |
| 最優先 | インストール対象APKが未署名 | 出力フォルダーから -Signed.apk を選んで端末にインストール | 「互換性がない」表示の原因を除去 |
| 次点 | minSdk が高すぎる | AndroidMinSdkVersion / SupportedOSPlatformVersion を確認 | OSバージョン不一致を解消 |
| 次点 | ABI(CPUアーキテクチャ)が不一致 | AndroidSupportedAbis や RuntimeIdentifier(s) を見直す | 端末CPUに合わないAPKを排除 |
症状の整理:端末表示の「互換性がない」は“翻訳”が必要
Androidのインストール失敗は、実際にはさまざまな理由で起こります。しかし、ファイルマネージャーからタップして入れる場合や、端末メーカーのUIによっては、詳細なエラーが省略されて「互換性がないためインストールできない」と丸められることがあります。
そのため、まずは「互換性がない=minSdkが高い」と決め打ちせず、以下の“ありがちなパターン”を同時に潰すのが近道です。
| 端末側の表示例 | 実際に起きていること(例) | よく効く対処 |
|---|---|---|
| App not installed as app isn’t compatible with your phone | Manifestに必要項目がない/インストール前検証で弾かれる | android:exported を明示する |
| インストールされませんでした | APKが未署名、または署名方式が不適切 | -Signed.apk を使う/署名設定を整える |
| 解析エラー | 破損、転送失敗、ZIP構造不正 | 再ビルド、USB転送やADB経由で入れる |
| このアプリはインストールできません | minSdk不一致、ABI不一致、既存アプリとの署名不一致など | minSdk/ABI/署名一致を確認 |
ポイント:本当に原因を確定したいなら、まずは adb install で入れて、端末が返すエラーコード(INSTALL_PARSE_FAILED_〜 など)を確認すると切り分けが一気に進みます。後半で手順を紹介します。
対策:Android 12以降で必須になった「exported」を明示する
Android 12以降(正確には targetSdkVersion が 31以上 のアプリ)では、intent-filter を持つ Activity / Service / BroadcastReceiver に対して、android:exported を明示しないとインストール時に弾かれます。.NET MAUIアプリの場合、ランチャーとして起動する MainActivity は intent-filter(MAIN/LAUNCHER)を持つため、真っ先に影響を受けます。
MainActivity.cs の設定例
既存の [Activity] 属性に Exported = true を追加します。すでに他のプロパティが並んでいても、Exportedだけ追記すればOKです。
using Android.App;
using Android.Content.PM;
namespace YourAppNamespace;
[Activity(
Theme = "@style/Maui.SplashTheme",
MainLauncher = true,
ConfigurationChanges = ConfigChanges.ScreenSize
| ConfigChanges.Orientation
| ConfigChanges.UiMode
| ConfigChanges.ScreenLayout
| ConfigChanges.SmallestScreenSize,
Exported = true)]
public class MainActivity : MauiAppCompatActivity
{
}
ここでの Exported = true は「他アプリ(ランチャーを含む)から起動され得る入口として公開する」意味です。ランチャーActivityは端末のホームアプリから起動されるため、通常は true が自然です。
「MainActivity だけ直したのに直らない」場合の見落とし
プロジェクト内に以下のようなコンポーネントがある場合、MainActivity以外にも exported の明示が必要になることがあります。
- 独自の BroadcastReceiver(Push通知、BOOT_COMPLETED、CONNECTIVITY_CHANGE など)
- 独自の Service(フォアグラウンドサービス、IntentService相当)
- Deep Link / App Link を受けるための intent-filter を付けた別Activity
“外部から呼ばれる必要がない”コンポーネントは exported を false にするのが基本です。外部公開すると、想定外のIntentを受け取る入口になり、セキュリティ事故の温床になり得ます。
| コンポーネント | intent-filter の有無 | exported の考え方 |
|---|---|---|
| ランチャーActivity(MainActivity) | あり(MAIN/LAUNCHER) | 基本 true(端末のホームから起動されるため) |
| Deep Link用Activity | あり | 外部から開くなら true、アプリ内専用なら intent-filter 自体を付けない |
| 内部処理専用Service/Receiver | あり・なしに関わらず | 外部公開不要なら false(必要なら権限や署名保護も検討) |
Manifestでどう反映されるか確認する
MAUIはビルド時にManifestが自動生成・マージされます。原因の確定に近づけたいときは、生成されたManifest(ビルド成果物)を開いて exported が入っているか確認します。
- 例:
obj\Release\net9.0-android\android\AndroidManifest.xml
確認できる状態なら、MainActivityの要素に android:exported="true" が入っているはずです。
対策:インストールするAPKは「署名済み(-Signed.apk)」を使う
Androidアプリは必ず署名が必要です。Visual Studioやdotnetビルドは通常「デバッグ署名」または「リリース署名」を行いますが、出力先には署名前のAPKや中間成果物が混在することがあります。
その結果、フォルダー内の “それっぽいAPK” を選んで入れたら、実は未署名で端末が拒否し、表面上「互換性がない」に見えるケースがあります。特に dotnet publish で生成された成果物を手作業で配布するときに起きがちです。
どのAPKを選べばいい?(ファイル名の目安)
出力フォルダー(例:bin\Release\net9.0-android\)を開き、末尾が -Signed.apk のファイルを探してそれをインストールしてください。
| ファイル名の例 | 意味 | 端末に入れてよい? |
|---|---|---|
com.companyname.app.apk | 中間または署名前の出力になっている場合がある | 状況次第(避けるのが安全) |
com.companyname.app-Unsigned.apk | 明確に未署名 | 不可 |
com.companyname.app-Signed.apk | 署名済み(配布・インストール向け) | 可(これを使う) |
com.companyname.app.aab | Playストア向けのApp Bundle | 通常は端末へ直インストール不可(別途手順が必要) |
Release署名の基本(社内配布・検証のときに詰まりやすい点)
Releaseビルドは「デバッグ署名とは別の証明書」で署名するのが一般的です。ここでつまずくポイントは次の2つです。
- すでに同じパッケージ名のアプリが端末に入っている:以前の署名と違うと上書きインストールできません(署名不一致)。いったんアンインストールが必要です。
- チーム内で署名鍵が統一されていない:開発者ごとに別キーストアだと、同じアプリでも更新できず運用が不安定になります。
社内配布・継続検証をするなら、早い段階で「共通のテスト用キーストア」を決め、Release署名を揃えるのがおすすめです。
CLIでpublishする場合の例(署名を明示する)
環境やプロジェクト構成によって異なりますが、publish時に署名設定を明示することで “どれが成果物なのか” が分かりやすくなります。パスワード類はソースに直書きせず、安全な方法で管理してください。
dotnet publish -f net9.0-android -c Release ^
-p:AndroidPackageFormat=apk ^
-p:AndroidKeyStore=true ^
-p:AndroidSigningKeyStore=YourKeyStore.keystore ^
-p:AndroidSigningKeyAlias=your_alias
実際にインストールするのは、publish後に出力される -Signed.apk です。
補足:切り分けで必ず見る(minSdk と ABI)
最小対応バージョン(minSdk)が端末より高いとインストール不可
Android 12端末であれば、minSdkがAndroid 7(API 24)でも問題なく動きます。しかし、誤って minSdk を 31以上にしてしまうと、端末によっては表示が「互換性がない」に寄って見えることがあります。
MAUI/.NET for Android では、プロジェクトファイル(.csproj)で次のようなプロパティを使って最小バージョンを管理します。
<PropertyGroup>
<TargetFramework>net9.0-android</TargetFramework>
<!-- 最小対応(例:Android 7.0 / API 24) -->
<SupportedOSPlatformVersion>24.0</SupportedOSPlatformVersion>
<!-- もしくは明示的に minSdk/targetSdk を設定する場合 -->
<AndroidMinSdkVersion>24</AndroidMinSdkVersion>
<AndroidTargetSdkVersion>34</AndroidTargetSdkVersion>
</PropertyGroup>
どれが有効になるかはプロジェクトの状態やSDKに依存しますが、混在させると分かりづらくなるため、チーム内で“どのプロパティを正”にするかを決めるのが運用上のコツです。
| Android | APIレベル | メモ |
|---|---|---|
| Android 12 | 31 | android:exported 明示が実質必須(target 31+) |
| Android 12L | 32 | タブレット最適化のマイナー版 |
| Android 13 | 33 | 通知権限などの挙動が変わる |
| Android 14 | 34 | バックグラウンド制限が強化される傾向 |
端末CPU(arm64など)とAPKの対応ABIが合っているか
最近の実機はほぼ arm64(arm64-v8a)です。ビルド設定で特定ABIのみ(例:x86_64だけ)になっていると、実機に入れたときにインストールできません。
確認の観点は2つあります。
- APKに含めるABIの設定(AndroidSupportedAbis や RuntimeIdentifier(s))
- 端末側のABI(arm64-v8a / armeabi-v7a など)
| ABI | 主な対象 | 注意点 |
|---|---|---|
| arm64-v8a | ほとんどの実機(近年のAndroid) | 基本はこれを含める |
| armeabi-v7a | 古めの32bit端末 | 配布対象に古い端末があるなら追加 |
| x86_64 | 一部エミュレーター、特殊端末 | 実機配布だけなら不要なことが多い |
プロジェクト側の例です。端末配布で失敗している場合は、まず arm64-v8a を含める構成になっているか確認してください。
<PropertyGroup>
<AndroidSupportedAbis>arm64-v8a;armeabi-v7a</AndroidSupportedAbis>
</PropertyGroup>
最短で原因を確定する:adb install で“本当のエラー”を見る
端末UIのメッセージだけで悩むより、可能ならPCからADBでインストールしてエラーを読み取るのが最短です。Visual Studio 2022 を使っていても、手元配布の検証ではADBが最も確実です。
ADBでインストールする
adb devices
adb install -r path\to\com.companyname.app-Signed.apk
ここでエラーが出た場合、次のような文字列が返ります。
INSTALL_PARSE_FAILED_MANIFEST_MALFORMED:Manifestが原因(exported未指定など)INSTALL_FAILED_UPDATE_INCOMPATIBLE:既存アプリと署名が違う(アンインストールが必要)INSTALL_FAILED_NO_MATCHING_ABIS:ABI不一致
表示された文字列に合わせて対策すれば、無駄な試行錯誤を減らせます。
既存アプリが邪魔しているケース(署名不一致)
同じアプリ(同じパッケージ名)が端末に残っている場合は、署名が変わると更新できません。検証中に起きやすいので、まずはアンインストールしてから試します。
adb uninstall com.companyname.app
よくある質問
Exported = true を付けるのは危険?
ランチャーActivityの exported=true 自体は一般的です。ただし「外部から起動されなくていいコンポーネント」まで exported=true にすると、想定外の入口になります。必要なものだけ true、不要なものは false が基本です。
-Signed.apk が見当たりません
ビルドの成否や設定により出力名が変わることがあります。まずは Release ビルドが通っているかを確認し、出力フォルダー(例:bin\Release\net9.0-android\)を検索してください。見つからない場合は、署名ステップが無効になっている可能性があります。Visual Studio の「アーカイブ」機能を使うと、署名済みの成果物が得やすくなります。
Playストアに出す予定でも、この対策は必要?
必要です。Playストア配布ではAABが主流ですが、android:exported の明示はManifest要件であり、ローカルインストールかストア配布かは関係ありません。また、署名もストア用鍵に切り替わるだけで、署名が必要である点は同じです。
まとめ:Android 12の“互換性がない”は、exported と署名で大半が解決する
.NET MAUI(net9.0-android)で作ったAPKがAndroid 12端末に入らないときは、まず次の順で潰すのが効率的です。
- MainActivity(および intent-filter を持つコンポーネント)に Exported を明示する
- インストール対象は -Signed.apk を使う(未署名APKを避ける)
- それでもダメなら minSdk と ABI、そして既存アプリとの署名不一致を確認する
端末UIのメッセージだけで悩まず、adb install でエラーを拾って原因に直撃すると、再発防止まで一気に進められます。

コメント