AndroidManifest Permissions คือการประกาศสิทธิ์ในไฟล์ AndroidManifest.xml ที่กำหนดว่าแอปพลิเคชันสามารถเข้าถึงทรัพยากรระบบและข้อมูลใดได้บ้าง Android กำหนดให้ต้องประกาศสิทธิ์แต่ละรายการในไฟล์แถลงการณ์ก่อนใช้ API ที่เกี่ยวข้อง ตั้งแต่กล้องและตำแหน่งที่ตั้งไปจนถึงการส่ง SMS และการเข้าถึงรายชื่อติดต่อ ตาม เอกสารสำหรับนักพัฒนา Android สิทธิ์แต่ละรายการจะแบ่งออกเป็นหนึ่งในสี่ระดับการป้องกัน: normal, dangerous, signature และ special
ประเด็นสำคัญ
AndroidManifest Permissions คือกลไกความปลอดภัยของ Android ที่ควบคุมการเข้าถึงข้อมูลที่ได้รับการป้องกันและฟังก์ชันระบบของแอปพลิเคชัน แต่ละแอปพลิเคชันต้องประกาศสิทธิ์ที่จำเป็นในไฟล์ AndroidManifest.xml โดยใช้องค์ประกอบ
โมเดลสิทธิ์ของ Android ได้ผ่านหลายขั้นตอนวิวัฒนาการ ก่อน Android 6.0 (API 23) สิทธิ์ทั้งหมดจะได้รับตอนติดตั้ง — ผู้ใช้เห็นรายการทั้งหมดและยอมรับหรือปฏิเสธการติดตั้งแอปพลิเคชัน เริ่มจาก Android 6.0 สิทธิ์ระดับ dangerous จะถูกขอในขณะรันไทม์ (Runtime Permissions) ทำให้ผู้ใช้สามารถควบคุมได้ยืดหยุ่นมากขึ้น
สิทธิ์แบ่งออกเป็นสี่ระดับการป้องกัน: normal (ให้โดยอัตโนมัติเมื่อติดตั้ง), dangerous (ต้องการคำขอในขณะรันไทม์), signature (ใช้ได้เฉพาะแอปพลิเคชันที่ลงนามด้วยใบรับรองเดียวกัน) และ special (ต้องการเปิดใช้งานแยกต่างหากในการตั้งค่า) แต่ละระดับมีกลไกการให้และการเพิกถอนของตัวเอง
ตามGoogle I/O 2024 Android 15 วางแผนที่จะแนะนำสิทธิ์ที่ละเอียดมากขึ้น — ผู้ใช้จะสามารถให้สิทธิ์เข้าถึงเฉพาะไฟล์บางไฟล์ในคลังสื่อแทนที่จะเป็นทั้งคอลเล็กชัน ซึ่งสานต่อแนวโน้มของ Android ในการลดปริมาณข้อมูลที่ให้ตามค่าเริ่มต้น
แตกต่างจาก iOS ที่สิทธิ์ทั้งหมดถูกขอในขณะรันไทม์ Android แบ่งสิทธิ์เป็นแบบติดตั้ง (install-time) และรันไทม์ (runtime) ระดับ normal จะให้โดยอัตโนมัติเมื่อติดตั้งโดยไม่ต้องแจ้งให้ผู้ใช้ทราบ ระดับ dangerous ต้องการกล่องโต้ตอบที่ชัดเจนเช่นเดียวกับ iOS
ความแตกต่างอีกอย่าง: ใน Android สิทธิ์จะถูกรวมเป็นกลุ่มสิทธิ์ หากผู้ใช้ยินยอมให้เข้าถึงกล้อง แอปพลิเคชันจะได้รับสิทธิ์เข้าถึงไมโครโฟนโดยอัตโนมัติ — ทั้งสองอยู่ในกลุ่ม MICROPHONE เดียวกัน ใน iOS แต่ละสิทธิ์จะถูกขออย่างอิสระโดยไม่คำนึงถึงกลุ่ม
| เวอร์ชัน Android | การเปลี่ยนแปลงในโมเดลสิทธิ์ |
|---|---|
| Android 1.0–5.x | สิทธิ์ทั้งหมดได้รับตอนติดตั้ง |
| Android 6.0 (API 23) | การนำ Runtime Permissions มาใช้สำหรับระดับ dangerous |
| Android 10 (API 29) | Scoped Storage — การเข้าถึงระบบไฟล์แบบจำกัด |
| Android 11 (API 30) | การรีเซ็ตสิทธิ์อัตโนมัติ — สิทธิ์ที่ไม่ได้ใช้จะถูกรีเซ็ต |
| Android 14 (API 34) | สิทธิ์รันไทม์สำหรับการเข้าถึงสื่อ (รูปภาพ วิดีโอ เสียง) |
Android กำหนดระดับการป้องกันสี่ระดับสำหรับสิทธิ์ แต่ละระดับมีกฎการให้ของตัวเอง มาดูแต่ละระดับอย่างละเอียด
สิทธิ์ Normal จะได้รับโดยอัตโนมัติเมื่อติดตั้งแอปพลิเคชันโดยไม่ต้องแจ้งหรือขอผู้ใช้ สิทธิ์เหล่านี้ครอบคลุมฟังก์ชันความเสี่ยงต่ำที่ไม่คุกคามความเป็นส่วนตัวของผู้ใช้: INTERNET, ACCESS_NETWORK_STATE, VIBRATE, BLUETOOTH ผู้ใช้จะไม่เห็นกล่องโต้ตอบการยินยอม — สิทธิ์ถือว่าได้รับเมื่อติดตั้ง
นักพัฒนาไม่จำเป็นต้องจัดการคำขอในโค้ดสำหรับสิทธิ์ normal — เพียงประกาศในไฟล์แถลงการณ์ก็เพียงพอ อย่างไรก็ตาม ใน Android 12+ เมื่อติดตั้งจาก Google Play ผู้ใช้จะเห็นแท็บ “สิทธิ์” ที่แสดงรายการสิทธิ์ normal ทั้งหมด ซึ่งเพิ่มความโปร่งใส ตามStatista (2024) กว่า 90% ของแอปพลิเคชันใน Google Play ใช้ INTERNET เป็นสิทธิ์ normal ที่พบบ่อยที่สุด
สิทธิ์ Dangerous ครอบคลุมการเข้าถึงข้อมูลและฟังก์ชันที่อาจละเมิดความเป็นส่วนตัว: กล้อง ไมโครโฟน ตำแหน่งที่ตั้ง รายชื่อติดต่อ SMS โทรศัพท์ ปฏิทิน เซ็นเซอร์ร่างกาย สิทธิ์เหล่านี้ต้องการกลไกสองขั้นตอน: ประกาศในไฟล์แถลงการณ์ + ขอในขณะรันไทม์ผ่าน ActivityCompat.requestPermissions()
ผู้ใช้สามารถปฏิเสธสิทธิ์ dangerous และแอปพลิเคชันต้องจัดการสถานการณ์นี้อย่างถูกต้อง ใน Android 11+ หากผู้ใช้ปฏิเสธสองครั้ง คำขอถัดไปจะไม่แสดงกล่องโต้ตอบของระบบ — ระบบจะส่งคืน DENIED โดยอัตโนมัติ ในกรณีนี้ แอปพลิเคชันควรนำผู้ใช้ไปยังการตั้งค่า
ระดับsignature — สิทธิ์จะได้รับโดยอัตโนมัติหากแอปพลิเคชันลงนามด้วยใบรับรองเดียวกันกับระบบหรือแอปพลิเคชันอื่นที่กำหนดสิทธิ์ ใช้สำหรับแอปพลิเคชันระบบและองค์กร ตัวอย่าง: BIND_ACCESSIBILITY_SERVICE — ใช้ได้เฉพาะแอปพลิเคชันระบบ
ระดับspecial (SYSTEM_ALERT_WINDOW, WRITE_SETTINGS, REQUEST_INSTALL_PACKAGES, MANAGE_EXTERNAL_STORAGE) — ต้องการการดำเนินการที่ชัดเจนจากผู้ใช้ผ่านการตั้งค่าระบบ แอปพลิเคชันสามารถเปิดหน้าการตั้งค่าด้วย Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION) Google Play จำกัด การใช้สิทธิ์ special และต้องการคำชี้แจงในแบบฟอร์มเมื่อเผยแพร่
ตั้งแต่ Android 6.0 สิทธิ์ dangerous ทั้งหมดต้องการคำขอในขณะรันไทม์ มาดูวงจรการทำงานทั้งหมดกับ runtime permissions ใน Kotlin
ก่อนเรียก API ที่ต้องใช้สิทธิ์ dangerous ให้ตรวจสอบสถานะปัจจุบันผ่าน ContextCompat.checkSelfPermission() เสมอ หากสถานะเป็น PERMISSION_GRANTED คุณสามารถเรียก API ได้ หากเป็น PERMISSION_DENIED คุณต้องขอสิทธิ์ผ่าน ActivityResultContract RequestPermission (AndroidX) หรือ requestPermissions() ที่เลิกใช้แล้ว
แนะนำให้ใช้ActivityResultContracts.RequestMultiplePermissions เพื่อขอหลายสิทธิ์พร้อมกัน Google แนะนำให้จัดกลุ่มสิทธิ์ที่เกี่ยวข้อง (เช่น กล้อง + ไมโครโฟนสำหรับบันทึกวิดีโอ) ในกล่องโต้ตอบเดียว เพื่อให้ผู้ใช้เห็นบริบททั้งหมดของคำขอ
import android.Manifest
import android.content.pm.PackageManager
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.ContextCompat
class CameraActivity : AppCompatActivity() {
private val requestPermissionLauncher =
registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { isGranted: Boolean ->
if (isGranted) {
openCamera()
} else {
explainWhyPermissionNeeded()
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
checkCameraPermission()
}
private fun checkCameraPermission() {
when {
ContextCompat.checkSelfPermission(this,
Manifest.permission.CAMERA
) == PackageManager.PERMISSION_GRANTED -> {
openCamera()
}
shouldShowRequestPermissionRationale(
Manifest.permission.CAMERA
) -> {
showRationale()
}
else -> {
requestPermissionLauncher.launch(
Manifest.permission.CAMERA
)
}
}
}
}
หากผู้ใช้ปฏิเสธสองครั้ง Android จะเปลี่ยนคำขอเป็นสถานะ “Never ask again” ในกรณีนี้ shouldShowRequestPermissionRationale() จะคืนค่า false และกล่องโต้ตอบระบบจะไม่แสดง แอปพลิเคชันควรนำผู้ใช้ไปยังการตั้งค่าระบบผ่าน Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
สำคัญ: อย่าแสดงกล่องโต้ตอบเสนอให้เปิดการตั้งค่าทันทีหลังการปฏิเสธครั้งแรก — ซึ่งถูกมองว่าเป็นพฤติกรรมก้าวร้าว ใช้ shouldShowRequestPermissionRationale() เพื่อพิจารณาว่าจำเป็นต้องแสดงคำอธิบายหรือไม่ แนวทาง Material Design แนะนำให้แสดงหน้าจอที่อธิบายคุณค่าของการเข้าถึง ไม่ใช่แค่ปุ่ม “เปิดการตั้งค่า”
private fun openAppSettings() {
Intent(
Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
Uri.fromParts("package", packageName, null)
).also { intent ->
startActivity(intent)
}
}
private fun showPermissionSettings() {
AlertDialog.Builder(this)
.setTitle("การเข้าถึงกล้อง")
.setMessage("อนุญาตการเข้าถึงกล้องในการตั้งค่า "
+ "เพื่อถ่ายรูปโปรไฟล์")
.setPositiveButton("เปิดการตั้งค่า") { _, _ ->
openAppSettings()
}
.setNegativeButton("ยกเลิก", null)
.show()
}
ไฟล์ AndroidManifest.xml มีองค์ประกอบ
แต่ละสิทธิ์ถูกประกาศด้วยองค์ประกอบ
ตัวอย่างเช่น สิทธิ์ WRITE_EXTERNAL_STORAGE ไม่จำเป็นบน Android 10+ (Scoped Storage) ดังนั้นให้ระบุ maxSdkVersion="28" (Android 9) ซึ่งป้องกันคำถามที่ไม่จำเป็นจากผู้ใช้บนเวอร์ชันใหม่กว่า Android Studio เตือนเกี่ยวกับ maxSdkVersion ที่แนะนำผ่าน Lint
<!-- AndroidManifest.xml -->
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Normal permissions (install-time) -->
<uses-permission
android:name="android.permission.INTERNET" />
<uses-permission
android:name="android.permission.ACCESS_NETWORK_STATE" />
<!-- Dangerous permissions (runtime) -->
<uses-permission
android:name="android.permission.CAMERA" />
<uses-permission
android:name="android.permission.ACCESS_FINE_LOCATION" />
<!-- Legacy storage permission limited to API 28 and below -->
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28" />
<uses-feature
android:name="android.hardware.camera"
android:required="false" />
<application>
<!-- ... -->
</application>
</manifest>
องค์ประกอบ
แนะนำให้ตั้งค่า required="false" สำหรับคุณสมบัติฮาร์ดแวร์ทั้งหมดและตรวจสอบความพร้อมใช้งานโดยโปรแกรมผ่าน PackageManager.hasSystemFeature() ซึ่งจะขยายกลุ่มผู้ใช้ของแอปพลิเคชันของคุณ ข้อยกเว้นเดียวคือหากคุณสมบัตินั้นสำคัญต่อการทำงานของแอปพลิเคชัน (แอปเรียกแท็กซี่ที่ไม่มี GPS นั้นไม่มีความหมาย)
การจัดการสิทธิ์ที่ถูกต้องเป็นสิ่งสำคัญของคุณภาพของแอปพลิเคชัน Android มาดูคำแนะนำหลักและข้อผิดพลาดทั่วไป
ขอเฉพาะสิทธิ์ที่จำเป็นจริง ๆ สำหรับการทำงานของแอปพลิเคชัน สิทธิ์พิเศษแต่ละรายการจะลดอัตราการแปลงการติดตั้งและเพิ่มจำนวนการปฏิเสธ Google Play Console แสดงจำนวนผู้ใช้ที่ปฏิเสธการติดตั้งเนื่องจากชุดสิทธิ์ ตามAppBrain (2024) แอปพลิเคชันที่มีสิทธิ์ dangerous 10+ รายการมียอดติดตั้งน้อยกว่า 35%
ตรวจสอบรายการสิทธิ์ของคุณเป็นประจำ ลบสิทธิ์ที่ไม่ได้ใช้ โดยเฉพาะเมื่อย้ายไปยังเวอร์ชัน Android ที่ใหม่กว่าซึ่งสิทธิ์บางอย่างกลายเป็นตัวเลือก ตัวอย่างเช่น ด้วยตัวเลือกรูปภาพ (ActivityResultContracts.PickVisualMedia) ใน Android 13+ สามารถเข้าถึงคลังสื่อได้โดยไม่ต้องใช้สิทธิ์ dangerous READ_MEDIA_IMAGES
ก่อนขอสิทธิ์ dangerous ให้แสดงหน้าจอที่อธิบายว่าทำไมต้องใช้สิทธิ์นี้และให้คุณค่าอย่างไรแก่ผู้ใช้ Material Design แนะนำให้ใช้ Bottom Sheet หรือกล่องโต้ตอบที่มีไอคอน ข้อความสั้น และปุ่ม “ดำเนินต่อ” การแสดงคำอธิบายช่วยเพิ่มการยินยอม 20–30% เมื่อเทียบกับการขอโดยตรง
ตรวจสอบ shouldShowRequestPermissionRationale() ก่อนเรียก launch() หากเป็น true ให้แสดงคำอธิบาย หากเป็น false แสดงว่าสิทธิ์ได้รับแล้วหรือผู้ใช้ปฏิเสธอย่างถาวร (อย่าถามอีก) ในกรณีหลัง ให้แสดงปุ่ม “เปิดการตั้งค่า” แทนที่จะขอซ้ำ
ทดสอบทุกสถานการณ์ที่เป็นไปได้: การให้สิทธิ์ การปฏิเสธ การปฏิเสธถาวร การเพิกถอนสิทธิ์ในการตั้งค่า การรีเซ็ตสิทธิ์ (Android 11+ รีเซ็ตอัตโนมัติ) แต่ละสถานการณ์ควรได้รับการจัดการโดยไม่เกิดข้อขัดข้องหรือสูญเสียข้อมูล คู่มือการทดสอบ Android แนะนำให้ใช้ไลบรารี TestPermission เพื่อทำให้การทดสอบเป็นอัตโนมัติ
ให้ความสนใจเป็นพิเศษกับสถานการณ์ที่ผู้ใช้เพิกถอนสิทธิ์ในขณะที่แอปพลิเคชันกำลังทำงาน (แอปย่อเล็กสุด → การตั้งค่า → เพิกถอน) เมื่อกลับมายังแอปพลิเคชัน ให้ตรวจสอบสิทธิ์ทั้งหมดใน onResume() อย่าพึ่งพาการแคชสถานะสิทธิ์ — ผู้ใช้สามารถเปลี่ยนแปลงได้ตลอดเวลา
คำถามที่พบบ่อย
ใช่ หาก SDK รวมสิทธิ์ในไฟล์แถลงการณ์ของมัน สิทธิ์นั้นจะรวมเข้ากับไฟล์แถลงการณ์ของแอปพลิเคชันตอนสร้าง คุณสามารถลบสิทธิ์ SDK ที่ไม่จำเป็นออกได้โดยใช้ tools:node="remove" ใน AndroidManifest.xml
การเรียก API โดยไม่มีสิทธิ์จะทำให้เกิด SecurityException ซึ่งทำให้แอปพลิเคชันขัดข้อง ตรวจสอบสถานะสิทธิ์ทุกครั้งก่อนใช้ API ที่เกี่ยวข้องและจัดการการปฏิเสธอย่างถูกต้อง
ในการตั้งค่าอุปกรณ์: การตั้งค่า → แอป → [แอปของคุณ] → สิทธิ์ หากต้องการรีเซ็ตสิทธิ์ทั้งหมด ให้ใช้คำสั่ง adb: adb shell pm reset-permissions
ได้ โดยใช้ ActivityResultLauncher ใน Fragment หรือ Service อย่างไรก็ตาม กล่องโต้ตอบคำขอต้องการบริบท UI ของ Activity เสมอ สำหรับ Service คุณสามารถแสดงการแจ้งเตือนพร้อม Intent ที่เปิด Activity สำหรับขอสิทธิ์
ตัวอย่างเช่น WRITE_EXTERNAL_STORAGE ไม่จำเป็นบน Android 10+ (Scoped Storage) โดยระบุ android:maxSdkVersion="28" คุณจะยกเว้นการประกาศสิทธิ์บนเวอร์ชันใหม่กว่า ซึ่งปรับปรุงความเข้ากันได้และลดรายการสิทธิ์ที่ขอ
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม