DXライブラリ Android版を使用した Androidアプリで Java のコードでストレージ使用の権限をリクエストする
( Android Studio編 )


 すべての処理を C++ で実行できれば良いのですが、Android の基本言語が Java である関係で、
今のところ Java を使用しないとストレージ内の標準で許可されているフォルダ以外にアクセスできません。

 なので、ここでは標準で許可されているフォルダ以外のフォルダ( /storage/emulated/0/Download フォルダ内のファイルなど )へ
アクセスするための権限を Java を使用してリクエストするための手順を記します。


 1.『AndroidManifest.xml』の内容をストレージの使用と Java のコードを実行できるように変更する

 2.Java のコードを入力する

 3.Java の情報を取得する C++ のコードを入力する



1.プロジェクトの設定と『『AndroidManifest.xml』の内容をストレージの使用と Java のコードを実行できるように変更する

  Android Studio『DXライブラリ Android版』を使用する Androidアプリのプロジェクトを開き
 画面左側のリストの『app』→『manifests』の中にある『AndroidManifest.xml』
 ダブルクリックして、内容を表示します。

  『AndroidManifest.xml』の内容は、使い方ページに沿って編集した場合は以下のようになっていると思いますが、
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.dxlibtest_androidstudio"> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme" android:hasCode="false"> <activity android:name="android.app.NativeActivity"> <meta-data android:name="android.app.lib_name" android:value="native-lib" /> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>

  この中の以下のように変更します。(色が緑の部分が変更箇所や追加箇所で、4箇所です)
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.dxlibtest_androidstudio"> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme" android:hasCode="true"> <activity android:name=".MainActivity"> <meta-data android:name="android.app.lib_name" android:value="native-lib" /> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
 


2.Java のコードを入力する

  Android Studio の画面左側のリストの『app』→『java』の中には使い方ページに沿ってプロジェクト名を
 『test』にした場合は『com.example.test』があり、更にその中に『MainActivity』がありますので、
 ダブルクリックして内容を表示すると最初から30行ほど書かれていますが、それを全部消して、代わりに
 今回の『ストレージの使用権限のリクエスト』を行うための以下のコードを入力します。
package com.example.(プロジェクト名); import android.app.NativeActivity; import java.lang.Runnable; import android.content.pm.PackageManager; import android.widget.Toast; import android.Manifest; public class MainActivity extends NativeActivity { // ストレージの使用許可の状態( 0:使用可能かチェック中 1:使用可能 2:使用不可能 ) int mStorageState ; // ストレージ使用の権限をリクエストする際に使用する識別番号 static final int PERMISSIONS_REQUEST_STORAGE = 1 ; // 権限の許可を求めるダイアログで許可か不許可が選択されたら呼ばれる関数 public void onRequestPermissionsResult( int requestCode, String[] permissions, int[] grantResults ) { // ストレージ使用の権限のリクエストに対する結果の場合のみ処理を行う if( requestCode == PERMISSIONS_REQUEST_STORAGE ) { // 許可されたのかどうかを判定 if( grantResults[ 0 ] == PackageManager.PERMISSION_GRANTED ) { // 許可されたらその旨表示 Toast.makeText( MainActivity.this, "ストレージの使用が許可されました", Toast.LENGTH_SHORT ).show() ; // 許可された事を示す 1 を代入 mStorageState = 1 ; } else { // 許可されなかったらその旨を表示 Toast.makeText( MainActivity.this, "ストレージの使用が拒否されました", Toast.LENGTH_SHORT ).show() ; // 許可されなかった事を示す 2 を代入 mStorageState = 2 ; } } } // ストレージ使用の権限をチェックして、権限が無かったら使用許可問い合わせの表示を行う public void CheckStoragePermission() { // UIスレッドで実行する処理を登録する runOnUiThread( new Runnable() { // UIスレッドで呼ばれる関数 @Override public void run() { // Android のバージョンチェック if( android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M ) { // Android 6.0以上の場合はアプリ実行中にストレージ使用の権限があるかをチェックする // ストレージ使用の権限があるか判定 if( checkSelfPermission( Manifest.permission.READ_EXTERNAL_STORAGE ) == PackageManager.PERMISSION_GRANTED && checkSelfPermission( Manifest.permission.WRITE_EXTERNAL_STORAGE ) == PackageManager.PERMISSION_GRANTED ) { // ストレージ使用の権限があれば mStorageState に 1 を代入 mStorageState = 1 ; // メッセージを表示 Toast.makeText( MainActivity.this, "このアプリはストレージ情報取得の権限が既にあります", Toast.LENGTH_SHORT ).show() ; } else { // ストレージ使用の権限が無ければ権限のリクエストを行う requestPermissions( new String[]{ Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE }, PERMISSIONS_REQUEST_STORAGE ) ; } } else { // Android 6.0 未満の場合は AndroidManifest.xml の記述だけで許可されるので 1 を代入する mStorageState = 1 ; } } } ) ; } }
 ( (プロジェクト名)となっている箇所は、お手元のプロジェクトの名前を入力してください )



3.Java の情報を取得する C++ のコードを入力する

  次に Java のストレージ使用権限のリクエストを行う関数を呼び出したり、権限リクエストの結果を取得する C++ 側のプログラムです。
  『DXライブラリ Android版』でのみ使用できる GetNativeActivity という関数を使用しています。
#include "DxLib.h" #include <string.h> int android_main( void ) { JNIEnv *env ; const ANativeActivity *NativeActivity ; int StorageState = 0 ; // 背景を灰色にする SetBackgroundColor( 128,128,128 ) ; // DXライブラリの初期化 if( DxLib_Init() < 0 ) return -1 ; // 描画先を裏画面に変更 SetDrawScreen( DX_SCREEN_BACK ) ; // アプリの NativeActivity を取得しておく NativeActivity = GetNativeActivity() ; // Java の関数 CheckStoragePermission の呼び出し { // JavaVM とソフト実行用スレッドを関連付け( C++ から Java の機能を使用するために必要 ) if( NativeActivity->vm->AttachCurrentThreadAsDaemon( &env, NULL ) != JNI_OK ) { return -1 ; } // Java のクラス MainActivity を取得 jclass jclass_MainActivity = env->GetObjectClass( NativeActivity->clazz ) ; // Java のクラス MainActivity のメンバー関数 CheckStoragePermission の ID を取得 jmethodID jmethodID_CheckStoragePermission = env->GetMethodID( jclass_MainActivity, "CheckStoragePermission", "()V" ) ; // Java のクラス MainActivity のメンバー関数 CheckStoragePermission の呼び出し env->CallVoidMethod( NativeActivity->clazz, jmethodID_CheckStoragePermission ) ; // Java のクラス MainActivity の参照を削除 env->DeleteLocalRef( jclass_MainActivity ) ; // JavaVM とソフト実行用スレッドの関連付け終了 NativeActivity->vm->DetachCurrentThread() ; } // ストレージ使用が許可されるか、拒否されるまでループ while( ProcessMessage() == 0 && StorageState == 0 ) { // 裏画面の内容をクリア ClearDrawScreen() ; // JavaVM とソフト実行用スレッドを関連付け( C++ から Java の機能を使用するために必要 ) if( NativeActivity->vm->AttachCurrentThreadAsDaemon( &env, NULL ) != JNI_OK ) { return -1 ; } // Java のクラス MainActivity を取得 jclass jclass_MainActivity = env->GetObjectClass( NativeActivity->clazz ) ; // Java のクラス MainActivity のメンバー変数 mStorageState の ID を取得 jfieldID jfieldID_mStorageState = env->GetFieldID( jclass_MainActivity, "mStorageState", "I" ) ; // Java のクラス MainActivity のメンバー変数 mStorageState の値をローカル変数 StorageState に代入 StorageState = env->GetIntField( NativeActivity->clazz, jfieldID_mStorageState ) ; // Java のクラス MainActivity の参照を削除 env->DeleteLocalRef( jclass_MainActivity ) ; // JavaVM とソフト実行用スレッドの関連付け終了 NativeActivity->vm->DetachCurrentThread() ; // メッセージを表示 DrawString( 0, 0, "ストレージ使用の許可待ち中", GetColor( 255,255,255 ) ) ; // 裏画面の内容を表画面に反映 ScreenFlip() ; } // ストレージ使用が許可されたかどうかで処理を分岐 ClearDrawScreen(); if( StorageState == 2 ) { // ストレージ使用が許可されなかった場合 DrawString( 0, 0, "ストレージ使用は拒否されました", GetColor( 255,255,255 ) ) ; } else { // ストレージ使用が許可された場合はダウンロードフォルダ内のファイルのサイズを取得( ファイルのパスは仮です ) LONGLONG FileSize = FileRead_size( "/storage/emulated/0/Download/test.bin" ); DrawFormatString( 0, 0, GetColor( 255,255,255 ), "ストレージ使用は許可されました ファイルサイズ : %dbyte", ( int )FileSize ); } ScreenFlip(); WaitKey(); // DXライブラリの後始末 DxLib_End() ; // ソフトの終了 return 0 ; }
 

  以上です。
  これでプロジェクトを実行すると、画面にストレージが使用可能かや、使用可能な場合はファイルのサイズが表示されます。
( ファイルのサイズが正常に表示されるには "/storage/emulated/0/Download/test.bin" ←こちらの部分を実際に存在するファイルのパスにする必要がありますが… )

  詳細は割愛しますが、C++ から Java にアクセスする基本的な流れは。

   ① AttachCurrentThreadAsDaemon でソフト実行用スレッドと JavaVM を関連付け、JNIEnv も取得。

   ② GetObjectClass でアプリの Java クラスを取得。

   ③ GetMethodID や GetFieldID で Java の関数や変数の ID を取得。

   ④ Call???Method や Get???Field で Java の関数の呼び出しや変数の値を取得。

   ⑤ DeleteLocalRef で Get???Field で取得したオブジェクト系の変数の参照を削除。

   ⑥ DeleteLocalRef で GetObjectClass で取得したアプリの Java クラスの参照を削除。

   ⑦ DetachCurrentThread でソフト実行用スレッドと JavaVM の関連付けを終了。

  となります。
  C++ と Java でのやり取りを行う機能( JNI( Java Native Interface ) ) の詳しい扱い方については、検索サイトで
 『Android Java JNI』などのキーワードで検索して調べてみてください。





戻る