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

Cách ghi file vào thư mục assets Android (Thực tế: Không thể ghi lúc runtime)

⚠️ Quan trọng: Assets là READ-ONLY tại runtime

Thư mục assets/ được đóng gói vào APK/AAB tại **build time**. **Không thể ghi/sửa/xóa file trong assets lúc runtime** (read-only).

Nếu cần ghi file tại runtime, hãy dùng:

  • Internal Storage (context.filesDir) - Private, xóa khi uninstall.
  • Cache Dir (context.cacheDir) - Temporary, hệ thống có thể xóa khi thiếu dung lượng.
  • External Storage (App-specific) (context.getExternalFilesDir()) - Lớn hơn, không cần permission API 19+.

Đọc file từ Assets (Chỉ đọc - Read Only)

Bước 1: Tạo file trong assets (Build time)

  1. Android Studio: Chuột phải module app → New → Folder → Assets Folder.
  2. Chuột phải thư mục assets → New → File → Đặt tên quotes.txt.
  3. Nhập nội dung:
"Hãy sống như thể bạn sẽ chết ngày mai. 
Hãy học như thể bạn sẽ sống mãi mãi."
— Mahatma Gandhi

Bước 2: Layout (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"
    android:gravity="center"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/btnReadAsset"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Đọc file từ Assets" />

    <TextView
        android:id="@+id/tvContent"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:layout_marginTop="16dp"
        android:padding="16dp"
        android:background="@android:color/darker_gray"
        android:textSize="16sp"
        android:gravity="center"
        tools:text="Nội dung file assets..." />
</LinearLayout>

Bước 3: Code đọc assets (MainActivity.kt)

package com.example.readassets

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

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

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

        binding.btnReadAsset.setOnClickListener {
            readFromAssets()
        }
    }

    private fun readFromAssets() {
        try {
            // Mở file từ assets
            val inputStream = assets.open("quotes.txt")
            
            // Đọc toàn bộ nội dung (Kotlin extension)
            val content = inputStream.bufferedReader().use { it.readText() }
            
            binding.tvContent.text = content.trim()
        } catch (e: Exception) {
            binding.tvContent.text = "Lỗi: ${e.message}"
            Toast.makeText(this, "Không tìm thấy file trong assets", Toast.LENGTH_SHORT).show()
        }
    }
}

Copy file từ Assets sang Internal Storage (Để có thể ghi/sửa)

Nếu cần file từ assets làm template, copy sang internal storage lần đầu chạy:

private fun copyAssetToInternalStorage(assetFileName: String, targetFileName: String): File {
    val targetFile = File(filesDir, targetFileName)
    if (targetFile.exists()) return targetFile // Đã copy rồi

    try {
        assets.open(assetFileName).use { input ->
            targetFile.outputStream().use { output ->
                input.copyTo(output)
            }
        }
        return targetFile
    } catch (e: Exception) {
        e.printStackTrace()
        throw RuntimeException("Copy asset failed: $assetFileName", e)
    }
}

// Sử dụng
val myFile = copyAssetToInternalStorage("quotes.txt", "my_quotes.txt")
// Giờ có thể ghi/sửa myFile
myFile.writeText("Nội dung mới")

So sánh: Assets vs Internal Storage vs Raw Resource

Đặc điểmassets/res/raw/Internal Storage (filesDir)
Ghi tại runtime❌ Không thể❌ Không thể✅ Có thể
Đọc tại runtime✅ AssetManager.open()✅ Resources.openRawResource()✅ File API
Cấu trúc thư mụcGiữ nguyên (subfolder OK)Phẳng (không subfolder)Tự quản lý
Truy cậpStream (không có Resource ID)Resource ID (R.raw.xxx)File path/Stream
Kích thước APKTăng size APKTăng size APKKhông (tạo lúc runtime)
Dùng choConfig, JSON, HTML, font, DB templateMedia ngắn, sound, raw dataUser data, cache, download, DB

Best Practices

  • File config/template lớn: Đặt trong assets → Copy sang internal storage lần đầu → Đọc/ghi từ internal storage.
  • File nhỏ, read-only: Đọc trực tiếp từ assets hoặc res/raw.
  • Database (Room): Ship pre-populated DB trong assets → Copy sang filesDir → Mở bằng Room.
  • Font: Đặt trong res/font/ (API 26+) hoặc assets (Calligraphy library cũ).
  • Đừng hardcode path assets - dùng assets.open("filename").

Tóm tắt

Không thể ghi file vào assets lúc runtime. Assets là read-only, đóng gói tại build time. Hãy copy sang filesDir hoặc cacheDir nếu cần modify.