สร้างผู้ให้บริการเนื้อหา

ผู้ให้บริการเนื้อหาจะจัดการสิทธิ์เข้าถึงที่เก็บข้อมูลกลาง คุณใช้ เป็นผู้ให้บริการคลาวด์อย่างน้อยหนึ่งคลาสในแอปพลิเคชัน Android พร้อมด้วยองค์ประกอบใน ไฟล์ Manifest หนึ่งในคลาสของคุณใช้คลาสย่อยของ ContentProvider ซึ่งเป็นอินเทอร์เฟซระหว่างผู้ให้บริการของคุณกับ แอปพลิเคชันอื่นๆ

แม้ว่าผู้ให้บริการเนื้อหาจะมีไว้เพื่อให้ข้อมูล คุณสามารถมีกิจกรรมในแอปพลิเคชันที่ช่วยให้ผู้ใช้ ค้นหาและแก้ไขข้อมูลที่จัดการโดยผู้ให้บริการ

หน้านี้ประกอบด้วยขั้นตอนพื้นฐานในการสร้างผู้ให้บริการเนื้อหาและรายชื่อ API ต่างๆ ที่ใช้ได้

ก่อนเริ่มสร้าง

ก่อนที่จะเริ่มสร้างผู้ให้บริการ ให้พิจารณาสิ่งต่อไปนี้

  • ตัดสินใจว่าคุณต้องการผู้ให้บริการเนื้อหาหรือไม่ คุณต้องสร้างเนื้อหา ผู้ให้บริการเครือข่าย หากคุณต้องการให้บริการฟีเจอร์ต่อไปนี้อย่างน้อย 1 อย่าง
    • คุณต้องการนำเสนอข้อมูลหรือไฟล์ที่ซับซ้อนให้แก่แอปพลิเคชันอื่น
    • คุณต้องการให้ผู้ใช้คัดลอกข้อมูลที่ซับซ้อนจากแอปไปยังแอปอื่นๆ
    • คุณต้องการให้คำแนะนำการค้นหาที่กำหนดเองโดยใช้กรอบการค้นหา
    • คุณต้องการเปิดเผยข้อมูลแอปพลิเคชันไปยังวิดเจ็ต
    • คุณต้องการใช้ AbstractThreadedSyncAdapter CursorAdapter หรือ CursorLoader ใหม่

    คุณไม่จำเป็นต้องมีผู้ให้บริการเพื่อใช้ฐานข้อมูลหรือข้อมูลประเภทอื่นๆ พื้นที่เก็บข้อมูลถาวร หากการใช้งานนั้นอยู่ภายในแอปพลิเคชันของคุณทั้งหมด และคุณไม่จำเป็นต้องมี ฟีเจอร์ก่อนหน้านี้แสดงอยู่ อย่างไรก็ตาม คุณสามารถ ใช้ระบบจัดเก็บข้อมูลระบบใดระบบหนึ่งตามที่อธิบายไว้ใน ภาพรวมของพื้นที่เก็บข้อมูลและไฟล์

  • หากคุณยังไม่ได้อ่าน โปรดอ่าน ข้อมูลเบื้องต้นเกี่ยวกับผู้ให้บริการเนื้อหาเพื่อดูข้อมูลเพิ่มเติมเกี่ยวกับผู้ให้บริการและวิธีการทำงาน

ถัดไป ให้ทำตามขั้นตอนต่อไปนี้เพื่อสร้างผู้ให้บริการ

  1. ออกแบบพื้นที่เก็บข้อมูลดิบสำหรับข้อมูลของคุณ ผู้ให้บริการเนื้อหานำเสนอข้อมูลด้วย 2 วิธีดังนี้
    ข้อมูลไฟล์
    ข้อมูลที่โดยปกติแล้วจะอยู่ในไฟล์ เช่น รูปภาพ เสียง หรือวิดีโอ จัดเก็บไฟล์ไว้ในที่ส่วนตัวของแอปพลิเคชัน พื้นที่ทำงาน ในการตอบกลับคำขอไฟล์จากแอปพลิเคชันอื่น สามารถเสนอแฮนเดิลไฟล์ได้
    "มีโครงสร้าง" ข้อมูล
    ข้อมูลที่โดยปกติแล้วจะอยู่ในฐานข้อมูล อาร์เรย์ หรือโครงสร้างที่คล้ายกัน จัดเก็บข้อมูลในแบบฟอร์มที่ใช้ร่วมกับตารางแถวและคอลัมน์ได้ แถว แสดงถึงเอนทิตี เช่น บุคคลหรือสินค้าในสินค้าคงคลัง คอลัมน์แสดงถึง ข้อมูลบางอย่างสำหรับเอนทิตี เช่น ชื่อบุคคลหรือราคาสินค้า วิธีทั่วไปในการ จัดเก็บข้อมูลประเภทนี้อยู่ในฐานข้อมูล SQLite แต่คุณสามารถใช้ พื้นที่เก็บข้อมูลถาวร หากต้องการดูข้อมูลเพิ่มเติมเกี่ยวกับประเภทพื้นที่เก็บข้อมูลที่มีให้ใช้งานใน ระบบ Android โปรดดู ออกแบบพื้นที่เก็บข้อมูล
  2. กำหนดการนำชั้นเรียน ContentProvider ไปใช้อย่างเป็นรูปธรรมและ วิธีการที่จำเป็น คลาสนี้เป็นอินเทอร์เฟซระหว่างข้อมูลของคุณและส่วนอื่นๆ ระบบ Android ดูข้อมูลเพิ่มเติมเกี่ยวกับชั้นเรียนนี้ได้ที่ ใช้คลาส ContentProvider
  3. กำหนดสตริงสิทธิ์ของผู้ให้บริการ, URI เนื้อหา และชื่อคอลัมน์ หากคุณต้องการ แอปพลิเคชันของผู้ให้บริการในการจัดการกับ Intent ต้องกำหนดการดำเนินการของ Intent ข้อมูลเพิ่มเติม และแฟล็ก รวมถึงกำหนดสิทธิ์ที่คุณต้องใช้สำหรับแอปพลิเคชันที่ต้องการ เพื่อเข้าถึงข้อมูลของคุณ ลองกำหนดค่าทั้งหมดนี้เป็นค่าคงที่ใน ชั้นสัญญาแยกต่างหาก คุณแสดงชั้นเรียนนี้แก่นักพัฒนาซอฟต์แวร์คนอื่นๆ ได้ในภายหลัง สำหรับข้อมูลเพิ่มเติม เกี่ยวกับ URI เนื้อหา โปรดดู ออกแบบ URI เนื้อหา สำหรับข้อมูลเพิ่มเติมเกี่ยวกับ Intent โปรดดู Intent และการเข้าถึงข้อมูล
  4. เพิ่มส่วนที่ไม่บังคับอื่นๆ เช่น ข้อมูลตัวอย่างหรือการใช้งาน จาก AbstractThreadedSyncAdapter ที่สามารถซิงค์ข้อมูลระหว่าง ผู้ให้บริการและข้อมูลในระบบคลาวด์

ออกแบบพื้นที่เก็บข้อมูล

ผู้ให้บริการเนื้อหาคืออินเทอร์เฟซสําหรับข้อมูลที่บันทึกไว้ในรูปแบบที่มีโครงสร้าง ก่อนสร้าง อินเทอร์เฟซ ในการตัดสินใจเลือกวิธีการจัดเก็บข้อมูล คุณสามารถจัดเก็บข้อมูลในรูปแบบใดก็ได้ ที่ชอบ จากนั้นออกแบบอินเทอร์เฟซให้อ่านและเขียนข้อมูลตามที่ต้องการ

เทคโนโลยีพื้นที่เก็บข้อมูลบางส่วนที่มีใน Android มีดังนี้

  • ถ้าคุณใช้ข้อมูลที่มีโครงสร้าง ให้พิจารณาใช้ฐานข้อมูลเชิงสัมพันธ์ เป็น SQLite หรือที่เก็บข้อมูลคีย์-ค่าที่ไม่ใช่เชิงสัมพันธ์ เช่น LevelDB หากคุณกำลังทำงาน กับข้อมูลที่ไม่มีโครงสร้าง เช่น สื่อเสียง รูปภาพ หรือวิดีโอ แล้วลองพิจารณาการจัดเก็บ เป็นไฟล์ข้อมูล คุณสามารถเลือกใช้พื้นที่หลายประเภทร่วมกันเพื่อแสดงพื้นที่เก็บข้อมูล โดยใช้ผู้ให้บริการเนื้อหารายเดียว หากจำเป็น
  • ระบบ Android สามารถโต้ตอบกับไลบรารีความต่อเนื่องของห้อง ซึ่ง ให้สิทธิ์เข้าถึง API สำหรับฐานข้อมูล SQLite ที่ผู้ให้บริการของ Android เอง ใช้ในการจัดเก็บข้อมูลแบบตาราง วิธีสร้างฐานข้อมูลโดยใช้ ไลบรารี, สร้างอินสแตนซ์คลาสย่อยของ RoomDatabase ตามที่อธิบายไว้ใน บันทึกข้อมูลในฐานข้อมูลในเครื่องโดยใช้ห้องแชท

    คุณไม่ต้องใช้ฐานข้อมูลเพื่อใช้ที่เก็บ ผู้ให้บริการ ปรากฏภายนอกเป็นชุดตาราง คล้ายกับฐานข้อมูลเชิงสัมพันธ์ แต่ ไม่ใช่ข้อกำหนดสำหรับการใช้งานภายในของผู้ให้บริการ

  • Android มี API ต่างๆ เกี่ยวกับไฟล์สำหรับการจัดเก็บข้อมูลไฟล์ หากต้องการดูข้อมูลเพิ่มเติมเกี่ยวกับพื้นที่เก็บข้อมูลไฟล์ โปรดอ่าน ภาพรวมของพื้นที่เก็บข้อมูลและไฟล์ หากคุณ การออกแบบผู้ให้บริการที่ให้ข้อมูลเกี่ยวกับสื่อ เช่น เพลงหรือวิดีโอ คุณสามารถ มีผู้ให้บริการที่รวมข้อมูลตารางและไฟล์เข้าด้วยกัน
  • ในบางกรณี คุณอาจได้ประโยชน์จากการใช้ผู้ให้บริการเนื้อหามากกว่า 1 ราย แอปพลิเคชันเดียว ตัวอย่างเช่น คุณอาจต้องการแชร์ข้อมูลบางอย่างกับวิดเจ็ตโดยใช้ ผู้ให้บริการเนื้อหารายหนึ่ง และแสดงชุดข้อมูลคนละชุดเพื่อแชร์กับผู้ให้บริการเนื้อหารายอื่น แอปพลิเคชัน
  • สำหรับการทำงานกับข้อมูลบนเครือข่าย ให้ใช้คลาสใน java.net และ android.net คุณยังสามารถซิงค์ข้อมูลสำหรับเครือข่ายกับข้อมูลในเครื่องได้ด้วย จัดเก็บข้อมูล เช่น ฐานข้อมูล แล้วนำเสนอข้อมูลเป็นตารางหรือไฟล์

หมายเหตุ: หากทำการเปลี่ยนแปลงที่เก็บในลักษณะดังกล่าว เข้ากันได้แบบย้อนหลัง คุณต้องทำเครื่องหมายที่เก็บด้วยเวอร์ชันใหม่ หมายเลข คุณยังต้องเพิ่มหมายเลขเวอร์ชันของแอปที่ จะใช้ผู้ให้บริการเนื้อหารายใหม่ การเปลี่ยนแปลงนี้จะป้องกันไม่ให้ระบบ ดาวน์เกรดจากการทำให้ระบบขัดข้องเมื่อพยายามติดตั้งใหม่อีกครั้ง แอปที่มีผู้ให้บริการเนื้อหาที่ใช้ร่วมกันไม่ได้

ข้อควรพิจารณาในการออกแบบข้อมูล

เคล็ดลับสำหรับการออกแบบโครงสร้างข้อมูลของผู้ให้บริการมีดังนี้

  • ข้อมูลตารางต้องมี "คีย์หลัก" เสมอ ที่ผู้ให้บริการเก็บไว้ เป็นค่าตัวเลขที่ไม่ซ้ำกันสำหรับแต่ละแถว คุณสามารถใช้ค่านี้เพื่อลิงก์แถวกับ แถวในตารางอื่นๆ (ใช้เป็น "กุญแจต่างประเทศ") แม้ว่าคุณจะสามารถใช้ชื่อใดก็ได้ สำหรับคอลัมน์นี้ การใช้ BaseColumns._ID ถือว่าดีที่สุด เนื่องจากการลิงก์ผลลัพธ์การค้นหาของผู้ให้บริการกับ ListView กำหนดให้คอลัมน์ใดคอลัมน์หนึ่งมีชื่อมีชื่อ _ID
  • หากคุณต้องการใส่ภาพบิตแมปหรือข้อมูลที่เกี่ยวกับไฟล์อื่นๆ ที่มีขนาดใหญ่มาก ให้จัดเก็บ ข้อมูลในไฟล์ แล้วส่งข้อมูลนั้นโดยอ้อม แทนที่จะเก็บไว้ในไฟล์โดยตรง หากคุณทำเช่นนี้ คุณต้องแจ้งผู้ใช้ของผู้ให้บริการว่าจำเป็นต้องใช้ ContentResolver วิธีเข้าถึงข้อมูล
  • ใช้ประเภทข้อมูลไบนารีที่มีขนาดใหญ่ (BLOB) เพื่อจัดเก็บข้อมูลที่มีขนาดแตกต่างกันไปหรือมีจำนวน โครงสร้างที่หลากหลาย ตัวอย่างเช่น คุณสามารถใช้คอลัมน์ BLOB เพื่อจัดเก็บ บัฟเฟอร์โปรโตคอล หรือ โครงสร้าง JSON

    นอกจากนี้ คุณยังใช้ BLOB เพื่อใช้ตารางที่ไม่อิงตามสคีมาได้ด้วย ใน ตารางประเภทนี้ คุณกำหนดคอลัมน์คีย์หลัก คอลัมน์ประเภท MIME และ คอลัมน์ทั่วไปในรูปแบบ BLOB มีการระบุความหมายของข้อมูลในคอลัมน์ BLOB ด้วยค่าในคอลัมน์ประเภท MIME ซึ่งจะช่วยให้คุณจัดเก็บแถวประเภทต่างๆ ไว้ใน ตารางเดียวกัน "ข้อมูล" ของผู้ให้บริการรายชื่อติดต่อ โต๊ะ ContactsContract.Data เป็นตัวอย่างของสคีมาที่ไม่อิงตามสคีมา

ออกแบบ URI เนื้อหา

URL เนื้อหาคือ URI ที่ระบุข้อมูลในผู้ให้บริการ URI เนื้อหาประกอบด้วย ชื่อสัญลักษณ์ของผู้ให้บริการทั้งหมด (หน่วยงานของผู้ให้บริการ) และ ชื่อที่ชี้ไปยังตารางหรือไฟล์ (เส้นทาง) ส่วน ID ที่ไม่บังคับจะชี้ไปยัง แต่ละแถวในตาราง วิธีการเข้าถึงข้อมูลทุกวิธีของ ContentProvider มี URI เนื้อหาเป็นอาร์กิวเมนต์ ซึ่งช่วยให้คุณ กำหนดตาราง แถว หรือไฟล์ที่ต้องการเข้าถึง

สำหรับข้อมูลเกี่ยวกับ URI เนื้อหา โปรดดู ข้อมูลเบื้องต้นเกี่ยวกับผู้ให้บริการเนื้อหา

ออกแบบหน่วยงาน

ผู้ให้บริการมักจะมีสิทธิ์เดียว ซึ่งทำหน้าที่เป็นชื่อภายใน Android ของตน ถึง หลีกเลี่ยงการขัดแย้งกับผู้ให้บริการรายอื่น ใช้การเป็นเจ้าของโดเมนอินเทอร์เน็ต (ในทางกลับกัน) เป็นพื้นฐานของอำนาจของผู้ให้บริการ เนื่องจากคำแนะนำนี้เป็นจริงสำหรับ Android เช่นกัน ชื่อแพ็กเกจ คุณสามารถกำหนดหน้าที่ของผู้ให้บริการของคุณเป็นส่วนขยายของชื่อได้ ของหีบห่อที่มีผู้ให้บริการรายนั้น

ตัวอย่างเช่น หากชื่อแพ็กเกจ Android ของคุณคือ com.example.<appname> ให้เวลาผู้ให้บริการ หน่วยงาน com.example.<appname>.provider

ออกแบบโครงสร้างเส้นทาง

โดยปกตินักพัฒนาซอฟต์แวร์มักจะสร้าง URI เนื้อหาจากผู้ออกใบรับรองโดยต่อท้ายเส้นทางที่ชี้ไปยัง แต่ละตาราง เช่น หากคุณมี 2 ตาราง ได้แก่ table1 และ table2 คุณสามารถรวมฟังก์ชันดังกล่าวกับผู้ออกใบรับรองจากตัวอย่างก่อนหน้านี้เพื่อแสดงผล URI เนื้อหา com.example.<appname>.provider/table1 และ com.example.<appname>.provider/table2 เส้นทางไม่เท่ากับ กลุ่มเดียวเท่านั้น และไม่จำเป็นต้องมีตารางสำหรับเส้นทางแต่ละระดับ

จัดการรหัส URI ของเนื้อหา

ตามหลักแล้ว ผู้ให้บริการจะเสนอการเข้าถึงแถวเดียวในตารางโดยการยอมรับ URI เนื้อหา ด้วยค่ารหัสสำหรับแถวที่ท้าย URI นอกจากนี้ ตามปกติแล้ว ผู้ให้บริการจะจับคู่กับ รหัสลงในคอลัมน์ _ID ของตารางและดำเนินการเข้าถึงตามที่ขอกับ แถวที่ตรงกัน

ข้อตกลงนี้จะเอื้อให้เกิดรูปแบบการออกแบบทั่วไปสำหรับแอปที่เข้าถึงผู้ให้บริการ แอป จะค้นหากับผู้ให้บริการและแสดงผล Cursor ใน ListView โดยใช้ CursorAdapter คำจำกัดความของ CursorAdapter ต้องการคอลัมน์ใดคอลัมน์หนึ่งใน Cursor จะเป็น _ID

จากนั้นผู้ใช้จะเลือกแถวที่แสดงแถวใดแถวหนึ่งจาก UI เพื่อดูหรือแก้ไข แอปจะได้รับแถวที่เกี่ยวข้องจาก Cursor ที่สำรองข้อมูล ListView รับค่า_IDสำหรับแถวนี้ นำไปต่อท้าย URI เนื้อหา แล้วส่งคำขอเข้าถึงไปยังผู้ให้บริการ จากนั้นผู้ให้บริการจะดำเนินการ ข้อความค้นหาหรือการแก้ไขตามแถวที่ผู้ใช้เลือกทุกประการ

รูปแบบ URI ของเนื้อหา

API ของผู้ให้บริการเพื่อช่วยคุณเลือกการดําเนินการสําหรับ URI เนื้อหาที่เข้ามาใหม่ คลาสช่วยอำนวยความสะดวก UriMatcher ซึ่งแมปรูปแบบ URI เนื้อหากับ ค่าจำนวนเต็ม คุณสามารถใช้ค่าจำนวนเต็มในคำสั่ง switch ซึ่ง เลือกการทำงานที่ต้องการสำหรับ URI เนื้อหาหรือ URI ที่ตรงกับรูปแบบที่เฉพาะเจาะจง

รูปแบบ URI เนื้อหาจะจับคู่ URI เนื้อหาโดยใช้อักขระไวลด์การ์ดดังนี้

  • * จะจับคู่สตริงของอักขระที่ถูกต้องที่มีความยาวเท่าใดก็ได้
  • # จะจับคู่สตริงของอักขระที่เป็นตัวเลขความยาวเท่าใดก็ได้

สำหรับตัวอย่างของการออกแบบและการเขียนโค้ดการจัดการ URI เนื้อหา ให้พิจารณาผู้ให้บริการที่มี ผู้ออกใบรับรอง com.example.app.provider ที่ยอมรับ URI เนื้อหาต่อไปนี้ ชี้ไปที่ตาราง:

  • content://com.example.app.provider/table1: ตารางชื่อ table1
  • content://com.example.app.provider/table2/dataset1: ตารางชื่อ dataset1
  • content://com.example.app.provider/table2/dataset2: ตารางชื่อ dataset2
  • content://com.example.app.provider/table3: ตารางชื่อ table3

ผู้ให้บริการยังรู้จัก URI เนื้อหาเหล่านี้หากมีรหัสแถวต่อท้ายด้วย เช่น content://com.example.app.provider/table3/1 สำหรับแถวที่ระบุโดย 1ในtable3

รูปแบบ URI ของเนื้อหาที่เป็นไปได้มีดังนี้

content://com.example.app.provider/*
จับคู่ URI เนื้อหาในผู้ให้บริการ
content://com.example.app.provider/table2/*
จับคู่ URI เนื้อหาสำหรับตาราง dataset1 และ dataset2 แต่ไม่ตรงกับ URI เนื้อหาสำหรับ table1 หรือ table3
content://com.example.app.provider/table3/#
ตรงกับ URI เนื้อหา สำหรับแถวเดี่ยวใน table3 เช่น content://com.example.app.provider/table3/6 สำหรับแถวที่ระบุโดย 6

ข้อมูลโค้ดต่อไปนี้แสดงวิธีการทำงานของเมธอดใน UriMatcher โค้ดนี้จะจัดการ URI สำหรับทั้งตารางแตกต่างจาก URI สำหรับ แถวเดียวโดยใช้รูปแบบ URI ของเนื้อหา content://<authority>/<path> สำหรับตารางและ content://<authority>/<path>/<id> สำหรับแถวเดี่ยว

เมธอด addURI() จะแมป และพาธไปยังค่าจำนวนเต็ม เมธอด match() จะแสดงผลค่าจำนวนเต็มสำหรับ URI ใบแจ้งยอดของ switch เลือกได้ระหว่างการค้นหาทั้งตารางและการค้นหาสำหรับระเบียนเดียว

Kotlin

private val sUriMatcher = UriMatcher(UriMatcher.NO_MATCH).apply {
    /*
     * The calls to addURI() go here for all the content URI patterns that the provider
     * recognizes. For this snippet, only the calls for table 3 are shown.
     */

    /*
     * Sets the integer value for multiple rows in table 3 to 1. Notice that no wildcard is used
     * in the path.
     */
    addURI("com.example.app.provider", "table3", 1)

    /*
     * Sets the code for a single row to 2. In this case, the # wildcard is
     * used. content://com.example.app.provider/table3/3 matches, but
     * content://com.example.app.provider/table3 doesn't.
     */
    addURI("com.example.app.provider", "table3/#", 2)
}
...
class ExampleProvider : ContentProvider() {
    ...
    // Implements ContentProvider.query()
    override fun query(
            uri: Uri?,
            projection: Array<out String>?,
            selection: String?,
            selectionArgs: Array<out String>?,
            sortOrder: String?
    ): Cursor? {
        var localSortOrder: String = sortOrder ?: ""
        var localSelection: String = selection ?: ""
        when (sUriMatcher.match(uri)) {
            1 -> { // If the incoming URI was for all of table3
                if (localSortOrder.isEmpty()) {
                    localSortOrder = "_ID ASC"
                }
            }
            2 -> {  // If the incoming URI was for a single row
                /*
                 * Because this URI was for a single row, the _ID value part is
                 * present. Get the last path segment from the URI; this is the _ID value.
                 * Then, append the value to the WHERE clause for the query.
                 */
                localSelection += "_ID ${uri?.lastPathSegment}"
            }
            else -> { // If the URI isn't recognized,
                // do some error handling here
            }
        }

        // Call the code to actually do the query
    }
}

Java

public class ExampleProvider extends ContentProvider {
...
    // Creates a UriMatcher object.
    private static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);

    static {
        /*
         * The calls to addURI() go here for all the content URI patterns that the provider
         * recognizes. For this snippet, only the calls for table 3 are shown.
         */

        /*
         * Sets the integer value for multiple rows in table 3 to one. No wildcard is used
         * in the path.
         */
        uriMatcher.addURI("com.example.app.provider", "table3", 1);

        /*
         * Sets the code for a single row to 2. In this case, the # wildcard is
         * used. content://com.example.app.provider/table3/3 matches, but
         * content://com.example.app.provider/table3 doesn't.
         */
        uriMatcher.addURI("com.example.app.provider", "table3/#", 2);
    }
...
    // Implements ContentProvider.query()
    public Cursor query(
        Uri uri,
        String[] projection,
        String selection,
        String[] selectionArgs,
        String sortOrder) {
...
        /*
         * Choose the table to query and a sort order based on the code returned for the incoming
         * URI. Here, too, only the statements for table 3 are shown.
         */
        switch (uriMatcher.match(uri)) {


            // If the incoming URI was for all of table3
            case 1:

                if (TextUtils.isEmpty(sortOrder)) sortOrder = "_ID ASC";
                break;

            // If the incoming URI was for a single row
            case 2:

                /*
                 * Because this URI was for a single row, the _ID value part is
                 * present. Get the last path segment from the URI; this is the _ID value.
                 * Then, append the value to the WHERE clause for the query.
                 */
                selection = selection + "_ID = " + uri.getLastPathSegment();
                break;

            default:
            ...
                // If the URI isn't recognized, do some error handling here
        }
        // Call the code to actually do the query
    }

ContentUris อีกชั้นเรียนหนึ่งมีวิธีการที่สะดวกในการทำงาน ด้วยส่วน id ของ URI เนื้อหา ชั้นเรียน Uri และ Uri.Builder มีวิธีการอำนวยความสะดวกในการแยกวิเคราะห์ Uri และสร้างออบเจ็กต์ใหม่

ใช้คลาส ContentProvider

อินสแตนซ์ ContentProvider จัดการการเข้าถึง ไปยังชุดข้อมูลที่มีโครงสร้างโดยจัดการคำขอจากแอปพลิเคชันอื่น แบบฟอร์มทั้งหมด ของการเข้าถึงเรียกอีกชื่อหนึ่งว่า ContentResolver ซึ่งทำให้เกิดเป็นรูปธรรมขึ้นมา วิธี ContentProvider เพื่อรับสิทธิ์เข้าถึง

วิธีการที่จำเป็น

คลาส Abstract ContentProvider จะกำหนดเมธอด Abstract จำนวน 6 เมธอดที่ ที่คุณใช้เป็นส่วนหนึ่งของคลาสย่อยที่เป็นรูปธรรม วิธีการเหล่านี้ทั้งหมดยกเว้น แอปพลิเคชันไคลเอ็นต์จะเรียกใช้ onCreate() ที่พยายามเข้าถึงผู้ให้บริการเนื้อหาของคุณ

query()
เรียกดูข้อมูลจากผู้ให้บริการ ใช้อาร์กิวเมนต์เพื่อเลือกตาราง แถวและคอลัมน์ที่จะแสดงผล รวมถึงลำดับการจัดเรียงของผลลัพธ์ แสดงผลข้อมูลเป็นออบเจ็กต์ Cursor
insert()
แทรกแถวใหม่ลงในผู้ให้บริการ ใช้อาร์กิวเมนต์เพื่อเลือก ปลายทาง และรับค่าคอลัมน์ที่จะใช้ แสดงผล URI เนื้อหาสำหรับ แถวที่แทรกใหม่
update()
อัปเดตแถวที่มีอยู่ในผู้ให้บริการ ใช้อาร์กิวเมนต์เพื่อเลือกตารางและแถว เพื่ออัปเดตและรับค่าของคอลัมน์ที่อัปเดต แสดงผลจำนวนแถวที่อัปเดต
delete()
ลบแถวจากผู้ให้บริการ ใช้อาร์กิวเมนต์เพื่อเลือกตารางและแถวที่จะ ลบ แสดงผลจำนวนแถวที่ลบ
getType()
แสดงผลประเภท MIME ที่สอดคล้องกับ URI เนื้อหา มีคำอธิบายวิธีการนี้ในหัวข้อ รายละเอียดในส่วนใช้งานประเภท MIME ของผู้ให้บริการเนื้อหา
onCreate()
เริ่มต้นผู้ให้บริการ ระบบ Android เรียกใช้วิธีการนี้ทันทีหลังจาก จะสร้างผู้ให้บริการ ระบบจะไม่สร้างผู้ให้บริการจนกว่า ออบเจ็กต์ ContentResolver รายการพยายามเข้าถึง

เมธอดเหล่านี้มีลายเซ็นเหมือนกับชื่อที่มีชื่อเดียวกัน ContentResolver วิธี

การใช้วิธีการเหล่านี้จะต้องคำนึงถึงสิ่งต่อไปนี้

  • ทุกวิธียกเว้น onCreate() สามารถเรียกใช้โดยชุดข้อความหลายรายการพร้อมกันได้ ชุดข้อความเหล่านั้นจึงปลอดภัยต่อชุดข้อความ เพื่อเรียนรู้ ข้อมูลเพิ่มเติมเกี่ยวกับชุดข้อความต่างๆ โปรดดู ภาพรวมกระบวนการและชุดข้อความ
  • หลีกเลี่ยงการดำเนินการที่ใช้เวลานานใน onCreate() เลื่อนงานเริ่มต้นจนกว่าจะจำเป็นจริงๆ หัวข้อเกี่ยวกับการใช้เมธอด onCreate() พูดถึงเรื่องนี้โดยละเอียดยิ่งขึ้น
  • ถึงแม้ว่าคุณจะต้องปรับใช้วิธีการเหล่านี้ แต่โค้ดของคุณไม่จำเป็นต้องดำเนินการใดๆ ยกเว้น แสดงผลประเภทข้อมูลที่ต้องการ เช่น คุณสามารถป้องกันไม่ให้แอปพลิเคชันอื่นๆ ไม่ให้แทรกข้อมูลลงในบางตารางโดยการไม่สนใจการเรียกใช้ไปยัง insert() และการกลับ 0.

ใช้เมธอด query()

เมธอด ContentProvider.query() ต้องแสดงผลออบเจ็กต์ Cursor หรือถ้า ล้มเหลว แสดงผล Exception หากใช้ฐานข้อมูล SQLite เป็นข้อมูลของคุณ คุณสามารถส่งคืน Cursor ที่แสดงผลโดยหนึ่งใน query() เมธอดของคลาส SQLiteDatabase

หากคำค้นหาไม่ตรงกับแถวใดเลย ให้แสดงผล Cursor อินสแตนซ์ที่เมธอด getCount() แสดงผลเป็น 0 แสดงผล null ต่อเมื่อเกิดข้อผิดพลาดภายในระหว่างขั้นตอนการค้นหา

หากคุณไม่ได้ใช้ฐานข้อมูล SQLite เป็นพื้นที่เก็บข้อมูล ให้ใช้คลาสย่อยที่เป็นรูปธรรมรายการใดรายการหนึ่ง จาก Cursor เช่น คลาส MatrixCursor ใช้เคอร์เซอร์ซึ่งแต่ละแถวคืออาร์เรย์ของ Object อินสแตนซ์ ในชั้นเรียนนี้ ใช้ addRow() เพื่อเพิ่มแถวใหม่

ระบบ Android ต้องสามารถสื่อสารกับ Exception ข้ามขอบเขตของกระบวนการ Android สามารถดำเนินการนี้ได้สำหรับข้อยกเว้นที่เป็นประโยชน์ต่อไปนี้ ในการจัดการกับข้อผิดพลาดในการค้นหา:

  • IllegalArgumentException คุณสามารถเลือกข้ามตัวเลือกนี้ได้ในกรณีที่ผู้ให้บริการ ได้รับ URI เนื้อหาที่ไม่ถูกต้อง
  • NullPointerException

ใช้เมธอด Insert()

เมธอด insert() จะเพิ่ม แถวใหม่ไปยังตารางที่เหมาะสมโดยใช้ค่าใน ContentValues อาร์กิวเมนต์ หากไม่มีชื่อคอลัมน์ในอาร์กิวเมนต์ ContentValues คุณจะ คุณอาจต้องระบุค่าเริ่มต้นในรหัสผู้ให้บริการหรือในฐานข้อมูล สคีมา

วิธีนี้จะแสดง URI เนื้อหาสำหรับแถวใหม่ ในการสร้าง URL นี้ ให้เพิ่มพารามิเตอร์ คีย์หลักของแถว โดยปกติแล้วจะเป็นค่า _ID ไปยัง URI เนื้อหาของตาราง โดยใช้ withAppendedId()

ใช้เมธอด delete()

เมธอด delete() จะไม่จำเป็นต้องลบแถวออกจากพื้นที่เก็บข้อมูล หากใช้อะแดปเตอร์การซิงค์ กับผู้ให้บริการของคุณ ลองทำเครื่องหมายแถวที่ถูกลบ ด้วยการ "ลบ" แทนการลบทั้งแถวออก อะแดปเตอร์การซิงค์ ตรวจหาแถวที่ถูกลบ และนำออกจากเซิร์ฟเวอร์ก่อนที่จะลบจากผู้ให้บริการ

ใช้เมธอด update()

เมธอด update() ใช้อาร์กิวเมนต์ ContentValues เดียวกันกับที่ใช้โดย insert() และ ใช้ selection และ selectionArgs อาร์กิวเมนต์เดียวกัน delete() และ ContentProvider.query() ซึ่งอาจทำให้คุณนำโค้ดมาใช้ซ้ำระหว่างวิธีการเหล่านี้ได้

ใช้เมธอด onCreate()

ระบบ Android เรียก onCreate() เมื่อผู้ให้บริการเริ่มทำงาน ดำเนินการเริ่มต้นแบบรวดเร็วเท่านั้น งานในเมธอดนี้และจะเลื่อนการสร้างฐานข้อมูลและการโหลดข้อมูลจนกว่าผู้ให้บริการ ได้รับคำขอข้อมูล ถ้าคุณทำงานยาวๆ onCreate() คุณลดความเร็ว ผู้ให้บริการใหม่ ซึ่งจะทำให้การตอบกลับจากผู้ให้บริการช้าลง แอปพลิเคชัน

ข้อมูลโค้ดสองชุดต่อไปนี้แสดงการโต้ตอบระหว่าง ContentProvider.onCreate() และ Room.databaseBuilder() องค์ประกอบ ข้อมูลโค้ดแสดงการใช้งาน ContentProvider.onCreate() โดยที่ มีการสร้างออบเจ็กต์ฐานข้อมูลและแฮนเดิลให้กับออบเจ็กต์การเข้าถึงข้อมูลแล้ว

Kotlin

// Defines the database name
private const val DBNAME = "mydb"
...
class ExampleProvider : ContentProvider() {

    // Defines a handle to the Room database
    private lateinit var appDatabase: AppDatabase

    // Defines a Data Access Object to perform the database operations
    private var userDao: UserDao? = null

    override fun onCreate(): Boolean {

        // Creates a new database object
        appDatabase = Room.databaseBuilder(context, AppDatabase::class.java, DBNAME).build()

        // Gets a Data Access Object to perform the database operations
        userDao = appDatabase.userDao

        return true
    }
    ...
    // Implements the provider's insert method
    override fun insert(uri: Uri, values: ContentValues?): Uri? {
        // Insert code here to determine which DAO to use when inserting data, handle error conditions, etc.
    }
}

Java

public class ExampleProvider extends ContentProvider

    // Defines a handle to the Room database
    private AppDatabase appDatabase;

    // Defines a Data Access Object to perform the database operations
    private UserDao userDao;

    // Defines the database name
    private static final String DBNAME = "mydb";

    public boolean onCreate() {

        // Creates a new database object
        appDatabase = Room.databaseBuilder(getContext(), AppDatabase.class, DBNAME).build();

        // Gets a Data Access Object to perform the database operations
        userDao = appDatabase.getUserDao();

        return true;
    }
    ...
    // Implements the provider's insert method
    public Cursor insert(Uri uri, ContentValues values) {
        // Insert code here to determine which DAO to use when inserting data, handle error conditions, etc.
    }
}

การใช้งานประเภท MIME ของ ContentProvider

คลาส ContentProvider มี 2 วิธีในการส่งคืนประเภท MIME ดังนี้

getType()
หนึ่งในวิธีการที่จำเป็นซึ่งคุณนำไปใช้กับผู้ให้บริการรายใดก็ได้
getStreamTypes()
วิธีการที่คุณควรใช้หากผู้ให้บริการของคุณมีไฟล์

ประเภท MIME สำหรับตาราง

เมธอด getType() แสดงผล String ในรูปแบบ MIME ที่อธิบายประเภทข้อมูลที่เนื้อหาแสดงผล อาร์กิวเมนต์ URI อาร์กิวเมนต์ Uri อาจเป็นรูปแบบ ไม่ใช่ URI ที่เจาะจง ในกรณีนี้ ให้แสดงผลประเภทข้อมูลที่เชื่อมโยงกับ URI เนื้อหาที่ตรงกับ รูปแบบ

สำหรับข้อมูลประเภททั่วไป เช่น ข้อความ, HTML หรือ JPEG getType() แสดงค่ามาตรฐาน ประเภท MIME สำหรับข้อมูลนั้น คุณสามารถดูรายการประเภทมาตรฐานเหล่านี้ได้ที่ ประเภทสื่อ MIME ของ IANA เว็บไซต์ของคุณ

สำหรับ URI เนื้อหาที่ชี้ไปยังแถวหรือแถวของข้อมูลในตาราง getType() ทำรีเทิร์น ประเภท MIME ในรูปแบบ MIME เฉพาะผู้ให้บริการของ Android ได้แก่

  • ประเภทส่วน: vnd
  • ส่วนประเภทย่อย:
    • หากรูปแบบ URI มีไว้สำหรับแถวเดียว: android.cursor.item/
    • หากรูปแบบ URI มีไว้สำหรับแถวมากกว่า 1 แถว: android.cursor.dir/
  • ส่วนเฉพาะผู้ให้บริการ: vnd.<name><type>

    คุณระบุ <name> และ <type> ค่า <name> ไม่ซ้ำกันทั่วโลก และค่า <type> จะเป็นค่าเฉพาะสำหรับ URI ที่เกี่ยวข้อง รูปแบบ ตัวเลือกที่ดีสำหรับ <name> คือชื่อบริษัทของคุณหรือ บางส่วนของชื่อแพ็กเกจ Android ของแอปพลิเคชันของคุณ ตัวเลือกที่ดีสำหรับ <type> คือสตริงที่ระบุตารางที่เชื่อมโยงกับ URI

ตัวอย่างเช่น หากสิทธิ์ของผู้ให้บริการคือ com.example.app.provider และแสดงตารางที่ชื่อ table1 ประเภท MIME สำหรับหลายแถวใน table1 คือ

vnd.android.cursor.dir/vnd.com.example.provider.table1

สำหรับ table1 แถวเดียว ประเภท MIME จะเป็น

vnd.android.cursor.item/vnd.com.example.provider.table1

ประเภท MIME สำหรับไฟล์

หากผู้ให้บริการของคุณมีไฟล์ ให้ใช้ getStreamTypes() เมธอดจะแสดงอาร์เรย์ String ของประเภท MIME สำหรับไฟล์ของผู้ให้บริการ สามารถแสดงผลสำหรับ URI เนื้อหาที่ระบุ กรองประเภท MIME ที่คุณเสนอตามประเภท MIME กรองเพื่อให้แสดงผลเฉพาะประเภท MIME ที่ไคลเอ็นต์ต้องการจัดการ

เช่น ลองพิจารณาผู้ให้บริการที่เสนอรูปภาพเป็นไฟล์รูปแบบ JPG PNG และ GIF หากแอปพลิเคชันเรียกใช้ ContentResolver.getStreamTypes() ด้วยสตริงตัวกรอง image/* สำหรับบางสิ่งที่ เป็น "รูปภาพ" เมธอด ContentProvider.getStreamTypes() จะแสดงอาร์เรย์

{ "image/jpeg", "image/png", "image/gif"}

หากแอปสนใจเฉพาะไฟล์ JPG ก็สามารถเรียก ContentResolver.getStreamTypes() โดยใช้สตริงตัวกรอง *\/jpeg และ getStreamTypes() ส่งกลับ:

{"image/jpeg"}

หากผู้ให้บริการไม่มีประเภท MIME ที่ขอในสตริงตัวกรอง getStreamTypes() แสดงผล null

ใช้คลาส Contract

คลาสสัญญาเป็นคลาส public final ที่มีคำจำกัดความคงที่สำหรับ URI, ชื่อคอลัมน์, ประเภท MIME และข้อมูลเมตาอื่นๆ ที่เกี่ยวข้องกับผู้ให้บริการ ชั้นเรียน ทำสัญญาระหว่างผู้ให้บริการกับแอปพลิเคชันอื่นๆ โดยตรวจสอบว่าผู้ให้บริการ สามารถเข้าถึงได้อย่างถูกต้อง แม้ว่าจะมีการเปลี่ยนแปลงค่าจริงของ URI, ชื่อคอลัมน์ เป็นต้น

คลาสสัญญายังช่วยนักพัฒนาซอฟต์แวร์เพราะมักจะมีชื่อช่วยจำสำหรับค่าคงที่ ดังนั้น นักพัฒนาซอฟต์แวร์จึงมีโอกาสน้อยลงที่จะใช้ค่าที่ไม่ถูกต้องสำหรับชื่อคอลัมน์หรือ URI เนื่องจากเป็น สามารถมีเอกสาร Javadoc ได้ สภาพแวดล้อมในการพัฒนาซอฟต์แวร์แบบผสานรวม เช่น Android Studio สามารถเติมชื่อคงที่โดยอัตโนมัติจากคลาสสัญญาและแสดง Javadoc สำหรับ ค่าคงที่นี้

นักพัฒนาแอปจะไม่สามารถเข้าถึงไฟล์ชั้นเรียนของคลาสสัญญาจากแอปพลิเคชันของคุณ แต่สามารถ ทำการคอมไพล์ลงในแอปพลิเคชันโดยใช้ไฟล์ JAR ที่คุณมี

คลาส ContactsContract และคลาสที่ซ้อนกันเป็นตัวอย่างของ คลาสที่มีสัญญาสูง

ใช้สิทธิ์ของผู้ให้บริการเนื้อหา

สิทธิ์และการเข้าถึงระบบ Android ในทุกด้านมีอธิบายไว้อย่างละเอียดใน เคล็ดลับความปลอดภัย ภาพรวมของพื้นที่เก็บข้อมูลและไฟล์ยัง อธิบายความปลอดภัยและสิทธิ์ที่จะมีผลสำหรับพื้นที่เก็บข้อมูลประเภทต่างๆ กล่าวโดยสรุปคือประเด็นสำคัญมีดังต่อไปนี้

  • โดยค่าเริ่มต้น ไฟล์ข้อมูลที่จัดเก็บในที่จัดเก็บข้อมูลภายในของอุปกรณ์จะเป็นแบบส่วนตัว แอปพลิเคชันและผู้ให้บริการ
  • ฐานข้อมูล SQLiteDatabase รายการที่คุณสร้างจะเป็นแบบส่วนตัว แอปพลิเคชันและผู้ให้บริการ
  • โดยค่าเริ่มต้น ไฟล์ข้อมูลที่คุณบันทึกลงในพื้นที่เก็บข้อมูลภายนอกจะเป็นสาธารณะและ ที่อ่านได้ทั่วโลก คุณใช้ผู้ให้บริการเนื้อหาเพื่อจำกัดการเข้าถึงไฟล์ใน ที่จัดเก็บข้อมูลภายนอก เนื่องจากแอปพลิเคชันอื่นสามารถใช้การเรียก API อื่นๆ เพื่ออ่านและเขียนได้
  • เมธอดนี้จะเรียกร้องให้เปิดหรือสร้างไฟล์หรือฐานข้อมูล SQLite ภายในอุปกรณ์ พื้นที่เก็บข้อมูลอาจให้สิทธิ์ทั้งการอ่านและเขียนแอปพลิเคชันอื่นๆ ทั้งหมด หากคุณ ใช้ไฟล์หรือฐานข้อมูลภายในเป็นที่เก็บของผู้ให้บริการ และให้ "สามารถอ่านได้ทั่วโลก" หรือ "world-writeable" สิทธิ์ที่คุณกำหนดไว้สำหรับผู้ให้บริการ ไฟล์ Manifest ของไฟล์ดังกล่าวไม่ปกป้องข้อมูลของคุณ การเข้าถึงเริ่มต้นสำหรับไฟล์และฐานข้อมูลใน ที่จัดเก็บข้อมูลภายในเป็นแบบ "ส่วนตัว" ไม่ต้องเปลี่ยนการตั้งค่านี้สำหรับที่เก็บของผู้ให้บริการ

หากต้องการใช้สิทธิ์ของผู้ให้บริการเนื้อหาเพื่อควบคุมการเข้าถึงข้อมูลของคุณ ให้ทำดังนี้ จัดเก็บข้อมูลของคุณในไฟล์ภายใน ฐานข้อมูล SQLite หรือระบบคลาวด์ เช่น บนเซิร์ฟเวอร์ระยะไกล และเก็บไฟล์และฐานข้อมูลเป็นส่วนตัวในแอปพลิเคชันของคุณ

ใช้สิทธิ์

โดยค่าเริ่มต้น แอปพลิเคชันทั้งหมดจะอ่านหรือเขียนข้อมูลจากผู้ให้บริการได้ แม้ว่าข้อมูลสำคัญจะเป็น เป็นส่วนตัว เนื่องจากโดยค่าเริ่มต้นผู้ให้บริการของคุณไม่ได้ตั้งค่าสิทธิ์ไว้ หากต้องการเปลี่ยนแปลง ตั้งค่าสิทธิ์สำหรับผู้ให้บริการในไฟล์ Manifest โดยใช้แอตทริบิวต์หรือหน่วยย่อย ขององค์ประกอบ <provider> คุณสามารถตั้งค่าสิทธิ์ที่ใช้กับผู้ให้บริการทั้งหมด กับบางตาราง บางบันทึก หรือทั้งสามรายการ

คุณกำหนดสิทธิ์สำหรับผู้ให้บริการ องค์ประกอบ <permission> ในไฟล์ Manifest หากต้องการทำให้ ของผู้ให้บริการของคุณโดยเฉพาะ ให้ใช้การกำหนดขอบเขตแบบ Java สำหรับ android:name เช่น ตั้งชื่อสิทธิ์อ่าน com.example.app.provider.permission.READ_PROVIDER

รายการต่อไปนี้จะอธิบายขอบเขตสิทธิ์ของผู้ให้บริการ โดยเริ่มจาก ที่ใช้กับผู้ให้บริการทั้งหมด และทำให้มีความละเอียดมากขึ้น สิทธิ์ที่มีความละเอียดมากกว่าจะมีลำดับความสำคัญสูงกว่าสิทธิ์ที่มีขอบเขตใหญ่กว่า

สิทธิ์ระดับผู้ให้บริการแบบอ่านและเขียนรายการเดียว
สิทธิ์ 1 รายการที่ควบคุมทั้งสิทธิ์อ่านและเขียนสำหรับผู้ให้บริการทั้งหมด ระบุ ที่มีแอตทริบิวต์ android:permission ของค่า องค์ประกอบ <provider>
แยกสิทธิ์ระดับผู้ให้บริการในการอ่านและเขียน
สิทธิ์อ่านและสิทธิ์ในการเขียนสำหรับผู้ให้บริการทั้งหมด คุณเป็นคนระบุ ด้วย android:readPermission และ android:writePermission ของแอตทริบิวต์ องค์ประกอบ <provider> โดยจะมีลำดับความสำคัญเหนือกว่าสิทธิ์ที่กำหนดโดย android:permission
สิทธิ์ระดับเส้นทาง
สิทธิ์อ่าน เขียน หรืออ่าน/เขียนสำหรับ URI เนื้อหาในผู้ให้บริการ คุณระบุ URI แต่ละรายการที่คุณต้องการควบคุมด้วย <path-permission> องค์ประกอบย่อยของ องค์ประกอบ <provider> สำหรับ URI เนื้อหาแต่ละรายการที่คุณระบุ คุณสามารถระบุ สิทธิ์อ่าน/เขียน สิทธิ์อ่าน สิทธิ์เขียน หรือทั้ง 3 อย่าง การอ่านและ สิทธิ์การเขียนจะมีผลเหนือกว่าสิทธิ์อ่าน/เขียน รวมถึงที่ระดับเส้นทาง จะมีความสำคัญเหนือสิทธิ์ระดับผู้ให้บริการ
สิทธิ์ชั่วคราว
ระดับสิทธิ์ที่ให้สิทธิ์เข้าถึงแอปพลิเคชันชั่วคราว แม้ว่าแอปพลิเคชันนั้น ไม่มีสิทธิ์ที่จำเป็นตามปกติ รูปภาพชั่วคราว ฟีเจอร์การเข้าถึงช่วยลดจำนวนสิทธิ์ที่แอปพลิเคชันต้องขอ ไฟล์ Manifest เมื่อคุณเปิดสิทธิ์ชั่วคราว แอปพลิเคชันเดียวที่จำเป็นต้องใช้ สิทธิ์ถาวรสำหรับผู้ให้บริการของคุณคือรายการที่เข้าถึง ข้อมูลของคุณ

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

ออกแบบแอปอีเมลเพื่อ เมื่อผู้ใช้ต้องการแสดงรูปภาพ แอปจะส่ง Intent ที่มี URI เนื้อหาของรูปภาพและธงสิทธิ์ให้กับโปรแกรมดูรูปภาพ โปรแกรมดูรูปภาพสามารถ ให้ค้นหาผู้ให้บริการอีเมลของคุณเพื่อเรียกรูปภาพ แม้ว่าผู้ดูจะไม่ มีสิทธิ์อ่านตามปกติสำหรับผู้ให้บริการ

หากต้องการเปิดสิทธิ์ชั่วคราว ให้ตั้งค่า แอตทริบิวต์ android:grantUriPermissions ของ <provider> องค์ประกอบหรือเพิ่มอย่างน้อย 1 รายการ <grant-uri-permission> องค์ประกอบย่อยลงใน องค์ประกอบ <provider> โทร Context.revokeUriPermission()เมื่อใดก็ตามที่คุณเลิกรองรับ URI เนื้อหาที่เชื่อมโยงกับสิทธิ์ชั่วคราวจาก

ค่าของแอตทริบิวต์จะกำหนดจำนวนผู้ให้บริการที่สามารถเข้าถึงได้ หากตั้งค่าแอตทริบิวต์เป็น "true" ระบบจะให้สิทธิ์ชั่วคราว สิทธิ์สำหรับผู้ให้บริการทั้งหมด โดยลบล้างสิทธิ์อื่นๆ ที่จำเป็น ตามสิทธิ์ระดับผู้ให้บริการหรือระดับเส้นทาง

หากตั้งค่าสถานะนี้เป็น "false" ให้เพิ่ม <grant-uri-permission> องค์ประกอบย่อยลงใน องค์ประกอบ <provider> องค์ประกอบย่อยแต่ละรายการจะระบุ URI เนื้อหาหรือ URI ที่ได้รับสิทธิ์เข้าถึงชั่วคราว

หากต้องการมอบสิทธิ์เข้าถึงชั่วคราวไปยังแอปพลิเคชัน Intent นั้นต้องมี ธงFLAG_GRANT_READ_URI_PERMISSION แฟล็ก FLAG_GRANT_WRITE_URI_PERMISSION หรือทั้งคู่ เหล่านี้ ได้รับการตั้งค่าด้วยเมธอด setFlags()

หากไม่มีแอตทริบิวต์ android:grantUriPermissions ระบบจะถือว่าเป็น "false"

<provider> องค์ประกอบ

เช่น คอมโพเนนต์ Activity และ Service คลาสย่อยของ ContentProvider ในไฟล์ Manifest ของแอปพลิเคชันนั้น โดยใช้ องค์ประกอบ <provider> ระบบ Android ได้รับข้อมูลต่อไปนี้จาก องค์ประกอบ

หน่วยงาน (android:authorities)
ชื่อสัญลักษณ์ที่ระบุผู้ให้บริการทั้งหมดภายในระบบ ช่วงเวลานี้ ได้รับการอธิบายโดยละเอียดยิ่งขึ้นใน ออกแบบ URI เนื้อหา
ชื่อคลาสของผู้ให้บริการ (android:name)
คลาสที่ใช้ ContentProvider ชั้นเรียนนี้ ซึ่งอธิบายโดยละเอียดยิ่งขึ้นใน ใช้คลาส ContentProvider
สิทธิ์
แอตทริบิวต์ที่ระบุสิทธิ์ที่แอปพลิเคชันอื่นต้องมีเพื่อเข้าถึง ข้อมูลของผู้ให้บริการ
  • android:grantUriPermissions: แฟล็กสิทธิ์ชั่วคราว
  • android:permission: สิทธิ์อ่าน/เขียนทั่วทั้งผู้ให้บริการรายเดียว
  • android:readPermission: สิทธิ์อ่านทั่วทั้งผู้ให้บริการ
  • android:writePermission: สิทธิ์เขียนทั่วทั้งผู้ให้บริการ

สิทธิ์และแอตทริบิวต์ที่เกี่ยวข้องมีการอธิบายไว้ในบทความเพิ่มเติม รายละเอียดใน ส่วนใช้สิทธิ์ของผู้ให้บริการเนื้อหา

แอตทริบิวต์การเริ่มต้นและการควบคุม
แอตทริบิวต์เหล่านี้จะกำหนดวิธีการและเวลาที่ระบบ Android จะเริ่มต้นผู้ให้บริการ ลักษณะกระบวนการของผู้ให้บริการและการตั้งค่ารันไทม์อื่นๆ
  • android:enabled: ธงให้ระบบเริ่มต้นผู้ให้บริการ
  • android:exported: แจ้งการอนุญาตให้แอปพลิเคชันอื่นใช้ผู้ให้บริการรายนี้
  • android:initOrder: ลำดับการเริ่มต้นผู้ให้บริการนี้ เมื่อเทียบกับผู้ให้บริการรายอื่นในกระบวนการเดียวกัน
  • android:multiProcess: ธงให้ระบบเริ่มต้นผู้ให้บริการ ในกระบวนการเดียวกับไคลเอ็นต์ที่โทร
  • android:process: ชื่อของกระบวนการที่ผู้ให้บริการเรียกใช้
  • android:syncable: ธงที่ระบุว่าข้อมูลของผู้ให้บริการจะเป็น ซิงค์กับข้อมูลบนเซิร์ฟเวอร์

แอตทริบิวต์เหล่านี้ได้รับการระบุไว้อย่างชัดเจนในคู่มือ องค์ประกอบ <provider>

แอตทริบิวต์ที่ให้ข้อมูล
ไอคอนและป้ายกำกับที่ไม่บังคับสำหรับผู้ให้บริการ:
  • android:icon: ทรัพยากรที่ถอนออกได้ที่มีไอคอนสำหรับผู้ให้บริการ ไอคอนนี้จะปรากฏถัดจากป้ายกำกับของผู้ให้บริการในรายการแอปใน การตั้งค่า > แอป > ทั้งหมด
  • android:label: ป้ายกำกับที่ให้ข้อมูลที่อธิบายผู้ให้บริการ ข้อมูล หรือทั้งสองอย่าง ป้ายกำกับจะปรากฏในรายการแอปใน การตั้งค่า > แอป > ทั้งหมด

แอตทริบิวต์เหล่านี้ได้รับการระบุไว้อย่างชัดเจนในคู่มือ องค์ประกอบ <provider>

หมายเหตุ: หากคุณกําหนดเป้าหมายเป็น Android 11 ขึ้นไป โปรดดูที่ เอกสารประกอบเกี่ยวกับระดับการเข้าถึงแพ็กเกจ สำหรับการกำหนดค่าเพิ่มเติม

Intent และการเข้าถึงข้อมูล

แอปพลิเคชันสามารถเข้าถึงผู้ให้บริการเนื้อหาโดยอ้อมด้วย Intent แอปพลิเคชันไม่ได้เรียกใช้เมธอดใดๆ ของ ContentResolver หรือ ContentProvider แต่จะส่ง Intent ที่เริ่มกิจกรรม ซึ่งมักเป็นส่วนหนึ่งของแอปพลิเคชันของผู้ให้บริการเอง กิจกรรมปลายทางรับผิดชอบ ดึงข้อมูลและแสดงข้อมูลใน UI

ขึ้นอยู่กับการดำเนินการใน Intent กิจกรรมปลายทางยังแจ้งเตือนให้ผู้ใช้แก้ไขข้อมูลของผู้ให้บริการด้วย Intent อาจมีคำว่า "พิเศษ" อยู่ด้วย ข้อมูลที่กิจกรรมปลายทางแสดง ใน UI ผู้ใช้จะมีตัวเลือกในการเปลี่ยนแปลงข้อมูลนี้ก่อนที่จะนำไปใช้เพื่อปรับเปลี่ยน ในผู้ให้บริการ

คุณใช้การเข้าถึง Intent เพื่อช่วยความสมบูรณ์ของข้อมูลได้ ผู้ให้บริการของคุณอาจพึ่งพา เกี่ยวกับการแทรก อัปเดต และลบข้อมูลตามตรรกะทางธุรกิจที่กำหนดไว้อย่างเคร่งครัด ถ้า ในกรณีนี้ การปล่อยให้แอปพลิเคชันอื่นๆ แก้ไขข้อมูลของคุณได้โดยตรงอาจทำให้เกิด ข้อมูลไม่ถูกต้อง

หากต้องการให้นักพัฒนาซอฟต์แวร์ใช้การเข้าถึง Intent ให้จัดทำเอกสารอย่างละเอียด อธิบายว่าทำไมการเข้าถึงด้วยความตั้งใจโดยใช้ UI ของแอปพลิเคชันจึงดีกว่าการพยายาม แก้ไขข้อมูลด้วยโค้ด

การจัดการกับความตั้งใจที่เข้ามาใหม่ซึ่งต้องการปรับเปลี่ยนข้อมูลของผู้ให้บริการก็ไม่แตกต่างจาก การจัดการกับ Intent อื่นๆ คุณสามารถเรียนรู้เพิ่มเติมเกี่ยวกับการใช้ Intent ด้วยการอ่าน ตัวกรอง Intent และ Intent

ดูข้อมูลที่เกี่ยวข้องเพิ่มเติมได้ที่ ภาพรวมผู้ให้บริการปฏิทิน