Читання та збереження файлів

Робота з налаштуваннями рівня activity і програми дозволяє зберігати невеликі дані окремих типів (string, int), але для роботи з більшими масивами даних, такими як графічні файли, файли мультимедіа тощо, нам доведеться звертатися до файлової системи.

ОС Android побудована на основі Linux. Цей факт знаходить своє відображення в роботі з файлами. Так, у шляхах до файлів в Linux використовується коса риска /, а не зворотна риска \ (як у Windows). А всі назви файлів і каталогів є чутливими до регістру, тобто "data" — це не те ж саме, що і "Data".

Програма Android зберігає свої дані в каталозі /data/data/<назва_пакета>/ і, як правило, відносно цього каталогу буде йти робота.

Для роботи з файлами абстрактний клас android.content.Context визначає низку методів:

  • boolean deleteFile (String name): видаляє певний файл.
  • String fileList (): отримує всі файли, які містяться в підкаталозі /files каталогу програми.
  • File getCacheDir(): отримує посилання на підкаталог cache в каталозі програми.
  • File getDir(String dirName, int mode): отримує посилання на підкаталог в каталозі програми, якщо такого підкаталога немає, то він створюється.
  • File getExternalCacheDir(): отримує посилання на папку /cache зовнішньої файлової системи пристрою.
  • File getExternalFilesDir(String type): отримує посилання на каталог /files зовнішньої файлової системи пристрою.
  • File getFileStreamPath(String filename): повертає абсолютний шлях до файлу в файловій системі.
  • FileInputStream openFileInput(String filename): відкриває файл для читання.
  • FileOutputStream openFileOutput (String name, int mode): відкриває файл для запису.

Усі файли, які створюються і редагуються в програмі, зазвичай зберігаються в підкаталозі /files в каталозі програми.

Для безпосереднього читання і запису файлів застосовуються також стандартні класи Java з пакету java.io.

Отже, застосуємо функціонал читання-запису файлів у додатку. Нехай у нас буде така примітивна розмітка layout:

<?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"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
 
    <EditText
        android:id="@+id/editor"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:textSize="18sp"
        android:gravity="start"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintBottom_toTopOf="@id/save_text"
        app:layout_constraintTop_toTopOf="parent" />
    <Button
        android:id="@+id/save_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
 
        android:onClick="saveText"
        android:text="Сохранить"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintBottom_toTopOf="@id/text"
        app:layout_constraintTop_toBottomOf="@id/editor" />
 
    <TextView
        android:id="@+id/text"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:gravity="start"
        android:textSize="18sp"
 
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintBottom_toTopOf="@+id/open_text"
        app:layout_constraintTop_toBottomOf="@+id/save_text" />
    <Button
        android:id="@+id/open_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="openText"
        android:text="Открыть"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/text" />
 
</androidx.constraintlayout.widget.ConstraintLayout>

Поле EditText призначене для введення тексту, а TextView - для виведення раніше збереженого тексту. Для збереження і відновлення тексту додано дві кнопки.

Тепер у коді Activity пропишемо обробники кнопок зі збереженням і читанням файлу:

package com.example.filesapp;

import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class MainActivity extends AppCompatActivity {

    private static final String FILE_NAME = "content.txt";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    // Збереження тексту у файл
    public void saveText(View view) {
        EditText editor = findViewById(R.id.editor);
        String text = editor.getText().toString();

        // Використовуємо try-with-resources для автоматичного закриття потоку
        try (FileOutputStream fos = openFileOutput(FILE_NAME, MODE_PRIVATE)) {
            fos.write(text.getBytes());
            showToast("Файл збережено");
        } catch (IOException e) {
            showToast("Помилка при збереженні: " + e.getMessage());
        }
    }

    // Відкриття тексту з файлу
    public void openText(View view) {
        TextView textView = findViewById(R.id.text);

        // Використовуємо try-with-resources для автоматичного закриття потоку
        try (FileInputStream fin = openFileInput(FILE_NAME)) {
            byte[] bytes = new byte[fin.available()];
            fin.read(bytes);
            String text = new String(bytes);
            textView.setText(text);
        } catch (IOException e) {
            showToast("Помилка при відкритті: " + e.getMessage());
        }
    }

    // Метод для відображення повідомлень Toast
    private void showToast(String message) {
        Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
    }
}

варіант 2:

package com.example.filesapp;

import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class MainActivity extends AppCompatActivity {

    private static final String FILE_NAME = "content.txt";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    // Збереження тексту у файл з використанням BufferedWriter
    public void saveText(View view) {
        EditText editor = findViewById(R.id.editor);
        String text = editor.getText().toString();

        // Використовуємо try-with-resources для автоматичного закриття потоку
        try (BufferedWriter writer = new BufferedWriter(new FileWriter(getFileStreamPath(FILE_NAME)))) {
            writer.write(text);
            showToast("Файл збережено");
        } catch (IOException e) {
            showToast("Помилка при збереженні: " + e.getMessage());
        }
    }

    // Відкриття тексту з файлу з використанням BufferedReader
    public void openText(View view) {
        TextView textView = findViewById(R.id.text);
        StringBuilder stringBuilder = new StringBuilder();

        // Використовуємо try-with-resources для автоматичного закриття потоку
        try (BufferedReader reader = new BufferedReader(new FileReader(getFileStreamPath(FILE_NAME)))) {
            String line;
            while ((line = reader.readLine()) != null) {
                stringBuilder.append(line).append("\n");
            }
            textView.setText(stringBuilder.toString());
        } catch (IOException e) {
            showToast("Помилка при відкритті: " + e.getMessage());
        }
    }

    // Метод для відображення повідомлень Toast
    private void showToast(String message) {
        Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
    }
}

варіант №3

package com.example.filesapp;

import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.charset.StandardCharsets;

public class MainActivity extends AppCompatActivity {

    private static final String FILE_NAME = "content.txt";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    // Збереження тексту у файл з використанням NIO (Java NIO)
    public void saveText(View view) {
        EditText editor = findViewById(R.id.editor);
        String text = editor.getText().toString();

        // Створюємо шлях до файлу
        Path path = getFileStreamPath(FILE_NAME).toPath();

        try {
            // Записуємо текст у файл, використовуючи NIO (вказуємо кодування)
            Files.write(path, text.getBytes(StandardCharsets.UTF_8));
            showToast("Файл збережено");
        } catch (IOException e) {
            showToast("Помилка при збереженні: " + e.getMessage());
        }
    }

    // Відкриття тексту з файлу з використанням NIO (Java NIO)
    public void openText(View view) {
        TextView textView = findViewById(R.id.text);

        // Створюємо шлях до файлу
        Path path = getFileStreamPath(FILE_NAME).toPath();

        try {
            // Зчитуємо вміст файлу в рядок за допомогою NIO
            String content = new String(Files.readAllBytes(path), StandardCharsets.UTF_8);
            textView.setText(content);
        } catch (IOException e) {
            showToast("Помилка при відкритті: " + e.getMessage());
        }
    }

    // Метод для відображення повідомлень Toast
    private void showToast(String message) {
        Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
    }
}

При натисканні на кнопку збереження буде створюватися потік виводу:

FileOutputStream fos = openFileOutput(FILE_NAME, MODE_PRIVATE);

У цьому випадку введений текст буде зберігатися у файл "content.txt". При цьому буде використовуватися режим MODE_PRIVATE.

Система дозволяє створювати файли з двома різними режимами:

  • MODE_PRIVATE: файли можуть бути доступні тільки власнику програми (режим за замовчуванням).
  • MODE_APPEND: дані можуть бути додані в кінець файлу.

Тому в даному випадку, якщо файл "content.txt" вже існує, він буде перезаписаний. Якщо ж потрібно було б дописати в файл, тоді слід використовувати режим MODE_APPEND:

FileOutputStream fos = openFileOutput(FILE_NAME, MODE_APPEND);

Для читання файлу застосовується потік вводу:

FileInputStream fin = openFileInput(FILE_NAME);

У підсумку після натискання кнопки збереження весь текст буде збережено у файлі /data/data/назва_пакета/files/content.txt

Де фізично знаходиться створений файл? Щоб побачити його на під'єднаному пристрої перейдемо в Android Stud у меню до пункту View -> Tool Windows -> Device File Explorer Після цього відкриється вікно Device File Explorer для перегляду файлової системи пристрою. І в папці data/data/[назва_пакета_додатка]/files ми зможемо знайти збережений файл.

Методи openFileOutput та openFileInput — це методи Android, що дозволяють працювати з файлами, які зберігаються в внутрішньому сховищі додатку. Це частина специфічного API для роботи з файлами в Android. Вони використовуються для збереження та зчитування даних, що зберігаються тільки для поточного додатка, і вони не доступні іншим додаткам або користувачам.

Основні характеристики:

  1. Внутрішнє сховище (Internal Storage):
    • Файли, створені за допомогою цих методів, зберігаються в приватній частині внутрішнього сховища вашого додатку. Це означає, що тільки ваш додаток має доступ до цих файлів.
    • Вони не доступні іншим додаткам або користувачам.
    • Файли, збережені через ці методи, не видимі в системному файловому менеджері.
  2. Автоматичне керування простором:
    • Android автоматично видаляє ці файли, коли додаток видаляється з пристрою.
    • Це зручно для зберігання даних, які не потребують постійного доступу або синхронізації з іншими додатками.
  3. Використання для зберігання невеликих даних:
    • Ці методи зазвичай використовуються для збереження невеликих файлів, таких як налаштування, журнали або тимчасові файли, які не вимагають доступу ззовні.