Content Provider ถือเป็นหนึ่งใน Component พื้นฐานที่ใช้สำหรับควบคุมการเข้าถึงข้อมูลภายในเครื่องจากแอปต่างๆได้ ซึ่งในบทความนี้จะมาพูดถึงเรื่อง Authority ของ Content Provider กันล่ะ
นักพัฒนาหลายๆคนน่าจะได้สัมผัสกับ Content Provider กันมาบ้างแล้วล่ะ เช่น เวลาที่ต้องการทำแอปให้สามารถถ่ายรูปได้ โดยใช้วิธีสั่งเปิดกล้องจากแอปอื่นๆแทน ถ้าอยากจะให้ไฟล์ภาพถูกบันทึกลงใน Data Storage ไม่ว่าจะที่ใดก็ตาม จะต้องสร้าง Content Provider ขึ้นมาเพื่อให้แอปกล้องปลายทางนั้นบันทึกไฟล์ภาพผ่าน Content Provider ของเรานั่นเอง
การทำเช่นนี้จะช่วยให้แอปปลายทางไม่จำเป็นต้องสนใจเลยว่า Data Storage ที่จะให้บันทึกไฟล์นั้นเป็นที่ไหน สนใจแค่ Content Provider ที่ส่งมาให้เท่านั้นก็พอ
และการสร้าง Content Provider ขึ้นมาซักตัวหนึ่ง ก็จะต้องประกาศไว้ใน Android Manifest เหมือนกับ Component ตัวอื่นๆอย่าง Activity, Service และ Broadcast Receiver ด้วยทุกครั้ง
จะเห็นว่าตัวอย่างโค้ดข้างบนนี้จะมีการกำหนด Attribute ที่จำเป็นสำหรับ Content Provider อยู่ด้วย ซึ่งหนึ่งในนั้นก็คือ Authority นั่นเอง
โดย Content Provider นั้นสามารถกำหนด Authority ได้มากกว่า 1 ตัว (แต่ส่วนใหญ่มักจะกำหนดแค่ตัวเดียว) และถ้าอิงตาม Guideline ของแอนดรอยด์ จะแนะนำให้ตั้งชื่อตาม Java-style naming convention แบบเดียวกับที่กำหนด Package Name ของแอปนั่นเอง
ยกตัวอย่างเช่น
การกำหนด Authority ของ Content Provider โดยอิงจาก Package Name แล้วใส่คำต่อท้ายเข้าไป เป็นวิธีที่ง่ายที่สุดและนักพัฒนาส่วนใหญ่ก็มักจะทำแบบนี้กัน
ในกรณที่มีหลาย Authority ก็ให้คั่นระหว่างชื่อด้วยเครื่องหมาย Semicolon
ดังนั้นถ้าผู้ใช้ติดตั้งแอป 2 ตัวที่มี Authority เหมือนกัน แอปที่ติดตั้งหลังสุดจะไม่สามารถติดตั้งลงในเครื่องได้นั่นเอง หรือที่เรียกกันว่า Conflicting Provider
ถ้าติดตั้งไฟล์ APK ผ่าน ADB ก็จะได้ข้อความในลักษณะเดียวกัน
ในกรณีที่ผู้ใช้เอา APK มาติดตั้งเองภายในเครื่องสิ่งที่ผู้ใช้จะเห็นก็คือ หน้าจอที่บอกว่าติดตั้งแอปไม่ได้ โดยไม่ได้มีสาเหตุระบุไว้ว่าทำไมถึงติดตั้งไม่ได้เช่นกัน
• อย่าใช้ชื่อ Authority ที่ก๊อปโค้ดมาจากอินเตอร์เน็ตโดยตรง เพราะโอกาสซ้ำเยอะมาก
• ควรนำหน้าชื่อด้วย Package Name แล้วเพิ่มคำต่อท้าย เพื่อลดโอกาสซ้ำกับแอปอื่นๆ
• ใช้ Application ID ใน Gradle จะได้ไม่ต้องพิมพ์ Package Name เองทุกครั้ง
ซึ่งการทำแบบนี้นอกจากจะช่วยให้นักพัฒนาสามารถทำ Provider ตัวนี้ไปใช้งานได้ง่าย เพราะรองรับกับการทำ Build Variant ที่แยก Package Name ของแต่ละ Flavor อยู่แล้วด้วย จึงไม่ต้องกังวลว่าจะเจอปัญหา Conflicting Provider เมื่อติดตั้งแอปหลายๆ Variant ไว้ในเครื่องเดียวกัน
นอกจากนี้ วิธีนี้ยังเหมาะกับการพัฒนาไลบรารีเพื่อให้แอปต่างๆนำไปใช้อีกด้วย เพื่อไม่ให้แอปที่นำไลบรารีไปใช้ต้องเจอปัญหา Conflicing Provider เพียงเพราะว่าใช้ไลบรารีตัวเดียวกัน
ดังนั้นการตั้งชื่อ Authority จึงมีความสำคัญอย่างมาก ถึงแม้ว่าจะเป็นเรื่องเล็กๆ แต่ก็ไม่ควรมองข้าม เพราะถ้าเกิดปัญหาขึ้นมาแล้วตอนหาสาเหตุจะทำได้ยากมาก แถมเสียโอกาสที่ผู้ใช้จะได้ติดตั้งแอปและใช้งานแอปของเราอีกด้วย ควรตรวจสอบให้ถี่ถ้วนก่อนจะ Publish ขึ้น Google Play นะ
การทำเช่นนี้จะช่วยให้แอปปลายทางไม่จำเป็นต้องสนใจเลยว่า Data Storage ที่จะให้บันทึกไฟล์นั้นเป็นที่ไหน สนใจแค่ Content Provider ที่ส่งมาให้เท่านั้นก็พอ
และการสร้าง Content Provider ขึ้นมาซักตัวหนึ่ง ก็จะต้องประกาศไว้ใน Android Manifest เหมือนกับ Component ตัวอื่นๆอย่าง Activity, Service และ Broadcast Receiver ด้วยทุกครั้ง
<!-- AndroidManifest.xml -->
<manifest ...>
<application ...>
<provider
android:name="..."
android:authorities="..."
...>
...
</provider>
</application>
</manifest>
จะเห็นว่าตัวอย่างโค้ดข้างบนนี้จะมีการกำหนด Attribute ที่จำเป็นสำหรับ Content Provider อยู่ด้วย ซึ่งหนึ่งในนั้นก็คือ Authority นั่นเอง
Authority คืออะไร?
Authority เป็นชื่อเฉพาะตัวของ Content Provider ที่นักพัฒนาจะต้องกำหนดเองทุกครั้งเพื่อให้ระบบแอนดรอยด์รู้จักกับ Content Provider ตัวนั้นๆโดย Content Provider นั้นสามารถกำหนด Authority ได้มากกว่า 1 ตัว (แต่ส่วนใหญ่มักจะกำหนดแค่ตัวเดียว) และถ้าอิงตาม Guideline ของแอนดรอยด์ จะแนะนำให้ตั้งชื่อตาม Java-style naming convention แบบเดียวกับที่กำหนด Package Name ของแอปนั่นเอง
ยกตัวอย่างเช่น
com.akexorcist.sleepingforless.imagepicker.provider
การกำหนด Authority ของ Content Provider โดยอิงจาก Package Name แล้วใส่คำต่อท้ายเข้าไป เป็นวิธีที่ง่ายที่สุดและนักพัฒนาส่วนใหญ่ก็มักจะทำแบบนี้กัน
<!-- AndroidManifest.xml -->
<manifest ...>
<application ...>
<provider
android:name="..."
android:authorities="com.akexorcist.sleepingforless.imagepicker.provider"
...>
...
</provider>
</application>
</manifest>
ในกรณที่มีหลาย Authority ก็ให้คั่นระหว่างชื่อด้วยเครื่องหมาย Semicolon
<!-- AndroidManifest.xml -->
<manifest ...>
<application ...>
<provider
android:name="..."
android:authorities="com.akexorcist.sleepingforless.imagepicker.provider;com.akexorcist.sleepingforless.filepicker.provider"
...>
...
</provider>
</application>
</manifest>
ชื่อที่กำหนดใน Authority จะต้องเป็น Unique เท่านั้น
อันนี้คือสิ่งที่สำคัญมากๆสำหรับการกำหนดชื่อให้กับ Authority ของ Content Provider เพราะว่าระบบแอนดรอยด์นั้นจะไม่ยอมให้แอปในอุปกรณ์แอนดรอยด์มี Content Provider ที่มี Authority ซ้ำกันเด็ดขาด ถึงแม้ว่าจะเป็น Content Provider ของแอปคนละตัวก็ตามดังนั้นถ้าผู้ใช้ติดตั้งแอป 2 ตัวที่มี Authority เหมือนกัน แอปที่ติดตั้งหลังสุดจะไม่สามารถติดตั้งลงในเครื่องได้นั่นเอง หรือที่เรียกกันว่า Conflicting Provider
จะเกิดอะไรขึ้นเมื่อเจอปัญหา Conflicting Provider?
ฝั่งนักพัฒนา
นักพัฒนาอย่างเราๆก็จะรู้ได้ทันทีในตอนที่ติดตั้งแอปเพื่อทดสอบผ่าน Android Studio จะเจอข้อความที่บอกว่าไม่สามารถติดตั้งแอปได้เพราะ Conflicting Providerถ้าติดตั้งไฟล์ APK ผ่าน ADB ก็จะได้ข้อความในลักษณะเดียวกัน
ฝั่งผู้ใช้ทั่วไป
เนื่องจาก Content Provider ไม่ใช่เรื่องที่ผู้ใช้จำเป็นต้องรู้จัก ดังนั้นเวลาติดตั้งแอปผ่าน Google Play แล้วเจอปัญหาดังกล่าว สิ่งที่ผู้ใช้จะเห็นก็มีแค่หน้าต่างแจ้งว่าติดตั้งแอปไม่ได้ พร้อมกับวิธีแก้ไขที่ไม่ได้เกี่ยวกับเรื่องนี้เลยซักนิดในกรณีที่ผู้ใช้เอา APK มาติดตั้งเองภายในเครื่องสิ่งที่ผู้ใช้จะเห็นก็คือ หน้าจอที่บอกว่าติดตั้งแอปไม่ได้ โดยไม่ได้มีสาเหตุระบุไว้ว่าทำไมถึงติดตั้งไม่ได้เช่นกัน
อย่าให้แอปต้องตกม้าตายเพียงเพราะเจอปัญหา Conflicting Provider
เพราะการที่ผู้ใช้ไม่สามารถติดตั้งแอปลงในเครื่องได้ก็สามารถทำให้แอปสูญเสียโอกาสส่วนหนึ่ง และถ้าเป็นปัญหา Conflicting Provider ก็จะวิเคราะห์หาสาเหตุได้ยากมาก เนื่องจากฝั่งผู้ใช้ไม่สามารถเช็คอะไรได้เลย นอกจากนักพัฒนาจะทดลองสั่งติดตั้งไฟล์ APK ผ่าน ADB ดู ดังนั้นทางที่ดีจึงควรให้ความสำคัญในการตั้ง Authority ด้วย• อย่าใช้ชื่อ Authority ที่ก๊อปโค้ดมาจากอินเตอร์เน็ตโดยตรง เพราะโอกาสซ้ำเยอะมาก
• ควรนำหน้าชื่อด้วย Package Name แล้วเพิ่มคำต่อท้าย เพื่อลดโอกาสซ้ำกับแอปอื่นๆ
• ใช้ Application ID ใน Gradle จะได้ไม่ต้องพิมพ์ Package Name เองทุกครั้ง
ลดปัญหาการกำหนดชื่อ Authority ให้กับ Content Provider ด้วยการ Inject Application ID จาก Gradle ลงใน Android Manifest
เนื่องจากการตั้งชื่อ Authority จะนิยมใช้ Package Name ของแอปเป็นคำนำหน้า ดังนั้นผู้ที่หลงเข้ามาอ่านจึงใช้ประโยชน์ของการกำหนด Application ID ใน Gradle เพื่อช่วยกำหนดชื่อให้กับ Authority ได้เช่นกัน<!-- AndroidManifest.xml -->
<manifest ...>
<application ...>
<provider
android:name="..."
android:authorities="${applicationId}.imagepicker.provider"
...>
...
</provider>
</application>
</manifest>
ซึ่งการทำแบบนี้นอกจากจะช่วยให้นักพัฒนาสามารถทำ Provider ตัวนี้ไปใช้งานได้ง่าย เพราะรองรับกับการทำ Build Variant ที่แยก Package Name ของแต่ละ Flavor อยู่แล้วด้วย จึงไม่ต้องกังวลว่าจะเจอปัญหา Conflicting Provider เมื่อติดตั้งแอปหลายๆ Variant ไว้ในเครื่องเดียวกัน
นอกจากนี้ วิธีนี้ยังเหมาะกับการพัฒนาไลบรารีเพื่อให้แอปต่างๆนำไปใช้อีกด้วย เพื่อไม่ให้แอปที่นำไลบรารีไปใช้ต้องเจอปัญหา Conflicing Provider เพียงเพราะว่าใช้ไลบรารีตัวเดียวกัน
ดังนั้นการตั้งชื่อ Authority จึงมีความสำคัญอย่างมาก ถึงแม้ว่าจะเป็นเรื่องเล็กๆ แต่ก็ไม่ควรมองข้าม เพราะถ้าเกิดปัญหาขึ้นมาแล้วตอนหาสาเหตุจะทำได้ยากมาก แถมเสียโอกาสที่ผู้ใช้จะได้ติดตั้งแอปและใช้งานแอปของเราอีกด้วย ควรตรวจสอบให้ถี่ถ้วนก่อนจะ Publish ขึ้น Google Play นะ