.NET MAUI net9.0-androidのAPKがAndroid 12で互換性がないと出る原因と解決策(android:exportedと-Signed.apk)

.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 phoneManifestに必要項目がない/インストール前検証で弾かれる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.aabPlayストア向けの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に依存しますが、混在させると分かりづらくなるため、チーム内で“どのプロパティを正”にするかを決めるのが運用上のコツです。

AndroidAPIレベルメモ
Android 1231android:exported 明示が実質必須(target 31+)
Android 12L32タブレット最適化のマイナー版
Android 1333通知権限などの挙動が変わる
Android 1434バックグラウンド制限が強化される傾向

端末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 でエラーを拾って原因に直撃すると、再発防止まで一気に進められます。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次