◆PROCESSING 逆引きリファレンス
カテゴリー:スマホ(AndroidMode)
ストレージのファイルをアクセスには(AndroidMode編)
【概要】
PROCESSINGにAndroidMode を導入する事で、PROCESSINGで開発したプログラムをAndroid端末上で動かす事ができるようになります。
AndroidModeの導入については「PROCESSINGをAndroid端末で動かすには(4.0版)」記事を参照してください。
PCを始め多くのコンピュータには「ストレージ」と呼ばれる記憶装置がついています。PCではHDDやSSDの事ですね。
AndroidスマホにはNAND型のフラッシュメモリを利用したストレージが搭載されている事が多いようです。
Androidのストレージ領域は
- 内部領域(getFilesDirectory)
- 外部領域(getExternalFilesDirectory)
- 公開領域(getExternalStoragePublicDirectory)
と呼ばれる3つの領域から構成されています※。()内はこの領域にアクセスする代表的なAPIです。
※実際には、これにキャッシュ領域が加わります。
(画像URL:illust-AC 様:shinya さん、acworks さん)
内部領域はアプリケーション固有の情報を格納する場所で、他のアプリケーションから参照することはできません。この領域にアクセスするには、AndroidのAPIを利用しますが、PROCESSINGの標準命令でもアクセスすることが可能です。
外部領域は「外部」という名前とは裏腹に、必ずしも外部メモリ(SDカードなど)に持つわけではなく、内部ストレージに確保されることが多いようです。
外部領域のアプリケーション固有領域にアクセスするには、特別なパーミッションは必要ありません。
ただし外部領域はアプリケーション固有の領域であっても、自分以外のアプリケーションから読み取られる可能性があります。
ですので、重要な情報を含むファイルは外部領域に格納してはいけません。
どうしても外部領域に格納するのであれば、読み取られたり書き換えられても困らないファイルにするか、しかるべき暗号化を行うなどの対策が必要となるでしょう。
公開領域は、アプリケーションが他のアプリケーションと情報を共有するための領域です。(必ずではありませんが)この領域はSDカード上に確保されることがあります。
公開領域にアクセスするには特別な権限が必要となります。またこの領域はAndroid 6.0以降、セキュリティの観点からアクセスが厳しく制限されるようになっています。
参照URL:
- Qiita 様:Androidのファイル扱う際のセキュリティについて:@serisawaさん
- Taosoftware Developer Blog 様
- Qiita 様:iOS/Android/Windows10のデータ領域まとめメモ:@flat-8-kikiさん
- wizaman’s blog 様
今回はこのうち、内部領域および外部領域と呼ばれる場所にあるファイルをアクセスする方法を紹介します。
公開領域については「公開領域にアクセスするには(AndroidMode編)」を参照してください。
また以下の説明はAndroid6.0以降を対象としています。Android5.X以前とは方法が異なる場合がありますので、注意して下さい。
【詳細】
ストレージ領域へアクセスする
内部領域へアクセスする
自分の内部領域であれば特別なパーミッションがなくてもファイルをアクセスすることが可能です。
内部領域のファイルは、Activityがもつ getFilesDir ()メソッドで得られるファイル記述子を使ってアクセスする事ができます。
またPROCESSINGの標準命令である sketchPath や dataPath でパスを得ることや、saveStrings、loadStrings 命令で簡単なテキストファイルを書き込んだり読み込む事 も可能です。
PROCESSINGの saveStrings、loadStrings 命令については、以下の記事を参照してください。
.
内部領域へのFileオブジェクト取得File file = act . getFilesDir () ;
file : Fileインスタンス
act : Activityインスタンス
以下は、内部領域へのパスをもつ File オブジェクトを得る例です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
/** * PROCESSING 内部領域パス取得Sample * @auther MSLABO * @version 1.0 2018/7 */ final int FONT_SIZE = 16; File internalStragePath; void setup(){ //横向き固定 orientation(LANDSCAPE); //文字サイズ、色、アライメント指定 textSize(FONT_SIZE * displayDensity); textAlign(LEFT, TOP); fill(0); } void draw(){ background(255); //内部領域のパス取得 internalStragePath= getActivity().getFilesDir(); text(internalStragePath.getPath(), 0, 0); noLoop(); } |
<出力例>
上記例では内部領域のパスとして「/data/user/0/ pkg名 /files 」が表示されます。これとは異なるパスが表示されることもあります。
本記事のサンプル一覧に、内部領域からテキストファイルを読み出す例を掲載しましたので参考としてください。
外部領域へアクセスする
まず外部領域は「必ず読み書きできる」とは限りません。
なぜなら、ユーザーがストレージをPCにマウントしたり、外部ストレージを提供するSDカードを取り外す場合があるためです。
より慎重にプログラミングするなら、 Environment がもつ getExternalStorageState() メソッドで、外部領域の状態を確認してから操作するのが良いでしょう。
.
外部領域の状態を得るstatic String status = Environment . getExternalStorageState (File file ) ;
file : 外部領域の Fileオブジェクト
status : 外部領域の状態
status には、Environmentクラスが持つ以下の定数が戻されます。
MEDIA_UNKNOWN
MEDIA_REMOVED
MEDIA_UNMOUNTED
MEDIA_CHECKING
MEDIA_NOFS
MEDIA_MOUNTED
MEDIA_MOUNTED_READ_ONLY
MEDIA_SHARED
MEDIA_BAD_REMOVAL
MEDIA_UNMOUNTABLE
戻された値が Environment.MEDIA_MOUNTED であれば、外部領域は書き込み可能です。また Environment.MEDIA_MOUNTED_READ_ONLY であれば読み取りのみが可能です。
Android 4.4(APIレベル19)以降、自分の外部領域(アプリケーション固有の領域)であれば特別なパーミッションがなくてもアクセスする事ができるようになりました。
外部領域へアクセスするためには、 Activity がもつ getExternalFilesDir()メソッドでFileオブジェクトを取得します。
.
アプリ固有の外部領域へのFileオブジェクト取得File file = act . getExternalFilesDir(String subDirectory ) ;
file : 外部領域の Fileオブジェクト
act : Activityインスタンス
subDirectory : 外部領域配下のサブディレクトリパス
内部領域の場合もそうですが、アプリ固有の領域へのパスには、該当アプリケーションのパッケージ名が付加されます。
下記はアプリ固有の外部領域へのパスを取得し、アクセスできるか調べる例です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
import android.os.Environment; /** * PROCESSING 外部領域パス取得Sample * @auther MSLABO * @version 1.0 2018/7 */ final int FONT_SIZE = 14; File privateExt; void setup(){ //横向き固定 orientation(LANDSCAPE); //文字サイズ、色、アライメント指定 textSize(FONT_SIZE * displayDensity); textAlign(LEFT, TOP); fill(0); } void draw(){ background(255); //外部領域(アプリ固有部)のパス取得 privateExt= getActivity().getExternalFilesDir(""); text(privateExt.getPath(), 0, 0); //アクセスできるか調べる String status = Environment.getExternalStorageState(privateExt); if( status.contains(Environment.MEDIA_MOUNTED) ){ text("読み書き可能です。status = " + status, 0, FONT_SIZE * displayDensity); } else if(status.contains(Environment.MEDIA_MOUNTED_READ_ONLY)){ text("読み取り専用です。status = " + status, 0, FONT_SIZE * displayDensity); } else { text("アクセス不可です。status = " + status, 0, FONT_SIZE * displayDensity); } noLoop(); } |
<出力例>
上記例では外部領域のパスとして「/storage/emulated/0/Android/data/ pkg名 /files 」が表示されます。これとは異なるパスが表示されることもあります。
本記事のサンプル一覧に、外部領域のアプリケーション固有部にテキストファイルを書き込む例を掲載しましたので参考としてください。
【関連記事】
サンプルプログラム
内部領域からテキストを読む例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.FileInputStream; /** * PROCESSING 内部ストレージ TEXT READ Sample * @auther MSLABO * @version 1.0 2018/7 */ final String SEPARATOR = File.separator; final int FONT_SIZE = 16; File internalStragePath; void setup(){ //横向き固定 orientation(LANDSCAPE); //文字サイズ、色、アライメント指定 textSize(FONT_SIZE * displayDensity); textAlign(LEFT, TOP); fill(0); } void draw(){ background(255); //内部ストレージエリアのパス取得 internalStragePath = getActivity().getFilesDir(); text(internalStragePath.getPath(), 0, 0); //内部ストレージから sample.txt を読み込む String filePath = internalStragePath.getPath() + SEPARATOR + "sample.txt"; ArrayList<String> readData = readTextFile(filePath); if( readData != null){ //読み込めたら内容を表示 float y = FONT_SIZE * displayDensity; for(String data : readData){ text(data, 0, y); y = y + FONT_SIZE * displayDensity; } } } //TEXTファイルを読んでArrayListに詰める処理 public ArrayList<String> readTextFile(String filePath){ FileInputStream fis = null; InputStreamReader isr = null; BufferedReader br = null; ArrayList<String> readData = new ArrayList<String>(); try { File file = new File(filePath); if( !file.exists()){ //ファイルなし return null; } //文字コードはUTF-8として読む fis = new FileInputStream(file); isr = new InputStreamReader(fis, "UTF-8"); br = new BufferedReader(isr); //1行ごとに読み込む String line; while ((line = br.readLine()) != null) { readData.add(line); } } catch (IOException e) { e.printStackTrace(); return null; } finally { //後始末 try { if(br != null) { br.close(); } if(isr != null){ isr.close(); } if(fis != null){ fis.close(); } } catch (IOException e) { e.printStackTrace(); return null; } } return readData; } |
アプリケーションの内部領域(/data/data/ pkg名 /files )に、予め sample.txt というテキストファイルが格納されている事が条件です。
外部領域にテキストを書く例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
import android.os.Environment; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; import java.io.BufferedWriter; /** * PROCESSING 外部領域 テキストファイル書き込みSample * @auther MSLABO * @version 1.0 2018/7 */ final String SEPARATOR = File.separator; final int FONT_SIZE = 14; File privateExt; void setup(){ //横向き固定 orientation(LANDSCAPE); //文字サイズ、色、アライメント指定 textSize(FONT_SIZE * displayDensity); textAlign(LEFT, TOP); fill(0); } void draw(){ background(255); //外部領域(アプリ固有部)のパス取得 privateExt = getActivity().getExternalFilesDir(""); text(privateExt.getPath(), 0, 0); //アクセスできるか調べる String status = Environment.getExternalStorageState(privateExt); if( status.contains(Environment.MEDIA_MOUNTED) ){ //書き込むデータを作成する ArrayList<String> datas = new ArrayList<String>(); datas.add("回復薬"); datas.add("ひのきの棒"); datas.add("ロトの印"); //ファイルパスを生成する File file = new File(privateExt.getPath()+SEPARATOR+"勇者の持ち物.txt"); //書き込む boolean ret = writeTextFile(file, datas); if( ret == true ){ text("書き込みました。fileName=" + file.getName(), 0, FONT_SIZE * displayDensity); } noLoop(); } } //TEXTファイルを書き込む処理 public boolean writeTextFile( File file, ArrayList<String> datas) { FileOutputStream fos = null; OutputStreamWriter isw = null; BufferedWriter bw = null; try { //文字コードはUTF-8として書く fos = new FileOutputStream(file); isw = new OutputStreamWriter(fos, "UTF-8"); bw = new BufferedWriter(isw); //1行ごとに書き込む for (String data : datas) { bw.write(data + "\n"); } bw.flush(); } catch (FileNotFoundException e) { e.printStackTrace(); return false; } catch (UnsupportedEncodingException e) { e.printStackTrace(); return false; } catch( IOException e) { e.printStackTrace(); return false; } finally { //後始末 try { if(bw != null) { bw.close(); } if(isw != null){ isw.close(); } if(fos != null){ fos.close(); } } catch (IOException e) { e.printStackTrace(); return false; } } return true; } |
外部領域のアプリケーション固有部に、「勇者の持ち物.txt」というテキストファイルを書き込みます。
戻されたパスは「/storage/emulated/0/Android/data/ pkg名 /files」となっていますが、このパスはエミュレート領域であるため、実際に書き込まれる物理的なパスは異なるエリアになっています。
パスの上位フォルダ名が sdcard となっていますが、本当に SDカード 上に書き込まれるわけではないので、注意してください。
機種や環境により、実際の書き出し位置は変わることがあります。
本ページで利用しているアイコン画像は、下記サイト様より拝借しております。各画像の著作権は、それぞれのサイト様および作者にあります。