Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> Android

Cách tạo file text và ghi dữ liệu vào bộ nhớ trong (Internal Storage) Android

Giới thiệu

Android cung cấp **Internal Storage** (bộ nhớ trong) riêng biệt cho mỗi app tại context.filesDir (/data/data/package.name/files/). File ở đây **private** - app khác không truy cập được, và bị xóa khi uninstall app. Bài viết hướng dẫn ghi/đọc file text cơ bản và best practices.

Các bước thực hiện

Bước 1: Tạo dự án mới

Android Studio → File → New Project → Empty View Activity.

Bước 2: Thiết kế giao diện (res/layout/activity_main.xml)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:tools="https://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="24dp"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Ghi nội dung vào file text"
        android:textSize="18sp"
        android:textStyle="bold"
        android:layout_marginBottom="16dp" />

    <EditText
        android:id="@+id/etInput"
        android:layout_width="match_parent"
        android:layout_height="120dp"
        android:gravity="top|start"
        android:hint="Nhập nội dung..."
        android:inputType="textMultiLine"
        android:padding="12dp"
        android:background="@drawable/edittext_rounded" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:orientation="horizontal"
        android:gravity="center">

        <Button
            android:id="@+id/btnWrite"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginEnd="12dp"
            android:text="Ghi file" />

        <Button
            android:id="@+id/btnRead"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Đọc file" />
    </LinearLayout>

    <TextView
        android:id="@+id/tvFileContent"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:layout_marginTop="24dp"
        android:padding="12dp"
        android:background="@android:color/darker_gray"
        android:textSize="14sp"
        tools:text="Nội dung file sẽ hiển thị ở đây" />
</LinearLayout>

Bước 3: Code ghi/đọc file (MainActivity.kt)

package com.example.internalstorage

import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.example.internalstorage.databinding.ActivityMainBinding
import java.io.File

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding
    private val FILE_NAME = "my_text_file.txt"

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.btnWrite.setOnClickListener { writeToFile() }
        binding.btnRead.setOnClickListener { readFromFile() }
    }

    private fun writeToFile() {
        val content = binding.etInput.text.toString()
        if (content.isBlank()) {
            Toast.makeText(this, "Vui lòng nhập nội dung", Toast.LENGTH_SHORT).show()
            return
        }

        // Cách 1: openFileOutput (API cũ, vẫn hoạt động)
        try {
            openFileOutput(FILE_NAME, MODE_PRIVATE).use { outputStream ->
                outputStream.write(content.toByteArray())
            }
            Toast.makeText(this, "Đã ghi file thành công", Toast.LENGTH_SHORT).show()
        } catch (e: Exception) {
            Toast.makeText(this, "Lỗi ghi file: ${e.message}", Toast.LENGTH_SHORT).show()
            e.printStackTrace()
        }

        /* 
        // Cách 2: File API hiện đại (khuyến nghị)
        val file = File(filesDir, FILE_NAME)
        file.writeText(content) // Kotlin extension - tự handle encoding UTF-8
        */
    }

    private fun readFromFile() {
        // Cách 1: openFileInput
        try {
            val content = openFileInput(FILE_NAME).bufferedReader().use { it.readText() }
            binding.tvFileContent.text = content
        } catch (e: Exception) {
            binding.tvFileContent.text = "Chưa có file hoặc lỗi đọc: ${e.message}"
        }

        /*
        // Cách 2: File API
        val file = File(filesDir, FILE_NAME)
        if (file.exists()) {
            binding.tvFileContent.text = file.readText()
        } else {
            binding.tvFileContent.text = "File không tồn tại"
        }
        */
    }
}

Các chế độ mở file (openFileOutput)

ModeMô tả
MODE_PRIVATE (0)Mặc định. File private cho app. Ghi đè nếu tồn tại.
MODE_APPEND (32768)Mở file để append (ghi tiếp cuối file).
MODE_WORLD_READABLEDeprecated API 17, không dùng. Dùng FileProvider/share Intent.
MODE_WORLD_WRITEABLEDeprecated API 17, không dùng.

Đường dẫn file thực tế

  • Internal Storage: /data/data/com.example.internalstorage/files/my_text_file.txt
  • Lấy đường dẫn: File(filesDir, FILE_NAME).absolutePath
  • Liệt kê file: fileList() hoặc filesDir.listFiles()
  • Xóa file: deleteFile(FILE_NAME) hoặc file.delete()

Internal vs External vs Cache Storage

LoạiĐường dẫnQuyềnXóa khiDùng cho
Internal StoragefilesDirPrivateUninstall appData nhạy cảm, config, DB
Cache DircacheDirPrivateHệ thống cần dung lượng / UninstallTemp file, image cache
External Storage (Public)Environment.getExternalStoragePublicDirectory()Cần permission (API < 29)User xóa thủ côngMedia, document chia sẻ
External Storage (App-specific)getExternalFilesDir()Không cần permission (API 19+)Uninstall appFile lớn, backup

Best Practices

  • Luôn dùng use { } (Kotlin) hoặc try-with-resources (Java) để tự đóng stream.
  • Ghi file lớn/nhiều lần: dùng background thread (coroutine IO dispatcher).
  • Encoding: Luôn dùng **UTF-8** (mặc định của Kotlin writeText/readText).
  • File nhạy cảm (token, password):考虑加密 (EncryptedFile từ Security library).
  • Kiểm tra dung lượng trước khi ghi file lớn: StatFs hoặc catch IOException (No space left).

Mã hóa file nhạy cảm (Security Library)

// build.gradle.kts
implementation("androidx.security:security-crypto:1.1.0-alpha06")
val masterKey = MasterKey.Builder(this)
    .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
    .build()

val encryptedFile = EncryptedFile.Builder(this,
    File(filesDir, "secret.txt"),
    masterKey,
    EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB
).build()

// Ghi
encryptedFile.openFileOutput().use { it.write("Secret data".toByteArray()) }
// Đọc
val secret = encryptedFile.openFileInput().bufferedReader().use { it.readText() }