Android6.0(APIレベル23以上)で、アプリに権限を与える

アプリに権限を与えようとした時に詰まったので、備忘用に。

やること

APIレベル23から、Androidでアプリを動かす際に必要なパーミッションが、アプリインストール時ではなく、その機能を使う時に取得するようになったため、AndroidManifestへの記述だけでなく、そのためのコードが必要になった(今更)。
この権限を与える処理が非常にめんどくさかったので備忘を兼ねて投稿。
ただ、まだ理解が追いついておらず、このやり方で効率が良いのかは確認できていない。
余分なものが入っていないコードはまとめに記載。

やったこと

前提として、AndroidManifestにパーミッション関連を記述しておく。

onCreate

今回はアプリ起動時に権限を付与することを想定して、onCreateメソッドから。

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    //カメラ権限の確認
    if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CAMERA)!= PackageManager.PERMISSION_GRANTED) {//権限がまだ無い場合
        if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this, Manifest.permission.CAMERA)) {//明示的に権限が拒否されていた時
            //拒否されていた時の処理、なんかしらのメッセージを出すと良いと思われる、今は何も考えずにパーミッションを聞いておく
            ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.CAMERA}, 1);
        } else {//まだ聞いてなかったとき
            //与えても良いか聞く、onRequestPermissionsResultが答えを受ける
            ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.CAMERA}, 1);
        }
    }else {//権限がある場合
        initCameraView();//そのままカメラをセッティング
    }
}

※initCameraViewメソッドは自作したもので、この記事の内容には関係無し

ここではカメラ権限のチェックと、権限が無かった場合の付与を呼び出している。
大半はコード中のコメントに書いた通りで、権限のチェックには

  • 現在権限が与えられているか
    • 権限が無かった場合どうするか
      • 権限が無いのは、ユーザーに拒絶されているからか
        • 拒絶されているならどうするか
        • まだ聞いていないならどうするか
    • 権限が有った場合どうするか

といったような手順が必要。

権限を与えるために呼び出すメソッドは以下。

ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.CAMERA}, 1);

Manifest.permission.CAMERAとなっている部分は取得する権限を意味する。
末尾で与えている定数の1は、後述するonRequestPermissionsResultメソッドで利用する。

onRequestPermissionsResult

このメソッドは、権限付与のために呼び出したrequestPermissionsメソッドに対する返答を受け取るメソッド。
これを書かないと権限の有無を確認しないまま処理が開始されてしまい、権限が無いまま機能を利用しようとしてアプリがSecurityExceptionで落ちたりする。

@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
    switch (requestCode) {
        case 1: { //ActivityCompat#requestPermissions()の第2引数で指定した値
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {//許可された場合
                setContentView(R.layout.activity_main);
                initCameraView();
            }else{//拒否された場合の処理
                //とりあえず終了しておく
                MainActivity.this.finish();
            }
            break;
        }
    }
}

ここでrequestCodeをswitchに渡しているが、この数字が先ほどのrequestPermissionsメソッドに与えた定数。
先ほどは1を渡したので、1に関してはカメラの権限付与がどうなったか確認を行っている。
幾つかの権限を与える最には、enumなりを使ってrequestCordを管理した方が良いかも?
とりあえずここまで実装しておけばアプリに権限を与えることができた。

蛇足

onRequestPermissionsResultにて、initCameraの前にsetContentViewを行っているが、これをしないとカメラを起動する前にアプリが落ちた。
原因を理解する前に多分ビューの切り替えがおかしいんだろと予想して切り替えを記述してみたが、ドンピシャだったので何故落ちるか理解できていない。

まとめ

今回載せたコードから、余計なものを取り除いてまとめたものが以下。

@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
    switch (requestCode) {
        case 1: { //requestPermissions()の第2引数で指定した値
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {//許可された場合
            }else{//拒否された場合の処理
            }
            break;
        }
    }
}

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    //カメラ権限の確認
    if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CAMERA)!= PackageManager.PERMISSION_GRANTED) {//権限がまだ無い場合
        if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this, Manifest.permission.CAMERA)) {//明示的に権限が拒否されていた時
            //拒否されていた時の処理
        } else {//まだ聞いてなかったとき
            //与えても良いか聞く、onRequestPermissionsResultが答えを受ける
            ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.CAMERA}, 1);
        }
    }else {//権限がある場合
    }
}