java.lang.SecurityException: android.permission.CAMERA への権限拒否(Permission Denial)の修正方法

intermediate📱 Android2026-05-30| Android 6.0 (API レベル 23) 以上、Android Studio、Java/Kotlin

Error Message

java.lang.SecurityException: Permission Denial: starting Intent requires android.permission.CAMERA
#パーミッション#実行時パーミッション#セキュリティ#Android 6#マニフェスト

シナリオ:古いスマホでは動くのに!

AndroidManifest.xml<uses-permission android:name="android.permission.CAMERA" /> を追加したばかりだとします。古い Android 5.1 タブレットでアプリをテストすると、完璧に動作します。しかし、最新の Pixel デバイスや最近のエミュレータで実行した瞬間、レンズが開く前にアプリがクラッシュしてしまいます。

Logcat を素早く確認すると、原因が判明します:

java.lang.SecurityException: Permission Denial: starting Intent { act=android.media.action.IMAGE_CAPTURE ... } requires android.permission.CAMERA

原因:なぜマニフェストだけでは不十分なのか

Android 6.0 (Marshmallow) より前は、権限(パーミッション)はインストール時に一括で承諾するかどうかを決めるものでした。ユーザーは事前にすべてに同意していました。しかし、Google は Runtime Permissions(実行時パーミッション)モデルを導入してルールを変えました。現在、カメラ、位置情報、連絡先などの「危険な(Dangerous)」権限については、マニフェストで宣言するだけでは不十分です。

アプリは、機密性の高いアクションを実行するたびに権限を確認する必要があります。ユーザーが明示的に「許可」をタップしていない場合、システムはプライバシーを保護するために SecurityException をスローします。現在、稼働している Android デバイスのほぼ 99% がバージョン 6.0 以降を実行しているため、このフローをマスターすることはすべての開発者にとって必須です。

ステップバイステップの解決策

ステップ 1:マニフェストで基礎を固める

引き続き権限の宣言は必要です。これにより、アプリが必要とするハードウェアを Play ストアに伝えます。カメラの場合は、カメラのないデバイスでアプリが表示されないように、uses-feature タグも含めてください。

<!-- AndroidManifest.xml -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <uses-permission android:name="android.permission.CAMERA" />
    
    <!-- Play ストアでデバイスをフィルタリングするのに役立ちます -->
    <uses-feature android:name="android.hardware.camera" android:required="false" />
</manifest>

ステップ 2:Permission Launcher を登録する

過去の煩雑な onRequestPermissionsResult やリクエストコードのことは忘れましょう。Activity Result API は、これを処理するためのモダンで型安全な方法です。このランチャーを Activity または Fragment の上部で登録します:

// Kotlin と Activity Result API を使用
private val requestPermissionLauncher = registerForActivityResult(
    ActivityResultContracts.RequestPermission()
) { isGranted: Boolean ->
    if (isGranted) {
        // 成功!カメラを起動します
        openCamera()
    } else {
        // ユーザーが拒否しました。適切に処理します。
        Toast.makeText(this, "Camera access is required to take photos.", Toast.LENGTH_LONG).show()
    }
}

ステップ 3:確認とリクエストのロジック

ユーザーが「写真を撮る」ボタンをクリックしたときに、この確認を実行します。これは公式の Android ロジックに従っています:権限があるか確認し、理由を説明すべきか判断し、必要に応じてリクエストします。

private fun checkCameraPermission() {
    when {
        ContextCompat.checkSelfPermission(
            this,
            Manifest.permission.CAMERA
        ) == PackageManager.PERMISSION_GRANTED -> {
            openCamera()
        }
        shouldShowRequestPermissionRationale(Manifest.permission.CAMERA) -> {
            // カメラが必要な理由を説明するアラートダイアログを表示する
            showPermissionRationaleDialog()
        }
        else -> {
            // これによりシステムポップアップが表示されます
            requestPermissionLauncher.launch(Manifest.permission.CAMERA)
        }
    }
}

ステップ 4:カメラを安全に起動する

権限が確保されれば、クラッシュを恐れることなくカメラのインテントをトリガーできます。デバイスにカメラアプリがインストールされていない場合に備えて、常に try-catch ブロックでラップしてください。

private fun openCamera() {
    val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
    try {
        startActivityForResult(intent, REQUEST_IMAGE_CAPTURE)
    } catch (e: ActivityNotFoundException) {
        Toast.makeText(this, "No camera app found", Toast.LENGTH_SHORT).show()
    }
}

検証:フローのテスト

- **クリーンな状態:** 以前の選択をクリアするために、デバイスからアプリをアンインストールします。
- **初回実行:** カメラを起動します。システムダイアログに「[アプリ名] に写真の撮影を許可しますか?」と表示されることを確認します。
- **拒否テスト:** 一度権限を拒否します。再度試行し、「Rationale(根拠)」の説明が表示されることを確認します。
- **最終承認:** 権限を許可し、カメラが実際に開くことを確認します。

防止策とベストプラクティス

- **毎回確認する:** ユーザーはいつでもシステム設定で権限を取り消すことができます。権限の状態を変数にキャッシュせず、常に `checkSelfPermission` を呼び出してください。
- **過剰に要求しない:** ユーザーが実際にカメラ関連のボタンをタップしたときにのみ、カメラの権限を要求してください。初回起動時に 5 つの権限を要求するのは、アプリがアンインストールされる近道です。
- **フルスタックのセキュリティ:** 権限はモバイルだけの問題ではありません。アプリがこれらの写真をサーバーにアップロードする場合、そこでも正しいファイル権限が必要になります。バックエンドの設定には、[Unix Permissions Calculator](https://toolcraft.app/en/tools/developer/unix-permissions) などのツールが、アップロードディレクトリの安全性と機能性を確保するのに非常に役立ちます。
- **適切なエラー処理:** ユーザーがアクセスを拒否しても、アプリを壊さないでください。単純に写真ボタンを無効にし、アプリの設定へのリンクを提供してください。

Related Error Notes