アプリの権限をリクエストする

すべての Android アプリは、制限されたアクセス サンドボックス内で実行されます。アプリが自身のサンドボックスの外部にあるリソースや情報を使用しなければならない場合、アプリは適切なパーミッションをリクエストする必要があります。アプリ マニフェストにパーミッションを記載することにより、アプリがパーミッションを必要としていることを宣言し、実行時にパーミッションごとにユーザーの承認をリクエストします(Android 6.0 以降)。

このページでは、Android Support Library を使用してパーミッションを確認およびリクエストする方法について説明します。Android フレームワークは Android 6.0(API レベル 23)と類似したメソッドを備えていますが、サポート ライブラリによって古いバージョンの Android との互換性をさらに簡単に確保できるようになります。

マニフェストにパーミッションを追加する

アプリがパーミッションを必要としていることを宣言するには、Android のバージョンにかかわらず、アプリ マニフェスト内で最上位の <manifest> 要素の子要素として <uses-permission> 要素を挿入します。たとえば、アプリでインターネットにアクセスする必要がある場合は、マニフェストに次の行を記述します。

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
            package="com.example.snazzyapp">

        <uses-permission android:name="android.permission.INTERNET"/>
        <!-- other permissions go here -->

        <application ...>
            ...
        </application>
    </manifest>
    

パーミッションを宣言した後のシステムの動作は、パーミッションの保護レベルによって異なります。「normal」とみなされるパーミッションはインストール直後にシステムによって付与されますが、「dangerous」とみなされるパーミッションについてはユーザーがアプリのアクセスを明示的に許可する必要があります。パーミッションの種類について詳しくは、保護レベルを参照してください。

パーミッションを確認する

アプリに dangerous パーミッションが必要な場合は、dangerous パーミッションが必要な操作を実行するたびにパーミッションの有無を確認する必要があります。Android 6.0(API レベル 23)以降では、アプリがより低い API レベルを対象にしている場合でも、ユーザーは任意のアプリからパーミッションをいつでも取り消すことができます。したがって、アプリで昨日カメラを使用した場合でも、今日そのアプリにそのパーミッションが引き続き存在すると想定することはできません。

パーミッションがあるかどうかを確認するには、ContextCompat.checkSelfPermission() メソッドを呼び出します。たとえば、次のスニペットではカレンダーに書き込むためのパーミッションがアクティビティにあるかどうかを確認する方法を示しています。

Kotlin

    if (ContextCompat.checkSelfPermission(thisActivity, Manifest.permission.WRITE_CALENDAR)
            != PackageManager.PERMISSION_GRANTED) {
        // Permission is not granted
    }
    

Java

    if (ContextCompat.checkSelfPermission(thisActivity, Manifest.permission.WRITE_CALENDAR)
            != PackageManager.PERMISSION_GRANTED) {
        // Permission is not granted
    }
    

アプリにそのパーミッションがある場合、このメソッドにより PERMISSION_GRANTED が返され、アプリは操作を続行できます。アプリにそのパーミッションがない場合、このメソッドにより PERMISSION_DENIED が返されるため、アプリはユーザーにパーミッションを明示的に求める必要があります。

パーミッションをリクエストする

アプリが checkSelfPermission() から PERMISSION_DENIED を受け取った場合、そのパーミッションをユーザーに要求する必要があります。次のコード スニペットに示すように、Android にはパーミッションをリクエストするときに使用できるメソッドがいくつかあります(requestPermissions() など)。これらのメソッドを呼び出すと、標準の Android ダイアログが開きます。このダイアログをカスタマイズすることはできません。

ユーザーへの表示方法は、パーミッションの概要で説明されているように、デバイスの Android のバージョンとアプリのターゲット バージョンによって異なります。

アプリにパーミッションが必要な理由の説明

状況によっては、アプリにパーミッションが必要な理由をユーザーに説明したほうがいいこともあります。たとえば、ユーザーがカメラアプリを起動した場合、ユーザーはカメラを使用するためのパーミッションが求められても驚くことはないでしょう。しかし、なぜアプリがユーザーの位置情報や連絡先へのアクセスを求めるかは理解できないかもしれません。アプリがパーミッションをリクエストする前に、ユーザーに理由を説明することを検討してください。ただし、説明を表示することでユーザーに負担を与えないように注意してください。説明が多すぎるとユーザーはストレスを感じ、アプリを削除してしまう可能性があります。

この問題の対処法の 1 つとして、ユーザーがパーミッション リクエストをすでに拒否している場合にのみ説明を表示する方法があります。Android には shouldShowRequestPermissionRationale() というユーティリティ メソッドが用意されており、このメソッドは、ユーザーが以前にリクエストを拒否した場合は true を返します。一方、ユーザーがパーミッションを拒否し、パーミッション リクエスト ダイアログで [今後表示しない] を選択した場合、またはデバイス ポリシーによってパーミッションが禁止されている場合は false を返します。

ユーザーがパーミッションを必要とする機能を使おうとしながら、パーミッション リクエストを拒否し続けている場合、その機能を利用するにはアプリにパーミッションが必要であることをユーザーが理解していない可能性があります。このような場合は、ユーザーに対して説明を表示するとよいでしょう。

パーミッションを求めるときのユーザー エクスペリエンスを向上させる方法について詳しくは、アプリの権限に関するおすすめの設定をご覧ください。

必要に応じてデフォルト ハンドラになるようリクエストする

アプリによっては、通話履歴や SMS メッセージに関するユーザーの機密情報にアクセスする必要があります。通話履歴や SMS メッセージに固有のパーミッションをリクエストするアプリを Play ストアに公開する場合は、ランタイム パーミッションをリクエストする前に、アプリを中核的システム機能のデフォルト ハンドラとして設定することをユーザーに求める必要があります。

デフォルト ハンドラ プロンプトをユーザーに表示する方法を含め、デフォルト ハンドラの詳細については、デフォルト ハンドラに限り使用できるパーミッションに関するガイドをご覧ください。

必要なパーミッションのリクエスト

アプリに必要なパーミッションがない場合、アプリは requestPermissions() メソッドの 1 つを呼び出し、適切なパーミッションをリクエストする必要があります。アプリは必要なパーミッションと、そのパーミッション リクエストを識別するために指定された整数のリクエスト コードを渡します。このメソッドは非同期的に機能します。メソッドの結果はすぐに返され、ユーザーがプロンプトに応答した後、システムはその結果を引数に指定してアプリのコールバック メソッドを呼び出し、アプリが requestPermissions() に渡したものと同じリクエスト コードを渡します。

次のコードでは、ユーザーの連絡先を読み込むパーミッションがアプリにあるかどうかを確認しています。パーミッションがない場合は、パーミッションの必要性に関する説明を表示するかどうかを確認し、説明が不要の場合はパーミッションをリクエストします。

Kotlin

    // Here, thisActivity is the current activity
    if (ContextCompat.checkSelfPermission(thisActivity,
            Manifest.permission.READ_CONTACTS)
            != PackageManager.PERMISSION_GRANTED) {

        // Permission is not granted
        // Should we show an explanation?
        if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
                Manifest.permission.READ_CONTACTS)) {
            // Show an explanation to the user *asynchronously* -- don't block
            // this thread waiting for the user's response! After the user
            // sees the explanation, try again to request the permission.
        } else {
            // No explanation needed, we can request the permission.
            ActivityCompat.requestPermissions(thisActivity,
                    arrayOf(Manifest.permission.READ_CONTACTS),
                    MY_PERMISSIONS_REQUEST_READ_CONTACTS)

            // MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
            // app-defined int constant. The callback method gets the
            // result of the request.
        }
    } else {
        // Permission has already been granted
    }
    

Java

    // Here, thisActivity is the current activity
    if (ContextCompat.checkSelfPermission(thisActivity,
            Manifest.permission.READ_CONTACTS)
            != PackageManager.PERMISSION_GRANTED) {

        // Permission is not granted
        // Should we show an explanation?
        if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
                Manifest.permission.READ_CONTACTS)) {
            // Show an explanation to the user *asynchronously* -- don't block
            // this thread waiting for the user's response! After the user
            // sees the explanation, try again to request the permission.
        } else {
            // No explanation needed; request the permission
            ActivityCompat.requestPermissions(thisActivity,
                    new String[]{Manifest.permission.READ_CONTACTS},
                    MY_PERMISSIONS_REQUEST_READ_CONTACTS);

            // MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
            // app-defined int constant. The callback method gets the
            // result of the request.
        }
    } else {
        // Permission has already been granted
    }
    

表示されるプロンプトには、アプリがアクセスする必要があるパーミッション グループに関する説明はありますが、具体的なパーミッションは表示されません。

注: アプリが requestPermissions() を呼び出すと、ユーザーに対して標準のダイアログ ボックスが表示されます。アプリでこのダイアログ ボックスを設定または変更することはできません。ユーザーに情報や説明を表示する必要がある場合は、アプリにパーミッションが必要な理由の説明に書いてあるように、requestPermissions() を呼び出す前に情報や説明を表示する必要があります。

パーミッション リクエストへの応答の処理

ユーザーがアプリのパーミッション リクエストに応答すると、システムがアプリの onRequestPermissionsResult() メソッドを呼び出してユーザーの応答結果を渡します。アプリはこのメソッドをオーバーライドし、パーミッションが付与されたことを確認する必要があります。 コールバックには、requestPermissions() に渡されたものと同じリクエスト コードが渡されます。たとえば、アプリが READ_CONTACTS アクセスをリクエストする場合、次のようなコールバック メソッドが含まれる可能性があります。

Kotlin

    override fun onRequestPermissionsResult(requestCode: Int,
            permissions: Array<String>, grantResults: IntArray) {
        when (requestCode) {
            MY_PERMISSIONS_REQUEST_READ_CONTACTS -> {
                // If request is cancelled, the result arrays are empty.
                if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
                    // permission was granted, yay! Do the
                    // contacts-related task you need to do.
                } else {
                    // permission denied, boo! Disable the
                    // functionality that depends on this permission.
                }
                return
            }

            // Add other 'when' lines to check for other
            // permissions this app might request.
            else -> {
                // Ignore all other requests.
            }
        }
    }
    

Java

    @Override
    public void onRequestPermissionsResult(int requestCode,
            String[] permissions, int[] grantResults) {
        switch (requestCode) {
            case MY_PERMISSIONS_REQUEST_READ_CONTACTS: {
                // If request is cancelled, the result arrays are empty.
                if (grantResults.length > 0
                    && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    // permission was granted, yay! Do the
                    // contacts-related task you need to do.
                } else {
                    // permission denied, boo! Disable the
                    // functionality that depends on this permission.
                }
                return;
            }

            // other 'case' lines to check for other
            // permissions this app might request.
        }
    }
    

表示されるダイアログ ボックスには、アプリがアクセスする必要があるパーミッション グループに関する説明はありますが、具体的なパーミッションは表示されません。 たとえば、READ_CONTACTS パーミッションをリクエストした場合、システム ダイアログ ボックスには、アプリがデバイスの連絡先にアクセスする必要があることのみが表示されます。ユーザーは、各パーミッション グループに対してパーミッションを一度付与するだけで済みます。アプリがそのグループ内の他のパーミッション(アプリ マニフェストに記述されているもの)をリクエストした場合は、パーミッションが自動的に付与されます。パーミッションをリクエストすると、ユーザーがシステム ダイアログ ボックスを介してリクエストを明示的に承認した場合と同じように、onRequestPermissionsResult() コールバック メソッドが呼び出され、PERMISSION_GRANTED が渡されます。

注: ユーザーが同じグループ内の別のパーミッションをすでに付与している場合でも、アプリは必要なすべてのパーミッションを明示的にリクエストする必要があります。また、パーミッションのグループ化については、今後の Android リリースで変更される可能性があります。特定のパーミッションが同じグループに存在する、または存在しないという前提に基づいてコードを記述しないでください。

たとえば、アプリ マニフェストに READ_CONTACTSWRITE_CONTACTS の両方を記述しているとします。READ_CONTACTS をリクエストし、ユーザーがパーミッションを付与した後、WRITE_CONTACTS をリクエストした場合、ユーザーの介入なしに、システムによってそのパーミッションが即座に付与されます。

ユーザーがパーミッション リクエストを拒否した場合は、アプリで適切なアクションを実行する必要があります。たとえば、パーミッションを必要とするアクションをユーザーがリクエストした場合、そのアクション実行できない理由をアプリでダイアログに表示します。

ユーザーはパーミッションの付与を求められたときに、そのパーミッションが再度求められないように選択することもできます。この場合、アプリが requestPermissions() を使用してパーミッションを再度求めたときは常に、システムによってそのリクエストが即座に拒否されます。ユーザーがリクエストを明示的に再度拒否した場合と同じように、onRequestPermissionsResult() コールバック メソッドが呼び出され、PERMISSION_DENIED が渡されます。アプリがそのパーミッションを取得することがデバイス ポリシーで禁止されている場合も、このメソッドは false を返します。つまり、requestPermissions() を呼び出すときは、ユーザーとの直接的なやり取りは発生していないと想定できます。

アプリのパーミッションを求めるときのユーザー エクスペリエンスを向上させるには、アプリの権限に関するおすすめの設定もご覧ください。

API レベルでパーミッションを宣言する

実行時パーミッションをサポートするデバイス、つまり Android 6.0(API レベル 23)以降を実行しているデバイスでのみパーミッションを宣言するには、uses-permission タグではなく uses- permission-sdk-23 タグを追加します。

このいずれかのタグを使用すると、maxSdkVersion 属性を設定して、新しいバージョンが実行されているデバイスでは特定のパーミッションが不要であることを指定できます。

参考情報

パーミッションについて詳しくは、以下の記事をご覧ください。

パーミッションのリクエストについて詳しくは、以下のサンプルアプリをダウンロードしてください。