アプリ固有の内部ストレージにファイルを保存する方法について見てみましょう。
2024.1.1
アプリ固有の内部ストレージ領域
ストレージには内部ストレージ(Internal Storage)と外部ストレージ(External Storage)があります。アプリには固有のストレージ領域があり、ここに起動時に必要な設定データなどを保存させることができます。
- 内部ストレージ:
- 常に使用できる
- ここに保存されたファイルは、自分のアプリからのみアクセスできる
- アプリがアンインストールされると削除される
- ユーザーからも他のアプリからも、自分のファイルにアクセスできないようにしたい場合に適する
- アクセスPermissionは不要
- ストレージ領域は限られているので大きなファイルは保存できない
- アプリ固有の外部ストレージ:
アプリがアンインストールされても残った方がいい場合などでは、共有のメディアや他の外部ストレージに保存することも可能です。
getFilesDir()
を使ってアプリ固有の内部ストレージにアクセスできます。
1 |
File file = new File(context.getFilesDir(), fileName); |
getFilesDir()はパスを返します。
/data/data/[package_name]/files/
adb shell コマンドではセキュリティーでブロックされて見えませんが、run-as を使えばデバック状態であれば見ることはできます。
サンプルコード
下の例は、 EditText で入力した文字列をTestFile.txt というファイルに入れて保存し、そのファイルから読み出す例です。
MainActivity.java
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 |
//package com.example.testprivateinternalstorage; import android.content.Context; import android.os.Bundle; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import androidx.activity.EdgeToEdge; import androidx.appcompat.app.AppCompatActivity; import androidx.core.graphics.Insets; import androidx.core.view.ViewCompat; import androidx.core.view.WindowInsetsCompat; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; public class MainActivity extends AppCompatActivity { private TextView textView; private EditText editText; private File file; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); EdgeToEdge.enable(this); setContentView(R.layout.activity_main); ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> { Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()); v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom); return insets; }); Context context = getApplicationContext(); String fileName = "TestFile.txt"; file = new File(context.getFilesDir(), fileName); textView = findViewById(R.id.text_view); editText = findViewById(R.id.edit_text); Button buttonSave = findViewById(R.id.button_save); // lambda buttonSave.setOnClickListener( v -> { // エディットテキストのテキストを取得 String text = editText.getText().toString(); saveFile(text); if(text.isEmpty()){ textView.setText(R.string.no_text); } else{ textView.setText(R.string.saved); } }); Button buttonRead = findViewById(R.id.button_read); // lambda buttonRead.setOnClickListener( v -> { String str = readFile(); if (str != null) { textView.setText(str); } else { textView.setText(R.string.read_error); } }); } // ファイルを保存 public void saveFile(String str) { // try-with-resources try (FileWriter writer = new FileWriter(file)) { writer.write(str); } catch (IOException e) { throw new RuntimeException(e); } } // ファイルを読み出し public String readFile() { String text = null; // try-with-resources try ( BufferedReader br = new BufferedReader(new FileReader(file)) ) { text = br.readLine(); } catch (IOException e) { throw new RuntimeException(e); } return text; } } |
activity_main.xml
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 |
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/main" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <EditText android:id="@+id/edit_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="50dp" android:autofillHints="@string/hint" android:background="#fff" android:hint="@string/hint" android:inputType="text" android:textSize="30sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.05" /> <Button android:id="@+id/button_save" android:layout_width="0dp" android:layout_height="wrap_content" android:text="@string/save_file" android:textSize="20sp" android:layout_marginStart="20dp" android:layout_marginTop="20dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toLeftOf="@+id/button_read" app:layout_constraintTop_toBottomOf="@+id/edit_text" app:layout_constraintVertical_bias="0.0" /> <Button android:id="@+id/button_read" android:layout_width="0dp" android:layout_height="wrap_content" android:text="@string/read_file" android:textSize="20sp" android:layout_marginEnd="20dp" android:layout_marginTop="20dp" app:layout_constraintLeft_toRightOf="@+id/button_save" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toBottomOf="@+id/edit_text" /> <TextView android:id="@+id/text_view" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/app_name" android:textColor="#000" android:textSize="30sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.4" /> </androidx.constraintlayout.widget.ConstraintLayout> |
リソースファイル
strings.xml
1 2 3 4 5 6 7 8 9 |
<resources> <string name="app_name">TestPrivateInternalStorage</string> <string name="no_text">"No text !"</string> <string name="saved">Saved</string> <string name="read_error">Read file error !</string> <string name="hint">文字を入力</string> <string name="save_file">Save file</string> <string name="read_file">Read file</string> </resources> |
一連の動作を確認してみます。
実際に保存したファイルを確認するときは
Device Explorer
を使うと確認できます。昔はADMともDevice File Explorerとも呼ばれていました、もっと昔にはDDMSと呼ばれていましたね…
Android Device Monitor, DDMS で確認
注)あくまでデバッグ段階でのファイル確認です
Android Studio にデフォルトで見えない場合は
「View」「Tool Windows」「Device Explorer」を選択
/data/data/[package_name]/files/以下に TestFile.txt を作るようにコーディングしましたが、クリックすると中身も見えて保存した内容が確認できました。
実機では
run-as [package name] をコマンドで打ち込んで確認できます
adb, run-as でローカルストレージ内をのぞく
openfileOutput , openFileInput
File API の代わりに、openFileOutput() を呼び出して、filesDir ディレクトリ内のファイルへの書き込みを行う FileOutputStream を取得することもできます。
ストリームを使用してファイルを保存する
1 |
FileOutputStream fileOutputstream = openFileOutput("test.txt", Context.MODE_PRIVATE); |
第一引数はファイル名のみの指定です。
第二引数のモードは
- MODE_APPEND
- 追加で書き込み
- MODE_PRIVATE
- このアプリのみアクセス許可
書き込みは write()メソッドを使います。
1 |
fileOutputstream.write(str.getBytes()); |
strは書き込む文字列です。
読み出しでは
FileInputStream のインスタンスを openFileInput()メソッドで取得します。
1 |
FileInputStream fileInputStream = openFileInput("test.txt"); |
InputStreamReaderでバイトを読み込み、指定された文字にデコードし、それをBufferedReaderに入れて、行毎に読み出します。変換効率を上げるため、BufferedReaderの内部にInputStreamReaderをラップするのがいいようです。
1 2 3 4 5 6 7 |
BufferedReader reader= new BufferedReader( new InputStreamReader(fileInputStream, StandardCharsets.UTF_8)); String lineBuffer; while( (lineBuffer = reader.readLine()) != null ) { text = lineBuffer ; } |
サンプルコード2
同様にEditTextから取得した文字列をファイルに入れて保存、読み出しをやってみます。
MainActivity.java
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 |
//package com.example.testprivateinternalstorage; import androidx.activity.EdgeToEdge; import androidx.appcompat.app.AppCompatActivity; import androidx.core.graphics.Insets; import androidx.core.view.ViewCompat; import androidx.core.view.WindowInsetsCompat; import android.os.Bundle; import android.content.Context; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; public class MainActivity extends AppCompatActivity { private TextView textView; private EditText editText; private final String fileName = "test.txt"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); EdgeToEdge.enable(this); setContentView(R.layout.activity_main); ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> { Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()); v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom); return insets; }); textView = findViewById(R.id.text_view); editText = findViewById(R.id.edit_text); Button buttonSave = findViewById(R.id.button_save); // lambda式 buttonSave.setOnClickListener(v -> { // エディットテキストのテキストを取得 String text = editText.getText().toString(); saveFile(fileName, text); if (text.isEmpty()) { textView.setText(R.string.no_text); } else { textView.setText(R.string.saved); } }); Button buttonRead = findViewById(R.id.button_read); // lambda式 buttonRead.setOnClickListener(v -> { String str = readFile(fileName); if (str != null) { textView.setText(str); } else { textView.setText(R.string.read_error); } }); } // ファイルを保存 public void saveFile(String file, String str) { // try-with-resources try (FileOutputStream fileOutputstream = openFileOutput(file, Context.MODE_PRIVATE)) { fileOutputstream.write(str.getBytes()); } catch (IOException e) { throw new RuntimeException(e); } } // ファイルを読み出し public String readFile(String file) { String text = null; // try-with-resources try (FileInputStream fileInputStream = openFileInput(file); BufferedReader reader = new BufferedReader( new InputStreamReader(fileInputStream, StandardCharsets.UTF_8))) { String lineBuffer; while ((lineBuffer = reader.readLine()) != null) { text = lineBuffer; } } catch (IOException e) { throw new RuntimeException(e); } return text; } } |
レイアウトなどの他のリソースは前のサンプルコードと同じです。
関連記事:
References:
ストレージ オプション | Android Developers
ファイルを保存する | Android Developers
Context#getFilesDir()
Context | Android Developers
View On-Device Files with Device File Explorer | Android Studio