質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Android Studio

Android Studioは、 Google社によって開発された、 Androidのネイティブアプリケーション開発に特化した統合開発ツールです。

Q&A

解決済

1回答

3005閲覧

android6.0のRuntimepermission対応

subjectman15

総合スコア7

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Android Studio

Android Studioは、 Google社によって開発された、 Androidのネイティブアプリケーション開発に特化した統合開発ツールです。

3グッド

0クリップ

投稿2016/08/23 16:33

編集2016/08/26 08:09

###前提・実現したいこと
現在AndroidStudioでカメラアプリを作成しています。機能の作成は完了したのですが、作成したアプリを使用予定の実機がAndroid6.0で、Runtimepermissionの対応が必要なのですがなかなか上手くいきません。私の希望ではパーミッション許可の流れを以下のように進めたいです。

アプリ起動(インストール初回起動)

カメラ使用のパーミッション許可を求めるダイアログ

オーディオのパーミッション許可を求めるダイアログ

ストレージ使用のパーミッション許可を求めるダイアログ

カメラオープン(プレビュー表示)

という流れが理想です。

###発生している問題・エラーメッセージ
現在実機にアプリをインストールして起動すると、パーミッション許可を求めるダイアログが表示される前にアプリがRuntimeExceptionにより終了してしまっています。アプリが終了しました〜という旨のメッセージの後ろに許可を求めるダイアログ表示されており、アプリが終了しました〜のメッセージをOKを選択してアプリを完全に停止した後にパーミッション許可を押す形になってしまっています。しかも、このダイアログボックスが一度にすべて表示されません(参考にしたサイトでは1/3のように一度のダイアログ表示でよいようになっていた)なので、アプリ起動→終了→ダイアログ表示の動作を3回繰り返さなければならない状態にあります。(全てのパーミッションを許可すればアプリの起動に問題はありません)
どうかどのようにコーディングしていけば良いかご教授お願いします。

以下エラーログです。

08-26 17:04:36.424 6846-6846パッケージ名 I/Process: Sending signal. PID: 6846 SIG: 9 08-26 17:04:37.671 6940-6940パッケージ名 W/System: ClassLoader referenced unknown path: /data/appパッケージ名-1/lib/arm64 08-26 17:04:37.773 6940-6940パッケージ名 W/System: ClassLoader referenced unknown path: /data/appパッケージ名-1/lib/arm64 08-26 17:04:37.827 6940-6940パッケージ名 D/AndroidRuntime: Shutting down VM 08-26 17:04:37.827 6940-6940パッケージ名 E/AndroidRuntime: FATAL EXCEPTION: main Process: パッケージ名, PID: 6940 java.lang.RuntimeException: Unable to start activity ComponentInfo{パッケージ名パッケージ名.MainActivity}: java.lang.ArrayIndexOutOfBoundsException: length=2; index=2 at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2455) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2541) at android.app.ActivityThread.access$900(ActivityThread.java:169) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1382) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:148) at android.app.ActivityThread.main(ActivityThread.java:5554) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:746) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:636) Caused by: java.lang.ArrayIndexOutOfBoundsException: length=2; index=2 at パッケージ名.MainActivity.requestPermissions(MainActivity.java:123) at パッケージ名.MainActivity.checkPermission(MainActivity.java:112) at パッケージ名.MainActivity.onCreate(MainActivity.java:50) at android.app.Activity.performCreate(Activity.java:6285) at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1108) at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2408) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2541)  at android.app.ActivityThread.access$900(ActivityThread.java:169)  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1382)  at android.os.Handler.dispatchMessage(Handler.java:102)  at android.os.Looper.loop(Looper.java:148)  at android.app.ActivityThread.main(ActivityThread.java:5554)  at java.lang.reflect.Method.invoke(Native Method)  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:746)  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:636) 

###該当のソースコード

public class MainActivity extends Activity implements View.OnClickListener { private SurfaceView mySurfaceView; private Camera camera; private static final String TAG = "log"; private SurfaceHolder holder = null; private boolean recording = false; MediaRecorder mediaRecorder; private final int REQUEST_PERMISSION = 1000; private Button btn1; private Button btn2; @Override protected void onCreate(Bundle savedInstanceState) { checkPermission(); Log.v( "Check", "permission" ); super.onCreate( savedInstanceState ); File dirs = new File( "storage" ); if (!dirs.exists()) { dirs.mkdir(); } } private void prepareCamera(){ setContentView( R.layout.activity_main ); btn1 = (Button) findViewById( R.id.button1 ); btn1.setOnClickListener( this ); btn2 = (Button) findViewById( R.id.rebtn ); btn2.setOnClickListener( this ); mySurfaceView = (SurfaceView) findViewById( R.id.surface_view ); holder = mySurfaceView.getHolder(); holder.addCallback( callback ); Log.v( "Holder", "ok" ); holder.setType( SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS ); Log.v( TAG, "Holder" + holder ); } private boolean checkPermission() { boolean camP; boolean recP; boolean strP; if (ActivityCompat.checkSelfPermission( this, Manifest.permission.CAMERA ) != PackageManager.PERMISSION_GRANTED) { camP=false; }else { camP=true; } if (ActivityCompat.checkSelfPermission( this, Manifest.permission.RECORD_AUDIO ) != PackageManager.PERMISSION_GRANTED) { recP=false; }else { recP=true; } if (ActivityCompat.checkSelfPermission( this, Manifest.permission.WRITE_EXTERNAL_STORAGE ) != PackageManager.PERMISSION_GRANTED) { strP=false; }else { strP=true; } requestPermissions(camP,recP,strP); Log.v( "request","permission" ); return camP && recP && strP; } private void requestPermissions(boolean camP, boolean recP, boolean strP) { boolean pList[]= new boolean[2]; pList[0] = camP; pList[1] = recP; pList[2] = strP; for (int i=0;i<pList.length;i++){ if (pList[i]){ switch (i){ case 0: ActivityCompat.requestPermissions( MainActivity.this, new String[]{Manifest.permission.CAMERA}, REQUEST_PERMISSION ); break; case 1: ActivityCompat.requestPermissions( MainActivity.this, new String[]{Manifest.permission.RECORD_AUDIO}, REQUEST_PERMISSION ); break; case 2: ActivityCompat.requestPermissions( MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_PERMISSION ); break; } } } } @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { if (requestCode == REQUEST_PERMISSION) { // 使用が許可された if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { prepareCamera(); return; } } } 以下文字数の関係で省略。

###試したこと
課題に対してアプローチしたことを記載してください
上記のソースコードは以下のサイトを参考にして書きました

https://akira-watson.com/android/runtime-permission.html

http://firespeed.org/diary.php?diary=kenz-1821

http://qiita.com/incheon/items/178cb540b697cd9419fd

###補足情報(言語/FW/ツール等のバージョンなど)
環境:AndroidStudio2.1.1

KiyoshiMotoki, yodel, ikuwow👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

onCreateでcheckPermissionの結果を見ないで処理を続けているので例外が発生していますね。
checkPermissionで1つでもパーミッションが不足している場合はパーミッションが必要な処理を行ってはいけません。

また、ダイアログが複数出るのは複数出すように記述しているので当たり前ですね。
ActivityCompat.requestPermissionsの第二引数の配列に不足しているパーミッションをまとめて指定するようにしてください。

投稿2016/08/23 16:58

yona

総合スコア18155

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

subjectman15

2016/08/23 23:35

早速のご回答ありがとうございます。 onCreateでcheckPermissionの結果を見ないで処理を続けているとのことですが、どのように記述を変えていけば結果見てから処理を始める、という流れにしていけるのでしょうか? 経験が浅いので妙な質問になってしまっているかもしれませんがよろしくお願いします。
yona

2016/08/24 00:37

メソッドの戻り値をbooleanにして戻り値をif文で判定してください。
subjectman15

2016/08/24 02:21

ダイアログはまとめて表示できるようになりましたありがとうございます! ただ以下のように戻り値をbooleanにして書き直してみたのですが同じようにエラーが発生してしまいました。 private boolean checkPermission() { if (ActivityCompat.checkSelfPermission( this, Manifest.permission.CAMERA ) != PackageManager.PERMISSION_GRANTED) { if (ActivityCompat.checkSelfPermission( this, Manifest.permission.RECORD_AUDIO ) != PackageManager.PERMISSION_GRANTED) { if (ActivityCompat.checkSelfPermission( this, Manifest.permission.WRITE_EXTERNAL_STORAGE ) != PackageManager.PERMISSION_GRANTED) { requestPermission(); } } return false; }else { return true; } } private void requestPermission() { if (ActivityCompat.shouldShowRequestPermissionRationale( this, Manifest.permission.CAMERA )) { ActivityCompat.requestPermissions( MainActivity.this, new String[]{Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO, Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_PERMISSION ); } else { ActivityCompat.requestPermissions( this, new String[]{Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO, Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_PERMISSION ); } } @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { if (requestCode == REQUEST_PERMISSION) { // 使用が許可された if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { Camera.open(); return; } else { // それでも拒否された時の対応 Toast toast = Toast.makeText( this, "これ以上なにもできません", Toast.LENGTH_SHORT ); toast.show(); } } }
yona

2016/08/24 02:35

このコードだと1つでもパーミッションが許可されていた場合はダイアログでないですよね? また、戻り値を判定するのでonCreateの処理を変更してください。 あと、エラーログも追記してください。
subjectman15

2016/08/24 03:08

確かに実機で一つ許可した状態で確認したところダイアログがでなくなりました。 checkPermissionのif文の書き方が間違えているということでしょうか? また onCreateの処理を変更ということですが if(Build.VERSION.SDK_INT >= 23){ checkPermission(); } の部分を変更するという認識でしょうか? エラーコードは以下に記載します。何度も申し訳ありません。 08-24 11:13:46.255 19824-19979/パッケージ名 I/OpenGLRenderer: Initialized EGL, version 1.4 08-24 11:13:46.272 19824-19824/パッケージ名 W/CameraBase: An error occurred while connecting to camera: 0 08-24 11:13:46.275 19824-19824/パッケージ名 D/AndroidRuntime: Shutting down VM 08-24 11:13:46.275 19824-19824/パッケージ名 E/AndroidRuntime: FATAL EXCEPTION: main Process: パッケージ名, PID: 19824 java.lang.RuntimeException: Fail to connect to camera service at android.hardware.Camera.<init>(Camera.java:702) at android.hardware.Camera.open(Camera.java:489) at パッケージ名.MainActivity$1.surfaceCreated(MainActivity.java:166) at android.view.SurfaceView.updateWindow(SurfaceView.java:597) at android.view.SurfaceView$3.onPreDraw(SurfaceView.java:179) at android.view.ViewTreeObserver.dispatchOnPreDraw(ViewTreeObserver.java:944) at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2073) at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1125) at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6058) at android.view.Choreographer$CallbackRecord.run(Choreographer.java:858) at android.view.Choreographer.doCallbacks(Choreographer.java:670) at android.view.Choreographer.doFrame(Choreographer.java:606) at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:844) at android.os.Handler.handleCallback(Handler.java:739) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:148) at android.app.ActivityThread.main(ActivityThread.java:5554) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:746) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:636)
yona

2016/08/24 03:17

checkPermissionは間違っています。 各if文は入れ子にせず、並べる必要があります。 ・メソッドの先頭でboolean型変数を3つ作ります。 ・各変数はそれぞれパーミッションの有無を表します。 ・各パーミッションチェックのif文でtrue/falseを切り替えてください。 ・3つのbooleanをrequestPermissionに渡し、対応するパーミッションを配列化し、ダイアログを表示します。 onCreateについて checkPermissionメソッドを呼び出してダイアログが表示されたらonCreateは中断されると考えていませんか?中断されません。 戻り値を判定し、1つでもパーミッションが無い場合は後続の処理を行ってはいけません。 また、エラーログは質問に追記してください。
subjectman15

2016/08/24 05:18

ありがとうございます。 少しむずかしかったのでいくつか確認させていただきたいです。 checkPermissionの記述の変更について 以下のように変更しました。 private boolean checkPermission() { boolean a; boolean b; boolean c; if (ActivityCompat.checkSelfPermission( this, Manifest.permission.CAMERA ) != PackageManager.PERMISSION_GRANTED) { a=false; return a; } if (ActivityCompat.checkSelfPermission( this, Manifest.permission.RECORD_AUDIO ) != PackageManager.PERMISSION_GRANTED) { b=false; return b; } if (ActivityCompat.checkSelfPermission( this, Manifest.permission.WRITE_EXTERNAL_STORAGE ) != PackageManager.PERMISSION_GRANTED) { c=false; return c; } return false; } これでyona様のおっしゃっていたような形になっているでしょうか?そして >・3つのbooleanをに渡し、対応するパーミッションを配列化し、ダイアログを表示します。 についてなのですが簡潔に不明点を表すと ・どのタイミングでrequestPermissionを呼ぶのかがわからなくなってしまいました。 ・対応するパーミッションを配列化し、ダイアログを表示というのは渡されたbooleanのうちパーミッションが許可されていないものだけをわけてダイアログを表示するようにするという認識で良いのでしょうか? また、それで良いとした時にどのようにコーディングしていけばよいのかがわからないです。 onCreateについて ご指摘の通りcheckPermissionメソッドを呼び出してダイアログが表示されたらonCreateは中断されると考えていました…どのように内容を変更すればダイアログ表示中onCreateを中断することができるのでしょうか? たびたびの質問まことに申し訳ありません、知識も経験も浅く右も左もわからない状態です。どうか何卒ご助力お願いします。 ※エラーログを質問に追記しました。お目汚し申し訳ありません。
yona

2016/08/24 06:08 編集

まず、変数を作ったのはokです、ただし名前はしっかりつけましょう。混乱の芽は潰しておきましょう。 次に各チェックのあとにreturnしてしまうのは誤りです、とにかく全てのパーミッションをチェックする必要があります。 全てのパーミッションチェックが終わったあとにrequestPermissionsを呼びます。この時引数として3つのbooleanを渡します、こうすることでrequestPermissionsの中でどのパーミッションが不足しているかがわかるので、表示すべきダイアログの内容を決めることができます。 最後に3つのbooleanの積をreturnすることで呼び出し元にパーミッションの不足を返すことができます。 onCreateについては下記を実装してください。 ・初期化処理をメソッド化する。 ・Android6.0以前のコードなら初期化メソッドを呼び出す。 ・Android6.0以降かつパーミッションが全て許可されていたら初期化メソッドを呼び出す。 ・Android6.0かつ1つでもパーミッションが許可されていなければ初期化メソッドを呼び出さない。 ・ダイアログで全てのパーミッションが許可されたら初期化メソッドを呼ぶ ・ダイアログで1つでもパーミッションの許可が得られなかったらアプリ終了または再度パーミッションチェックを行う。
subjectman15

2016/08/24 06:36 編集

ありがとうございます。 checkPermissionの記述について private boolean checkPermission() { boolean camP; boolean recP; boolean strP; if (ActivityCompat.checkSelfPermission( this, Manifest.permission.CAMERA ) != PackageManager.PERMISSION_GRANTED) { camP=false; }else { camP=true; } if (ActivityCompat.checkSelfPermission( this, Manifest.permission.RECORD_AUDIO ) != PackageManager.PERMISSION_GRANTED) { recP=false; }else { recP=true; } if (ActivityCompat.checkSelfPermission( this, Manifest.permission.WRITE_EXTERNAL_STORAGE ) != PackageManager.PERMISSION_GRANTED) { strP=false; }else { strP=true; } requestPermissions(camP,recP,strP); return false; } private void requestPermissions() { if (ActivityCompat.shouldShowRequestPermissionRationale( this, Manifest.permission.CAMERA )) { ActivityCompat.requestPermissions( MainActivity.this, new String[]{Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO, Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_PERMISSION ); Log.v( "request", "permission" ); } else { ActivityCompat.requestPermissions( this, new String[]{Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO, Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_PERMISSION ); Log.v( "request", "permission" ); } } と記述したのですがrequestPermissions(camP,recP,strP);を呼んだときに引数にエラーが出てしまいます。 後、booleanの積というのは何でしょうか?調べたのですが良くわからず…
yona

2016/08/24 06:49

エラーメッセージをしっかり読みましょう、問題点が書いてあるはずです。requestPermissionsに引数を書き足してください。 また、論理積&&について調べてください。
subjectman15

2016/08/24 09:13 編集

ありがとうございます。以下のように記述したことでエラーは解消されました。 requestPermissions(camP,recP,strP); return camP && recP && strP; } private void requestPermissions(boolean camP, boolean recP, boolean strP) { if (camP && recP && strP != true){ if (ActivityCompat.shouldShowRequestPermissionRationale( this, Manifest.permission.CAMERA )) { ActivityCompat.requestPermissions( MainActivity.this, new String[]{Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO, Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_PERMISSION ); } else { ActivityCompat.requestPermissions( this, new String[]{Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO, Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_PERMISSION ); } } } requestPermissionsのメソッドもboolean変数を反映させたものに変更してみたのですがこちらで実機の動作確認を行ったのですがダイアログが表示されませんでした。アプリが強制終了せず真っ暗な画面のままになっています。boolean変数の反映のさせ方に問題があったのでしょうか?
yona

2016/08/24 09:39

何のために3つのbooleanを引数にしたかを考えてください。 また、初期化処理メソッドにはパーミッションに関係する処理だけを書いてください。パーミッションに関係無いものはonCreateに戻してください。
subjectman15

2016/08/25 02:28

申し訳ありません、一晩考えましたがわかりませんでした。 3つのbooleanの積がtureでなかった場合、つまり3つの中で1つでもfalseがある場合にパーミッションのリクエストを行うというイメージでコーディングをしたつもりだったのですが、そもそもcheckPermissionのreturnの方に問題があったのでしょうか? onCreateに関してですが以下のような認識でいるのですがまちがっていたら申し訳ありません ・onCreateの上に初期化処理メソッドを作成。 ・まず初期化処理メソッド(パーミッションのチェックなど)を行う。 ・初期化処理メソッドが終わってからonCreateを呼び出す。
yona

2016/08/25 03:09

3つのbooleanはそれぞれがパーミッションの許可、不許可を表しています。なのでrequestPermissionsの中で不許可のパーミッションに対してだけパーミッションを要求することができます。 onCreateの上っていうのはファイル上の記述順を指していますか?JAVAは記述順と実行順は関係ありません。 1-onCreate内でcheckPermissionメソッドを呼び出す。 2-checkPermissionの戻り値がfalseだった場合はパーミッションが必要な処理は行わない 3-onCreate内でパーミッションが必要無い処理を書く また、質問に最新のプログラムを追記してください。
subjectman15

2016/08/25 07:39

Permissionsの中で不許可のパーミッションに対してだけパーミッションを要求するというのはrequestPermissionsのif文の条件式あたる部分にboolean変数を入れて判定を行うという認識で正しいでしょうか?でもそうしてしまうとif文を3つにしなけらばならないように思います。 申し訳ありませんcheckPermissionから渡されたboolean変数の扱い方がいまいちわかっていない状態です。 ※質問のコードを現在編集中のものに更新しました。
yona

2016/08/25 10:51

requesPermissionの中でパーミッションのチェックはしなくていいです、3つのパーミッションからリクエストするパーミッションの配列を作ってください。 preaparCameraの中からパーミッションが不要な処理をonCreateに戻してください。
subjectman15

2016/08/26 08:39

ありがとうございます。コードとエラーメッセージを更新しました。 受け取った変数で配列をつくり、falseを検出したらパーミッションをリクエストするように処理を組んでみたのですがエラーが出てしまっています。配列の使用法にエラーが出ているようなのですが、配列の作り方に問題があるのでしょうか?
yona

2016/08/27 04:46

エラーメッセージを読んでください。 配列にはついては自分で調べましょう。 また、boolean型ではなくString型のパーミッションの配列にしてください。
subjectman15

2016/08/30 03:42

ありがとうございます!無事に問題を解決することができました!この度は長いことお付き合いいただきありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問