ภาพรวม
ตัวอย่างนี้สาธิตวิธีบันทึกรูปภาพลง External Storage (หน่วยความจำภายนอก) พร้อมการขอ Runtime Permission สำหรับ Android 6.0 (API 23) ขึ้นไป
ขั้นตอนที่ 1: สร้างโปรเจกต์ใหม่
Android Studio → File → New Project → กรอกรายละเอียด
ขั้นตอนที่ 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"
tools:context=".MainActivity"
android:orientation="vertical">
<Button
android:id="@+id/save"
android:text="Save"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
ขั้นตอนที่ 3: เขียน MainActivity.java
package com.example.andy.myapplication;
import android.annotation.TargetApi;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import java.io.File;
import java.io.FileOutputStream;
public class MainActivity extends AppCompatActivity {
private static final int PERMISSION_REQUEST_CODE = 100;
Button save;
Bitmap bitmap;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// โหลดรูปจาก Drawable
Drawable drawable = getResources().getDrawable(R.drawable.mario);
bitmap = ((BitmapDrawable) drawable).getBitmap();
save = findViewById(R.id.save);
save.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String state = Environment.getExternalStorageState();
// ตรวจสอบว่า External Storage พร้อมใช้งาน
if (Environment.MEDIA_MOUNTED.equals(state)) {
if (Build.VERSION.SDK_INT >= 23) {
// Android 6.0+ ต้องขอ Runtime Permission
if (checkPermission()) {
saveImageToExternalStorage();
} else {
requestPermission();
}
} else {
// Android 5.1 ลงมา ไม่ต้องขอ Permission
saveImageToExternalStorage();
}
} else {
Toast.makeText(this, "External Storage ไม่พร้อมใช้งาน", Toast.LENGTH_SHORT).show();
}
}
});
}
private void saveImageToExternalStorage() {
String path = Environment.getExternalStorageDirectory().toString();
File file = new File(path, "UniqueFileName.jpg");
if (!file.exists()) {
Log.d("path", file.toString());
try {
FileOutputStream fos = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
fos.flush();
fos.close();
Toast.makeText(this, "บันทึกรูปภาพสำเร็จ", Toast.LENGTH_SHORT).show();
} catch (Exception e) {
e.printStackTrace();
Toast.makeText(this, "บันทึกล้มเหลว: " + e.getMessage(), Toast.LENGTH_SHORT).show();
}
}
}
private boolean checkPermission() {
int result = ContextCompat.checkSelfPermission(this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE);
return result == PackageManager.PERMISSION_GRANTED;
}
private void requestPermission() {
if (ActivityCompat.shouldShowRequestPermissionRationale(this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
Toast.makeText(this, "ต้องการสิทธิ์เขียน External Storage เพื่อบันทึกไฟล์ กรุณาอนุญาตใน Settings", Toast.LENGTH_LONG).show();
} else {
ActivityCompat.requestPermissions(this, new String[]{android.Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_REQUEST_CODE);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == PERMISSION_REQUEST_CODE) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.e("value", "Permission Granted, Now you can use local drive");
saveImageToExternalStorage();
} else {
Log.e("value", "Permission Denied, You cannot use local drive");
Toast.makeText(this, "ไม่ได้รับอนุญาต ไม่สามารถบันทึกไฟล์ได้", Toast.LENGTH_SHORT).show();
}
}
}
}
ขั้นตอนที่ 4: กำหนดสิทธิ์ใน AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
package="com.example.andy.myapplication">
<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>
การทดสอบและผลลัพธ์


ข้อมูลสำคัญเกี่ยวกับ External Storage และ Permission
การเปลี่ยนแปลงตาม Android Version
| Android Version | API Level | การจัดการ Permission |
|---|---|---|
| Android 5.1 และก่อน | < 23 | Permission ประกาศใน Manifest พอ |
| Android 6.0 - 9.0 | 23 - 28 | ต้องขอ Runtime Permission |
| Android 10 (Q) | 29 | Scoped Storage เริ่มใช้งาน จำกัดการเข้าถึงไฟล์ |
| Android 11+ | >= 30 | Scoped Storage บังคับ ใช้ MediaStore API แทน |
⚠️ หมายเหตุสำคัญสำหรับ Android 10+ (API 29+)
Scoped Storage: ตั้งแต่ Android 10 แอปไม่สามารถเข้าถึงไฟล์ใน External Storage โดยตรงได้แล้ว ควรใช้:
MediaStore APIสำหรับรูปภาพ วิดีโอ เสียงStorage Access Framework (SAF)สำหรับไฟล์ทั่วไปgetExternalFilesDir()สำหรับไฟล์ส่วนตัวของแอป (ไม่ต้อง Permission)
โฟลเดอร์ที่แนะนำสำหรับแอป
// ไฟล์ส่วนตัวของแอป (ไม่ต้อง Permission, ถูกลบเมื่อ Uninstall) File file = new File(getExternalFilesDir(Environment.DIRECTORY_PICTURES), "image.jpg"); // ไฟล์สาธารณะ (ต้อง Permission, คงอยู่เมื่อ Uninstall) - Deprecated ใน API 29+ File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "image.jpg");
สรุป
การเขียนไฟล์ลง External Storage ต้องคำนึงถึง Android Version และ Permission Model ที่เปลี่ยนแปลงไป ควรย้ายไปใช้ MediaStore API หรือ Scoped Storage สำหรับแอปใหม่ๆ