Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Android

วิธีเก็บ List ลงไฟล์ Text และอ่านกลับมาใน Android

ภาพรวม

ตัวอย่างนี้สาธิตการบันทึกรายการข้อมูล (List) ลงในไฟล์ Text บน Internal Storage และอ่านกลับมาแสดงผล ใช้คลาส File, FileWriter, BufferedReader สำหรับการทำงานกับไฟล์

ขั้นตอนการสร้าง

ขั้นตอนที่ 1: สร้างโปรเจกต์ใหม่

Android Studio → File → New Project → กรอกรายละเอียด

ขั้นตอนที่ 2: จัดรูปแบบหน้าจอ (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"
    tools:context=".MainActivity"
    android:orientation="vertical">

    <EditText
        android:id="@+id/enterText"
        android:hint="กรุณาป้อนข้อความที่นี่"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <Button
        android:id="@+id/save"
        android:text="บันทึก"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <TextView
        android:id="@+id/output"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="25sp" />

</LinearLayout>

ขั้นตอนที่ 3: เขียนโค้ด MainActivity (src/MainActivity.java)

package com.example.andy.myapplication;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {
    Button save;
    ArrayList<String> list = new ArrayList<>();

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

        final TextView output = findViewById(R.id.output);
        final EditText enterText = findViewById(R.id.enterText);
        save = findViewById(R.id.save);

        save.setOnClickListener(v -> {
            String input = enterText.getText().toString().trim();
            if (!input.isEmpty()) {
                // สร้างโฟลเดอร์ text ใน Internal Storage
                File dir = new File(getFilesDir(), "text");
                if (!dir.exists()) {
                    dir.mkdirs();
                }

                try {
                    File file = new File(dir, "sample.txt");
                    FileWriter writer = new FileWriter(file, true); // true = append mode
                    writer.append(input).append("\n");
                    writer.flush();
                    writer.close();
                    
                    output.setText(readFile());
                    Toast.makeText(this, "บันทึกข้อความสำเร็จ", Toast.LENGTH_LONG).show();
                    enterText.setText(""); // ล้างช่องป้อน
                } catch (IOException e) {
                    e.printStackTrace();
                    Toast.makeText(this, "เกิดข้อผิดพลาด: " + e.getMessage(), Toast.LENGTH_LONG).show();
                }
            }
        });
        
        // โหลดข้อมูลเดิมตอนเริ่มต้น
        output.setText(readFile());
    }

    private String readFile() {
        File file = new File(getFilesDir(), "text/sample.txt");
        StringBuilder text = new StringBuilder();
        
        if (!file.exists()) {
            return "ยังไม่มีข้อมูล";
        }

        try (BufferedReader br = new BufferedReader(new FileReader(file))) {
            String line;
            list.clear();
            while ((line = br.readLine()) != null) {
                list.add(line);
                text.append(line).append("\n");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        
        return text.length() > 0 ? text.toString() : "ยังไม่มีข้อมูล";
    }
}

ขั้นตอนที่ 4: AndroidManifest.xml (Permissions)

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
    package="com.example.andy.myapplication">

    <!-- ไม่ต้องการ WRITE_EXTERNAL_STORAGE สำหรับ Internal Storage -->
    <!-- แต่ใส่ไว้เผื่อใช้ External Storage ในอนาคต -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

การทดสอบ

รันแอปพลิเคชัน ป้อนข้อความ กดบันทึก หลายครั้ง

วิธีเก็บ List ลงไฟล์ Text และอ่านกลับมาใน Android

ข้อความจะถูกเพิ่มต่อท้าย (Append) และแสดงใน TextView

วิธีเก็บ List ลงไฟล์ Text และอ่านกลับมาใน Android

ตรวจสอบไฟล์ที่: /data/data/<package.name>/files/text/sample.txt

วิธีเก็บ List ลงไฟล์ Text และอ่านกลับมาใน Android

Internal Storage vs External Storage

คุณสมบัติ Internal Storage External Storage
Path /data/data/package/files/ /sdcard/ หรือ /storage/emulated/0/
Permission ไม่ต้องการ Permission ต้องการ Permission (API < 29)
เข้าถึงโดย App อื่น ไม่ได้ (Private) ได้ (Public)
ถูกลบเมื่อ Uninstall ใช่ ไม่ (ถ้าไม่อยู่ในโฟลเดอร์แอป)
พื้นที่ จำกัด มากกว่า

แนวทางปฏิบัติที่ดีกว่า (Modern Android)

1. ใช้ Kotlin Coroutines + Okio

// FileManager.kt
class FileManager @Inject constructor(@ApplicationContext context: Context) {
    private val baseDir = File(context.filesDir, "text")
    
    suspend fun appendToFile(fileName: String, text: String) = withContext(Dispatchers.IO) {
        baseDir.mkdirs()
        File(baseDir, fileName).bufferedWriter().use { it.write(text + "\n") }
    }
    
    suspend fun readFile(fileName: String): List<String> = withContext(Dispatchers.IO) {
        File(baseDir, fileName).readLines().orEmpty()
    }
}

2. ใช้ DataStore (Preferences/Proto) แทน Text File

// สำหรับข้อมูลโครงสร้าง (Structured Data) ใช้ DataStore/Room แทน
@Serializable
data class UserSettings(
    val items: List<String> = emptyList()
)

// Proto DataStore สำหรับ List
// ใช้ได้กับข้อมูลที่ซับซ้อนกว่า Text File

3. ใช้ JSON/Gson สำหรับ List ที่ซับซ้อน

// เขียน List เป็น JSON
val gson = Gson()
val json = gson.toJson(myList)
file.writeText(json)

// อ่าน JSON กลับเป็น List
val list = gson.fromJson(file.readText(), object : TypeToken<List<String>>() {}.type)

Best Practices

  • ใช้ getFilesDir() สำหรับ Internal Storage (Private, ไม่ต้อง Permission)
  • ใช้ FileWriter(file, true) สำหรับ Append Mode
  • ใช้ Try-with-resources (Java 7+) หรือ Kotlin .use() ปิด Resource อัตโนมัติ
  • รัน File I/O บน Background Thread (IO Dispatcher) ไม่ใช่ Main Thread
  • จัดการ Exception ให้ครบถ้วน
  • สำหรับข้อมูลโครงสร้าง ใช้ Room Database หรือ DataStore แทน

สรุป

การเขียน/อ่านไฟล์ Text ใน Internal Storage ใช้งานง่าย เหมาะสำหรับข้อมูลเล็กๆ แบบไม่โครงสร้าง สำหรับข้อมูลที่ซับซ้อน ต้องการ Query หรือ Sync ควรใช้ Room Database หรือ DataStore แทน