lightbulb_outline Help shape the future of the Google Play Console, Android Studio, and Firebase. Start survey

実行時のパーミッション リクエスト

Android 6.0(API レベル 23)以降では、ユーザーは、アプリのインストール時ではなく、アプリの実行時にパーミッションをアプリに付与します。このアプローチにより、ユーザーがアプリをインストールまたはアップデートするときに、パーミッションを付与する必要がなくなるため、アプリのインストール プロセスが効率化されます。また、ユーザーはアプリの機能をより細かく制御できるようになります。たとえば、カメラアプリがカメラにアクセスできるが、端末の位置情報にはアクセスできないようにすることができます。ユーザーはアプリの [Settings] 画面でパーミッションをいつでも取り消すことができます。

システム パーミッションは、Normal と Dangerous の 2 つのカテゴリに分けられます。

  • Normal パーミッションでは、ユーザーのプライバシーが直接脅かされることはありません。アプリのマニフェストに Normal パーミッションが記述されている場合、このパーミッションは自動的に付与されます。
  • Dangerous パーミッションは、アプリによるユーザーの機密データへのアクセスを許可します。アプリのマニフェストに Normal パーミッションが記述されている場合、このパーミッションは自動的に付与されます。Dangerous パーミッションがリストされている場合、ユーザーは、アプリに明示的な承認を与える必要があります。

詳細については、Normal パーミッションと Dangerous パーミッションを参照してください。

Android のすべてのバージョンでは、パーミッションの宣言で説明したように、アプリに必要な Normal パーミッションと Dangerous パーミッションの両方をアプリ マニフェストで宣言する必要があります。ただし、その宣言の効果は、システムのバージョンおよびアプリが対象とする SDK レベルによって異なります。

  • 端末で Android 5.1 以前のバージョンが実行されている場合、またはアプリが対象とする SDK が 22 以下の場合:マニフェストに Dangerous パーミッションが記述されている場合、ユーザーはアプリのインストール時にパーミッションを付与する必要があります。パーミッションを付与しないと、アプリをインストールできません。
  • 端末で Android 6.0 以降のバージョンが実行されており、かつアプリが対象とする SDK が 23 以上の場合:アプリ マニフェストにパーミッションを記述し、アプリの実行中に必要な各 Dangerous パーミッションをリクエストする必要があります。ユーザーは各パーミッションを付与または拒否することができ、ユーザーがパーミッション リクエストを拒否した場合でも、アプリは機能が制限された状態で実行を継続することができます。

注: Android 6.0(API レベル 23)以降では、アプリがより低い API レベルを対象にしている場合でも、ユーザーは任意のアプリからパーミッションをいつでも取り消すことができます。アプリが対象としている API レベルにかかわらず、アプリをテストして、必要なパーミッションがない状態でもアプリが適切に動作することを確認する必要があります。

このレッスンでは、Android Support Library を使用して、パーミッションを確認およびリクエストする方法について説明します。Android フレームワークは、Android 6.0(API レベル 23)と類似したメソッドを備えています。ただし、Support Library を使用すると、メソッドを呼び出す前に、アプリが実行されている Android のバージョンを確認する必要がないため、処理がより簡単になります。

パーミッションの確認

アプリに Dangerous パーミッションが必要な場合は、Dangerous パーミッションが必要な操作を実行するたびにパーミッションの有無を確認する必要があります。ユーザーはいつでもパーミッションを取り消すことができるため、アプリで昨日、カメラを使用した場合でも、本日もアプリにそのパーミッションが引き続き存在すると想定することはできません。

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

// Assume thisActivity is the current activity
int permissionCheck = ContextCompat.checkSelfPermission(thisActivity,
        Manifest.permission.WRITE_CALENDAR);

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

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

アプリ マニフェストに記述されている Dangerous パーミッションがアプリに必要な場合、アプリはユーザーにそのパーミッションの付与を求める必要があります。Android には、パーミッションをリクエストするときに使用できるいくつかのメソッドがあります。これらのメソッドを呼び出すと、標準の Android ダイアログが開きます。このダイアログをカスタマイズすることはできません。

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

図 1 ユーザーにパーミッションの付与または拒否を求めるシステム ダイアログ

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

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

ユーザーが説明を必要とする可能性のある状況を特定するために、Android には、ユーティリティ メソッド shouldShowRequestPermissionRationale() が用意されています。アプリがパーミッションを既にリクエストしていて、ユーザーがそのパーミッションを拒否した場合、このメソッドは true を返します。

注: ユーザーがパーミッション リクエストを拒否し、パーミッション リクエストのシステム ダイアログで [Don't ask again] を選択した場合、このメソッドは false を返します。アプリがそのパーミッションを取得することがデバイス ポリシーで禁止されている場合も、このメソッドは false を返します。

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

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

次のコードは、ユーザーの連絡先を読み込むパーミッションがアプリにあることを確認し、必要に応じてパーミッションをリクエストします。

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

    // Should we show an explanation?
    if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
            Manifest.permission.READ_CONTACTS)) {

        // Show an expanation 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,
                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.
    }
}

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

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

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

@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 が渡されます。つまり、requestPermissions() を呼び出すときは、ユーザーとの直接的なやり取りは発生していないと想定できます。