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 ด้วยทุกครั้ง

<!-- 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 เหมือนกัน แอปที่ติดตั้งหลังสุดจะไม่สามารถติดตั้งลงในเครื่องได้นั่นเอง หรือที่เรียกกันว่า Conflict Provider

จะเกิดอะไรขึ้นเมื่อเจอปัญหา Conflict Provider?

ฝั่งนักพัฒนา

นักพัฒนาอย่างเราๆก็จะรู้ได้ทันทีในตอนที่ติดตั้งแอปเพื่อทดสอบผ่าน Android Studio จะเจอข้อความที่บอกว่าไม่สามารถติดตั้งแอปได้เพราะ Conflicting Provider

ถ้าติดตั้งไฟล์ APK ผ่าน ADB ก็จะได้ข้อความในลักษณะเดียวกัน

ฝั่งผู้ใช้ทั่วไป

เนื่องจาก Content Provider ไม่ใช่เรื่องที่ผู้ใช้จำเป็นต้องรู้จัก ดังนั้นเวลาติดตั้งแอปผ่าน Google Play แล้วเจอปัญหาดังกล่าว สิ่งที่ผู้ใช้จะเห็นก็มีแค่หน้าต่างแจ้งว่าติดตั้งแอปไม่ได้ พร้อมกับวิธีแก้ไขที่ไม่ได้เกี่ยวกับเรื่องนี้เลยซักนิด

ในกรณีที่ผู้ใช้เอา APK มาติดตั้งเองภายในเครื่องสิ่งที่ผู้ใช้จะเห็นก็คือ หน้าจอที่บอกว่าติดตั้งแอปไม่ได้ โดยไม่ได้มีสาเหตุระบุไว้ว่าทำไมถึงติดตั้งไม่ได้เช่นกัน

อย่าให้แอปต้องตกม้าตายเพียงเพราะเจอปัญหา Conflict Provider

เพราะการที่ผู้ใช้ไม่สามารถติดตั้งแอปลงในเครื่องได้ก็สามารถทำให้แอปสูญเสียโอกาสส่วนหนึ่ง และถ้าเป็นปัญหา Conflict 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 นะ