Contacts Provider เป็นคอมโพเนนต์ Android ที่มีประสิทธิภาพและยืดหยุ่นซึ่งจัดการที่เก็บส่วนกลางเกี่ยวกับผู้คนของอุปกรณ์ Contacts Provider เป็นแหล่งที่มาของข้อมูลที่คุณเห็นในแอปพลิเคชันรายชื่อผู้ติดต่อของอุปกรณ์ และคุณยังเข้าถึงข้อมูลดังกล่าวในแอปพลิเคชันของคุณเอง รวมถึงโอนข้อมูลระหว่างอุปกรณ์และบริการออนไลน์ได้ด้วย ผู้ให้บริการรองรับแหล่งข้อมูลที่หลากหลายและพยายามจัดการข้อมูลให้ได้มากที่สุดสำหรับแต่ละคน ส่งผลให้องค์กรมีความซับซ้อน ด้วยเหตุนี้ API ของผู้ให้บริการจึงมีชุดคลาสและอินเทอร์เฟซ Contract ที่ครอบคลุมซึ่งช่วยอำนวยความสะดวกทั้งในการดึงข้อมูลและการแก้ไขข้อมูล
คู่มือนี้จะอธิบายสิ่งต่อไปนี้
- โครงสร้างผู้ให้บริการพื้นฐาน
- วิธีดึงข้อมูลจากผู้ให้บริการ
- วิธีแก้ไขข้อมูลในผู้ให้บริการ
- วิธีเขียนอะแดปเตอร์การซิงค์เพื่อซิงค์ข้อมูลจากเซิร์ฟเวอร์ไปยัง Contacts Provider
คู่มือนี้ถือว่าคุณทราบข้อมูลเบื้องต้นเกี่ยวกับ Content Provider ของ Android แล้ว ดูข้อมูลเพิ่มเติมเกี่ยวกับ Content Provider ของ Android ได้ที่คู่มือ ข้อมูลเบื้องต้นเกี่ยวกับ Content Provider
องค์กร Contacts Provider
Contacts Provider เป็นคอมโพเนนต์ Content Provider ของ Android โดยจะเก็บรักษาข้อมูลเกี่ยวกับบุคคล 3 ประเภท ซึ่งแต่ละประเภทจะสอดคล้องกับตารางที่ผู้ให้บริการเสนอ ดังที่แสดงในรูปที่ 1
รูปที่ 1 โครงสร้างตาราง Contacts Provider
โดยทั่วไปแล้ว ตารางทั้ง 3 ตารางจะเรียกตามชื่อคลาส Contract คลาสจะกำหนดค่าคงที่สำหรับ URI เนื้อหา ชื่อคอลัมน์ และค่าคอลัมน์ที่ตารางใช้ ดังนี้
-
ตาราง
ContactsContract.Contacts - แถวที่แสดงถึงบุคคลต่างๆ โดยอิงตามการรวมแถวข้อมูลติดต่อดิบ
-
ตาราง
ContactsContract.RawContacts - แถวที่มีข้อมูลสรุปของบุคคล ซึ่งเจาะจงตามบัญชีและประเภทผู้ใช้
-
ตาราง
ContactsContract.Data - แถวที่มีรายละเอียดของข้อมูลติดต่อดิบ เช่น อีเมลหรือหมายเลขโทรศัพท์
ตารางอื่นๆ ที่แสดงโดยคลาส Contract ใน ContactsContract เป็นตารางเสริมที่ Contacts Provider ใช้เพื่อจัดการการดำเนินการหรือรองรับฟังก์ชันเฉพาะในแอปพลิเคชันรายชื่อติดต่อหรือแอปพลิเคชันโทรศัพท์ของอุปกรณ์
ข้อมูลติดต่อดิบ
ข้อมูลติดต่อดิบแสดงข้อมูลของบุคคลที่มาจากประเภทบัญชีและชื่อบัญชีเดียว เนื่องจาก Contacts Provider อนุญาตให้ใช้บริการออนไลน์มากกว่า 1 รายการเป็นแหล่งข้อมูลสำหรับบุคคลหนึ่ง Contacts Provider จึงอนุญาตให้มีข้อมูลติดต่อดิบหลายรายการสำหรับบุคคลเดียวกัน นอกจากนี้ ข้อมูลติดต่อดิบหลายรายการยังช่วยให้ผู้ใช้รวมข้อมูลของบุคคลจากบัญชีมากกว่า 1 บัญชีจากประเภทบัญชีเดียวกันได้ด้วย
ข้อมูลส่วนใหญ่ของข้อมูลติดต่อดิบไม่ได้จัดเก็บไว้ในตาราง ContactsContract.RawContacts แต่จะจัดเก็บไว้ในแถวอย่างน้อย 1 แถวในตาราง ContactsContract.Data แทน แถวข้อมูลแต่ละแถวมีคอลัมน์ Data.RAW_CONTACT_ID ที่มีค่า RawContacts._ID ของแถว ContactsContract.RawContacts หลัก
คอลัมน์ข้อมูลติดต่อดิบที่สำคัญ
คอลัมน์ที่สำคัญในตาราง ContactsContract.RawContacts แสดงอยู่ในตารางที่ 1 โปรดอ่านหมายเหตุที่ตามหลังตารางต่อไปนี้
ตารางที่ 1 คอลัมน์ข้อมูลติดต่อดิบที่สำคัญ
| ชื่อคอลัมน์ | ใช้ | หมายเหตุ |
|---|---|---|
ACCOUNT_NAME
|
ชื่อบัญชีสำหรับประเภทบัญชีที่เป็นแหล่งที่มาของข้อมูลติดต่อดิบนี้
ตัวอย่างเช่น ชื่อบัญชีของบัญชี Google คืออีเมล Gmail ของเจ้าของอุปกรณ์ ดูข้อมูลเพิ่มเติมเกี่ยวกับ ACCOUNT_TYPE ได้ในรายการถัดไป
|
รูปแบบของชื่อนี้จะเจาะจงตามประเภทบัญชี ซึ่งไม่จำเป็นต้องเป็นอีเมล |
ACCOUNT_TYPE
|
ประเภทบัญชีที่เป็นแหล่งที่มาของข้อมูลติดต่อดิบนี้ เช่น ประเภทบัญชีของบัญชี Google คือ com.google ระบุประเภทบัญชีด้วยตัวระบุโดเมนสำหรับโดเมนที่คุณเป็นเจ้าของหรือควบคุมเสมอ ซึ่งจะช่วยให้มั่นใจได้ว่าประเภทบัญชีของคุณไม่ซ้ำกัน
|
ประเภทบัญชีที่ให้ข้อมูลรายชื่อติดต่อมักจะมีอะแดปเตอร์การซิงค์ที่เชื่อมโยงซึ่งจะซิงค์ข้อมูลกับ Contacts Provider |
DELETED
|
แฟล็ก "ลบแล้ว" สำหรับข้อมูลติดต่อดิบ | แฟล็กนี้ช่วยให้ Contacts Provider เก็บแถวไว้ภายในจนกว่าอะแดปเตอร์การซิงค์จะลบแถวออกจากเซิร์ฟเวอร์ได้ แล้วจึงลบแถวออกจากที่เก็บในที่สุด |
หมายเหตุ
หมายเหตุสำคัญเกี่ยวกับตาราง ContactsContract.RawContacts มีดังนี้
-
ชื่อของข้อมูลติดต่อดิบไม่ได้จัดเก็บไว้ในแถวของข้อมูลนั้นใน
ContactsContract.RawContactsแต่จะจัดเก็บไว้ในตารางContactsContract.DataในแถวContactsContract.CommonDataKinds.StructuredNameข้อมูลติดต่อดิบจะมีแถวประเภทนี้เพียงแถวเดียวในตารางContactsContract.Data -
ข้อควรระวัง: หากต้องการใช้ข้อมูลบัญชีของคุณเองในแถวข้อมูลติดต่อดิบ คุณต้องลงทะเบียนกับ
AccountManagerก่อน โดยแจ้งให้ผู้ใช้เพิ่มประเภทบัญชีและชื่อบัญชีลงในรายการบัญชี หากไม่ดำเนินการ Contacts Provider จะลบแถวข้อมูลติดต่อดิบโดยอัตโนมัติตัวอย่างเช่น หากต้องการให้แอปเก็บรักษาข้อมูลรายชื่อติดต่อสำหรับบริการบนเว็บที่มีโดเมน
com.example.dataserviceและบัญชีของผู้ใช้สำหรับบริการของคุณคือbecky.sharp@dataservice.example.comผู้ใช้ต้องเพิ่ม "ประเภท" บัญชี (com.example.dataservice) และ "ชื่อ" บัญชี (becky.smart@dataservice.example.com) ก่อน แอปจึงจะเพิ่มแถวข้อมูลติดต่อดิบได้ คุณสามารถอธิบายข้อกำหนดนี้ให้ผู้ใช้ทราบในเอกสารประกอบ หรือจะแจ้งให้ผู้ใช้เพิ่มประเภทและชื่อ หรือทั้ง 2 อย่างก็ได้ ประเภทบัญชีและชื่อบัญชีจะอธิบายไว้อย่างละเอียดยิ่งขึ้นในส่วนถัดไป
แหล่งที่มาของข้อมูลติดต่อดิบ
หากต้องการทำความเข้าใจวิธีการทำงานของข้อมูลติดต่อดิบ ให้พิจารณาผู้ใช้ "Emily Dickinson" ซึ่งมีบัญชีผู้ใช้ 3 บัญชีที่กำหนดไว้ในอุปกรณ์ ดังนี้
emily.dickinson@gmail.comemilyd@gmail.com- บัญชี Twitter "belle_of_amherst"
ผู้ใช้รายนี้เปิดใช้ซิงค์รายชื่อติดต่อสำหรับทั้ง 3 บัญชีในการตั้งค่าบัญชี
สมมติว่า Emily Dickinson เปิดหน้าต่างเบราว์เซอร์ ลงชื่อเข้าใช้ Gmail เป็น emily.dickinson@gmail.com เปิดรายชื่อติดต่อ และเพิ่ม "Thomas Higginson" ต่อมา เธอเข้าสู่ระบบ Gmail ในชื่อ emilyd@gmail.com และส่งอีเมลถึง "Thomas Higginson" ซึ่งจะเพิ่มเขาเป็นรายชื่อติดต่อโดยอัตโนมัติ นอกจากนี้ เธอยังติดตาม "colonel_tom" (รหัส Twitter ของ Thomas Higginson) ใน Twitter ด้วย
Contacts Provider จะสร้างข้อมูลติดต่อดิบ 3 รายการซึ่งเป็นผลมาจากการดำเนินการนี้ ดังนี้
-
ข้อมูลติดต่อดิบของ "Thomas Higginson" ที่เชื่อมโยงกับ
emily.dickinson@gmail.comประเภทบัญชีผู้ใช้คือ Google -
ข้อมูลติดต่อดิบรายการที่ 2 สำหรับ "Thomas Higginson" ที่เชื่อมโยงกับ
emilyd@gmail.comประเภทบัญชีผู้ใช้เป็น Google ด้วย มีข้อมูลติดต่อดิบรายการที่ 2 แม้ว่าชื่อจะเหมือนกับชื่อก่อนหน้า เนื่องจากมีการเพิ่มบุคคลดังกล่าวสำหรับบัญชีผู้ใช้อื่น - ข้อมูลติดต่อดิบรายการที่ 3 สำหรับ "Thomas Higginson" ที่เชื่อมโยงกับ "belle_of_amherst" ประเภทบัญชีผู้ใช้คือ Twitter
ข้อมูล
ดังที่ระบุไว้ก่อนหน้านี้ ข้อมูลสำหรับข้อมูลติดต่อดิบจะจัดเก็บไว้ในแถว ContactsContract.Data ที่ลิงก์กับค่า _ID ของข้อมูลติดต่อดิบ ซึ่งช่วยให้ข้อมูลติดต่อดิบรายการเดียวมีข้อมูลประเภทเดียวกันได้หลายรายการ เช่น อีเมลหรือหมายเลขโทรศัพท์ ตัวอย่างเช่น หาก "Thomas Higginson" สำหรับ emilyd@gmail.com (แถวข้อมูลติดต่อดิบสำหรับ Thomas Higginson ที่เชื่อมโยงกับบัญชี Google emilyd@gmail.com) มีอีเมลบ้านเป็น thigg@gmail.com และอีเมลที่ทำงานเป็น thomas.higginson@gmail.com Contacts Provider จะจัดเก็บแถวอีเมล 2 แถวและลิงก์ทั้ง 2 แถวกับข้อมูลติดต่อดิบ
โปรดสังเกตว่าข้อมูลประเภทต่างๆ จะจัดเก็บไว้ในตารางเดียวนี้ แถวรายละเอียดชื่อที่แสดง หมายเลขโทรศัพท์ อีเมล ที่อยู่ไปรษณีย์ รูปภาพ และเว็บไซต์ทั้งหมดจะอยู่ในตาราง ContactsContract.Data เพื่อช่วยจัดการเรื่องนี้ ตาราง ContactsContract.Data จึงมีคอลัมน์บางคอลัมน์ที่มีชื่อที่สื่อความหมาย และคอลัมน์อื่นๆ ที่มีชื่อทั่วไป เนื้อหาของคอลัมน์ชื่อที่สื่อความหมายจะมีความหมายเหมือนกันไม่ว่าข้อมูลในแถวจะเป็นประเภทใดก็ตาม ส่วนเนื้อหาของคอลัมน์ชื่อทั่วไปจะมีความหมายแตกต่างกันไปตามประเภทข้อมูล
ชื่อคอลัมน์ที่สื่อความหมาย
ตัวอย่างชื่อคอลัมน์ที่สื่อความหมายมีดังนี้
-
RAW_CONTACT_ID -
ค่าของคอลัมน์
_IDของข้อมูลติดต่อดิบสำหรับข้อมูลนี้ -
MIMETYPE -
ประเภทข้อมูลที่จัดเก็บไว้ในแถวนี้ ซึ่งแสดงเป็นประเภท MIME ที่กำหนดเอง Contacts Provider จะใช้ประเภท MIME ที่กำหนดไว้ในคลาสย่อยของ
ContactsContract.CommonDataKindsประเภท MIME เหล่านี้เป็นโอเพนซอร์ส และแอปพลิเคชันหรืออะแดปเตอร์การซิงค์ที่ทำงานร่วมกับ Contacts Provider สามารถใช้ได้ -
IS_PRIMARY -
หากแถวข้อมูลประเภทนี้เกิดขึ้นได้มากกว่า 1 ครั้งสำหรับข้อมูลติดต่อดิบ คอลัมน์
IS_PRIMARYจะแจ้งแถวข้อมูลที่มีข้อมูลหลักสำหรับประเภทนั้น เช่น หากผู้ใช้กดหมายเลขโทรศัพท์ของรายชื่อติดต่อค้างไว้แล้วเลือกตั้งเป็นค่าเริ่มต้น แถวContactsContract.Dataที่มีหมายเลขนั้นจะมีการตั้งค่าคอลัมน์IS_PRIMARYเป็นค่าที่ไม่ใช่ 0
ชื่อคอลัมน์ทั่วไป
มีคอลัมน์ทั่วไป 15 คอลัมน์ชื่อ DATA1 ถึง DATA15 ซึ่งโดยทั่วไปจะพร้อมใช้งาน และคอลัมน์ทั่วไปอีก 4 คอลัมน์ชื่อ SYNC1 ถึง SYNC4 ซึ่งควรใช้โดยอะแดปเตอร์การซิงค์เท่านั้น ค่าคงที่ชื่อคอลัมน์ทั่วไปจะใช้งานได้เสมอ ไม่ว่าแถวจะมีข้อมูลประเภทใดก็ตาม
คอลัมน์ DATA1 มีการจัดทำดัชนี Contacts Provider จะใช้คอลัมน์นี้เสมอสำหรับข้อมูลที่ผู้ให้บริการคาดว่าจะเป็นเป้าหมายที่พบบ่อยที่สุดของการค้นหา เช่น ในแถวอีเมล คอลัมน์นี้จะมีอีเมลจริง
ตามแบบแผน คอลัมน์ DATA15 จะสงวนไว้สำหรับจัดเก็บข้อมูล Binary Large Object (BLOB) เช่น ภาพขนาดย่อของรูปภาพ
ชื่อคอลัมน์เฉพาะประเภท
Contacts Provider ยังมีค่าคงที่ของชื่อคอลัมน์เฉพาะประเภทซึ่งกำหนดไว้ในคลาสย่อยของ ContactsContract.CommonDataKinds เพื่ออำนวยความสะดวกในการทำงานกับคอลัมน์สำหรับแถวประเภทหนึ่งๆ ด้วย ค่าคงที่จะกำหนดชื่อค่าคงที่ที่แตกต่างกันให้กับชื่อคอลัมน์เดียวกัน ซึ่งจะช่วยให้คุณเข้าถึงข้อมูลในแถวของประเภทหนึ่งๆ ได้
เช่น คลาส ContactsContract.CommonDataKinds.Email จะกำหนดค่าคงที่ของชื่อคอลัมน์ที่เจาะจงประเภทสำหรับแถว ContactsContract.Data ที่มีประเภท MIME เป็น Email.CONTENT_ITEM_TYPE คลาสมีค่าคงที่ ADDRESS สำหรับคอลัมน์อีเมล ค่าจริงของ ADDRESS คือ "data1" ซึ่งเหมือนกับชื่อทั่วไปของคอลัมน์
ข้อควรระวัง: อย่าเพิ่มข้อมูลที่กำหนดเองของคุณลงในตาราง ContactsContract.Data โดยใช้แถวที่มีประเภท MIME ที่กำหนดไว้ล่วงหน้าของผู้ให้บริการ หากดำเนินการดังกล่าว คุณอาจสูญเสียข้อมูลหรือทำให้ผู้ให้บริการทำงานผิดปกติ เช่น คุณไม่ควรเพิ่มแถวที่มีประเภท MIME Email.CONTENT_ITEM_TYPE ซึ่งมีชื่อผู้ใช้แทนอีเมลในคอลัมน์ DATA1 หากคุณใช้ประเภท MIME ที่กำหนดเองสำหรับแถว คุณจะกำหนดชื่อคอลัมน์เฉพาะประเภทของคุณเองและใช้คอลัมน์ตามที่ต้องการได้อย่างอิสระ
รูปที่ 2 แสดงลักษณะของคอลัมน์คำอธิบายและคอลัมน์ข้อมูลที่ปรากฏในแถว ContactsContract.Data และลักษณะของชื่อคอลัมน์ที่เจาะจงประเภทซึ่ง "วางซ้อน" ชื่อคอลัมน์ทั่วไป
รูปที่ 2 ชื่อคอลัมน์เฉพาะประเภทและชื่อคอลัมน์ทั่วไป
คลาสชื่อคอลัมน์เฉพาะประเภท
ตารางที่ 2 แสดงคลาสชื่อคอลัมน์ที่เจาะจงประเภทซึ่งใช้กันมากที่สุด
ตารางที่ 2 คลาสชื่อคอลัมน์เฉพาะประเภท
| คลาสการแมป | ประเภทของข้อมูล | หมายเหตุ |
|---|---|---|
ContactsContract.CommonDataKinds.StructuredName |
ข้อมูลชื่อสำหรับข้อมูลติดต่อดิบที่เชื่อมโยงกับแถวข้อมูลนี้ | ข้อมูลติดต่อดิบจะมีแถวเหล่านี้เพียงแถวเดียว |
ContactsContract.CommonDataKinds.Photo |
รูปภาพหลักสำหรับข้อมูลติดต่อดิบที่เชื่อมโยงกับแถวข้อมูลนี้ | ข้อมูลติดต่อดิบจะมีแถวเหล่านี้เพียงแถวเดียว |
ContactsContract.CommonDataKinds.Email |
อีเมลสำหรับข้อมูลติดต่อดิบที่เชื่อมโยงกับแถวข้อมูลนี้ | ข้อมูลติดต่อดิบอาจมีอีเมลหลายรายการ |
ContactsContract.CommonDataKinds.StructuredPostal |
ที่อยู่ไปรษณีย์สำหรับข้อมูลติดต่อดิบที่เชื่อมโยงกับแถวข้อมูลนี้ | ข้อมูลติดต่อดิบสามารถมีที่อยู่ทางไปรษณีย์ได้หลายรายการ |
ContactsContract.CommonDataKinds.GroupMembership |
ตัวระบุที่ลิงก์ข้อมูลติดต่อดิบกับกลุ่มใดกลุ่มหนึ่งใน Contacts Provider | กลุ่มเป็นฟีเจอร์ที่ไม่บังคับของประเภทบัญชีและชื่อบัญชี ซึ่งจะอธิบายไว้อย่างละเอียดในส่วนกลุ่มรายชื่อติดต่อ |
รายชื่อติดต่อ
Contacts Provider จะรวมแถวข้อมูลติดต่อดิบในบัญชีทุกประเภทและชื่อบัญชีทั้งหมดเพื่อสร้างรายชื่อติดต่อ ซึ่งจะช่วยให้แสดงและแก้ไขข้อมูลทั้งหมดที่ผู้ใช้รวบรวมไว้สำหรับบุคคลหนึ่งๆ ได้ Contacts Provider จะจัดการการสร้างแถวรายชื่อติดต่อใหม่และการรวมข้อมูลติดต่อดิบกับแถวรายชื่อติดต่อที่มีอยู่ ทั้งแอปพลิเคชันและอะแดปเตอร์การซิงค์ไม่ได้รับอนุญาตให้เพิ่มรายชื่อติดต่อ และคอลัมน์บางคอลัมน์ในแถวรายชื่อติดต่อเป็นแบบอ่านอย่างเดียว
หมายเหตุ: หากพยายามเพิ่มรายชื่อติดต่อลงใน Contacts Provider ด้วย insert() คุณจะได้รับข้อยกเว้น UnsupportedOperationException หากคุณพยายามอัปเดตคอลัมน์ที่ระบุว่าเป็น "อ่านอย่างเดียว" ระบบจะไม่สนใจการอัปเดตนั้น
Contacts Provider จะสร้างรายชื่อติดต่อใหม่เพื่อตอบสนองต่อการเพิ่มข้อมูลติดต่อดิบใหม่ที่ไม่ตรงกับรายชื่อติดต่อที่มีอยู่ นอกจากนี้ ผู้ให้บริการจะดำเนินการนี้หากข้อมูลของข้อมูลติดต่อดิบที่มีอยู่เปลี่ยนแปลงไปในลักษณะที่ไม่ตรงกับรายชื่อติดต่อที่แนบไว้ก่อนหน้านี้อีกต่อไป หากแอปพลิเคชันหรืออะแดปเตอร์การซิงค์สร้างข้อมูลติดต่อดิบใหม่ที่ตรงกับรายชื่อติดต่อที่มีอยู่ ระบบจะรวมข้อมูลติดต่อดิบใหม่เข้ากับรายชื่อติดต่อที่มีอยู่
Contacts Provider จะลิงก์แถวรายชื่อติดต่อกับแถวข้อมูลติดต่อดิบด้วยคอลัมน์ _ID ของแถวรายชื่อติดต่อในตาราง Contacts คอลัมน์ CONTACT_ID ของตารางข้อมูลติดต่อดิบ ContactsContract.RawContacts มีค่า _ID สำหรับแถวรายชื่อติดต่อที่เชื่อมโยงกับแถวข้อมูลติดต่อดิบแต่ละแถว
ตาราง ContactsContract.Contacts ยังมีคอลัมน์ LOOKUP_KEY ซึ่งเป็นลิงก์ "ถาวร" ไปยังแถวรายชื่อติดต่อด้วย เนื่องจาก Contacts Provider จะดูแลรักษาข้อมูลรายชื่อติดต่อโดยอัตโนมัติ จึงอาจเปลี่ยนค่า _ID ของแถวรายชื่อติดต่อเพื่อตอบสนองต่อการรวมหรือการซิงค์ แม้ว่ากรณีนี้จะเกิดขึ้น URI เนื้อหา CONTENT_LOOKUP_URI ที่รวมกับ LOOKUP_KEY ของรายชื่อติดต่อจะยังคงชี้ไปยังแถวของรายชื่อติดต่อ คุณจึงใช้ LOOKUP_KEY เพื่อรักษาลิงก์ไปยังรายชื่อติดต่อ "ที่ชอบ" และอื่นๆ ได้ คอลัมน์นี้มีรูปแบบของตัวเองซึ่งไม่เกี่ยวข้องกับรูปแบบของคอลัมน์ _ID
รูปที่ 3 แสดงความสัมพันธ์ระหว่างตารางหลักทั้ง 3 ตาราง
รูปที่ 3 ความสัมพันธ์ของตารางรายชื่อติดต่อ ข้อมูลติดต่อดิบ และรายละเอียด
ข้อควรระวัง: หากคุณเผยแพร่แอปไปยัง Google Play Store หรือหากแอปอยู่ในอุปกรณ์ที่ใช้ Android 10 (ระดับ API 29) ขึ้นไป โปรดทราบว่าฟิลด์ข้อมูลและเมธอดของรายชื่อติดต่อบางชุดจะล้าสมัย
ภายใต้เงื่อนไขที่กล่าวถึง ระบบจะล้างค่าที่เขียนลงในฟิลด์ข้อมูลเหล่านี้เป็นระยะๆ ดังนี้
-
ContactsContract.ContactOptionsColumns.LAST_TIME_CONTACTED -
ContactsContract.ContactOptionsColumns.TIMES_CONTACTED -
ContactsContract.DataUsageStatColumns.LAST_TIME_USED -
ContactsContract.DataUsageStatColumns.TIMES_USED
นอกจากนี้ API ที่ใช้ตั้งค่าฟิลด์ข้อมูลข้างต้นก็ล้าสมัยแล้วเช่นกัน
นอกจากนี้ ช่องต่อไปนี้จะไม่แสดงรายชื่อติดต่อที่ติดต่อบ่อยอีกต่อไป โปรดทราบว่าช่องเหล่านี้บางช่องจะมีผลต่อการจัดอันดับรายชื่อติดต่อก็ต่อเมื่อรายชื่อติดต่อเป็นส่วนหนึ่งของประเภทข้อมูลที่เฉพาะเจาะจงเท่านั้น
-
ContactsContract.Contacts.CONTENT_FREQUENT_URI -
ContactsContract.Contacts.CONTENT_STREQUENT_URI -
ContactsContract.Contacts.CONTENT_STREQUENT_FILTER_URI -
CONTENT_FILTER_URI(มีผลกับข้อมูลประเภทอีเมล โทรศัพท์ โทรได้ และติดต่อได้เท่านั้น) -
ENTERPRISE_CONTENT_FILTER_URI(มีผลเฉพาะกับประเภทข้อมูลอีเมล โทรศัพท์ และโทรได้)
หากแอปของคุณเข้าถึงหรืออัปเดตช่องหรือ API เหล่านี้ ให้ใช้วิธีอื่น ตัวอย่างเช่น คุณสามารถดำเนินการตาม Use Case บางอย่างได้โดยใช้ Content Provider ส่วนตัวหรือข้อมูลอื่นๆ ที่จัดเก็บไว้ภายในแอปหรือระบบแบ็กเอนด์
หากต้องการยืนยันว่าฟังก์ชันการทำงานของแอปไม่ได้รับผลกระทบจากการเปลี่ยนแปลงนี้ คุณสามารถล้างฟิลด์ข้อมูลเหล่านี้ด้วยตนเองได้ โดยเรียกใช้คำสั่ง adb ต่อไปนี้ในอุปกรณ์ที่ใช้ Android 4.1 (ระดับ API 16) ขึ้นไป
adb shell content delete \ --uri content://com.android.contacts/contacts/delete_usage
ข้อมูลจากอะแดปเตอร์การซิงค์
ผู้ใช้ป้อนข้อมูลรายชื่อติดต่อลงในอุปกรณ์โดยตรง แต่ข้อมูลจะส่งไปยัง Contacts Provider จากบริการบนเว็บผ่านอะแดปเตอร์การซิงค์ด้วย ซึ่งจะโอนข้อมูลระหว่างอุปกรณ์และบริการโดยอัตโนมัติ อะแดปเตอร์การซิงค์จะทำงานในเบื้องหลังภายใต้การควบคุมของระบบ และจะเรียกใช้เมธอด ContentResolver เพื่อจัดการข้อมูล
ใน Android บริการเว็บที่อะแดปเตอร์การซิงค์ทำงานด้วยจะระบุตามประเภทบัญชี อะแดปเตอร์การซิงค์แต่ละรายการจะทำงานกับบัญชีประเภทเดียว แต่สามารถรองรับชื่อบัญชีหลายชื่อสำหรับประเภทนั้นได้ ประเภทบัญชีและชื่อบัญชีจะอธิบายไว้สั้นๆ ในส่วนแหล่งที่มาของข้อมูลรายชื่อติดต่อดิบ คำจำกัดความต่อไปนี้จะให้รายละเอียดเพิ่มเติม และอธิบายว่าประเภทและชื่อบัญชีเกี่ยวข้องกับอะแดปเตอร์การซิงค์และบริการอย่างไร
- ประเภทบัญชี
-
ระบุบริการที่ผู้ใช้จัดเก็บข้อมูลไว้ โดยส่วนใหญ่แล้ว ผู้ใช้จะต้องตรวจสอบสิทธิ์กับบริการ เช่น Google Contacts เป็นประเภทบัญชีที่ระบุด้วยรหัส
google.comค่านี้สอดคล้องกับประเภทบัญชีที่AccountManagerใช้ - ชื่อบัญชี
- ระบุบัญชีหรือการเข้าสู่ระบบที่เฉพาะเจาะจงสำหรับประเภทบัญชี บัญชี Google Contacts เป็นบัญชีเดียวกับบัญชี Google ซึ่งมีอีเมลเป็นชื่อบัญชี บริการอื่นๆ อาจใช้ชื่อผู้ใช้ที่เป็นคำเดียวหรือรหัสที่เป็นตัวเลข
ประเภทบัญชีไม่จำเป็นต้องไม่ซ้ำกัน ผู้ใช้สามารถกำหนดค่าบัญชี Google Contacts หลายบัญชีและดาวน์โหลดข้อมูลไปยัง Contacts Provider ได้ ซึ่งอาจเกิดขึ้นในกรณีที่ผู้ใช้มีรายชื่อติดต่อส่วนตัวชุดหนึ่งสำหรับชื่อบัญชีส่วนตัว และอีกชุดหนึ่งสำหรับงาน ชื่อบัญชีมักจะไม่ซ้ำกัน โดยทั้ง 2 อย่างนี้จะระบุโฟลว์ข้อมูลที่เฉพาะเจาะจงระหว่าง Contacts Provider กับบริการภายนอก
หากต้องการโอนข้อมูลของบริการไปยัง Contacts Provider คุณต้องเขียนอะแดปเตอร์การซิงค์ของคุณเอง ซึ่งอธิบายไว้อย่างละเอียดในส่วนอะแดปเตอร์การซิงค์ Contacts Provider
รูปที่ 4 แสดงให้เห็นว่า Contacts Provider เหมาะสมกับขั้นตอนการรับส่งข้อมูลเกี่ยวกับผู้คนอย่างไร ในช่องที่มีข้อความ "อะแดปเตอร์การซิงค์" อะแดปเตอร์แต่ละรายการจะมีป้ายกำกับตามประเภทบัญชี
รูปที่ 4 ขั้นตอนการรับส่งข้อมูลของ Contacts Provider
สิทธิ์ที่จำเป็น
แอปพลิเคชันที่ต้องการเข้าถึง Contacts Provider ต้องขอสิทธิ์ต่อไปนี้
- สิทธิ์การอ่านตารางอย่างน้อย 1 ตาราง
-
READ_CONTACTSซึ่งระบุไว้ในAndroidManifest.xmlโดยมีองค์ประกอบ<uses-permission>เป็น<uses-permission android:name="android.permission.READ_CONTACTS"> - สิทธิ์เขียนในตารางอย่างน้อย 1 ตาราง
-
WRITE_CONTACTSซึ่งระบุไว้ในAndroidManifest.xmlโดยมีองค์ประกอบ<uses-permission>เป็น<uses-permission android:name="android.permission.WRITE_CONTACTS">
สิทธิ์เหล่านี้ไม่ครอบคลุมถึงข้อมูลโปรไฟล์ผู้ใช้ เราจะพูดถึงโปรไฟล์ผู้ใช้และสิทธิ์ที่จำเป็นในส่วนถัดไป ซึ่งก็คือโปรไฟล์ผู้ใช้
โปรดทราบว่าข้อมูลรายชื่อติดต่อของผู้ใช้เป็นข้อมูลส่วนบุคคลและข้อมูลที่ละเอียดอ่อน ผู้ใช้กังวลเกี่ยวกับความเป็นส่วนตัว จึงไม่ต้องการให้แอปพลิเคชันรวบรวมข้อมูลเกี่ยวกับตนเองหรือรายชื่อติดต่อของตน หากไม่ทราบเหตุผลที่ชัดเจนว่าทำไมคุณจึงต้องขอสิทธิ์เข้าถึงข้อมูลรายชื่อติดต่อ ผู้ใช้อาจให้คะแนนแอปพลิเคชันของคุณต่ำหรือปฏิเสธที่จะติดตั้งแอป
โปรไฟล์ผู้ใช้
ตาราง ContactsContract.Contacts มีแถวเดียวซึ่งมีข้อมูลโปรไฟล์ของผู้ใช้อุปกรณ์ ข้อมูลนี้อธิบายuserของอุปกรณ์ ไม่ใช่รายชื่อติดต่อของผู้ใช้ แถวรายชื่อติดต่อในโปรไฟล์จะลิงก์กับแถวข้อมูลติดต่อดิบสำหรับแต่ละระบบที่ใช้โปรไฟล์
แถวข้อมูลติดต่อดิบของโปรไฟล์แต่ละแถวมีแถวข้อมูลได้หลายแถว ค่าคงที่สำหรับการเข้าถึงโปรไฟล์ผู้ใช้มีอยู่ในคลาส ContactsContract.Profile
การเข้าถึงโปรไฟล์ผู้ใช้ต้องใช้สิทธิ์พิเศษ นอกเหนือจากสิทธิ์ READ_CONTACTS และ WRITE_CONTACTS ที่จำเป็นสำหรับการอ่านและเขียนแล้ว การเข้าถึงโปรไฟล์ผู้ใช้ยังต้องใช้สิทธิ์ android.Manifest.permission#READ_PROFILE และ android.Manifest.permission#WRITE_PROFILE สำหรับสิทธิ์การเข้าถึงแบบอ่านและเขียนตามลำดับ
โปรดทราบว่าคุณควรพิจารณาว่าโปรไฟล์ของผู้ใช้เป็นข้อมูลที่ละเอียดอ่อน สิทธิ์ android.Manifest.permission#READ_PROFILE ช่วยให้คุณเข้าถึงข้อมูลส่วนบุคคลที่ระบุตัวบุคคลนั้นได้ของผู้ใช้อุปกรณ์ อย่าลืมแจ้งให้ผู้ใช้ทราบถึงเหตุผลที่คุณต้องการสิทธิ์เข้าถึงโปรไฟล์ผู้ใช้ในคำอธิบายของแอปพลิเคชัน
หากต้องการเรียกข้อมูลแถวรายชื่อติดต่อที่มีโปรไฟล์ของผู้ใช้ ให้เรียกใช้ ContentResolver.query() ตั้งค่า URI เนื้อหาเป็น CONTENT_URI และไม่ต้องระบุเกณฑ์การเลือกใดๆ นอกจากนี้ คุณยังใช้ URI เนื้อหานี้เป็น URI ฐานเพื่อดึงข้อมูลติดต่อดิบหรือข้อมูลสำหรับโปรไฟล์ได้ด้วย ตัวอย่างเช่น ข้อมูลโค้ดนี้จะดึงข้อมูลสำหรับโปรไฟล์
Kotlin
// Sets the columns to retrieve for the user profile projection = arrayOf( ContactsContract.Profile._ID, ContactsContract.Profile.DISPLAY_NAME_PRIMARY, ContactsContract.Profile.LOOKUP_KEY, ContactsContract.Profile.PHOTO_THUMBNAIL_URI ) // Retrieves the profile from the Contacts Provider profileCursor = contentResolver.query( ContactsContract.Profile.CONTENT_URI, projection, null, null, null )
Java
// Sets the columns to retrieve for the user profile projection = new String[] { Profile._ID, Profile.DISPLAY_NAME_PRIMARY, Profile.LOOKUP_KEY, Profile.PHOTO_THUMBNAIL_URI }; // Retrieves the profile from the Contacts Provider profileCursor = getContentResolver().query( Profile.CONTENT_URI, projection , null, null, null);
หมายเหตุ: หากคุณดึงข้อมูลแถวรายชื่อติดต่อหลายแถว และต้องการตรวจสอบว่าแถวใดแถวหนึ่งเป็นโปรไฟล์ผู้ใช้หรือไม่ ให้ทดสอบคอลัมน์ IS_USER_PROFILE ของแถว คอลัมน์นี้จะตั้งค่าเป็น "1" หากรายชื่อติดต่อเป็นโปรไฟล์ผู้ใช้
ข้อมูลเมตาของ Contacts Provider
Contacts Provider จะจัดการข้อมูลที่ติดตามสถานะของข้อมูลรายชื่อติดต่อในที่เก็บข้อมูล ข้อมูลเมตาเกี่ยวกับที่เก็บนี้จะจัดเก็บไว้ในที่ต่างๆ ซึ่งรวมถึงแถวตารางข้อมูลติดต่อดิบ ข้อมูล และรายชื่อติดต่อ ตาราง ContactsContract.Settings และตาราง ContactsContract.SyncState ตารางต่อไปนี้แสดงผลลัพธ์ของข้อมูลเมตาแต่ละรายการ
ตารางที่ 3 ข้อมูลเมตาใน Contacts Provider
| โต๊ะ | คอลัมน์ | ค่า | ความหมาย |
|---|---|---|---|
ContactsContract.RawContacts |
DIRTY |
"0" - ไม่มีการเปลี่ยนแปลงนับตั้งแต่การซิงค์ครั้งล่าสุด |
ทำเครื่องหมายข้อมูลติดต่อดิบที่มีการเปลี่ยนแปลงในอุปกรณ์และต้องซิงค์กลับไปยังเซิร์ฟเวอร์ Contacts Provider จะตั้งค่านี้โดยอัตโนมัติเมื่อแอปพลิเคชัน Android อัปเดตแถว
อะแดปเตอร์การซิงค์ที่แก้ไขข้อมูลติดต่อดิบหรือตารางข้อมูลควรเพิ่มสตริง |
| "1" - มีการเปลี่ยนแปลงตั้งแต่การซิงค์ครั้งล่าสุด และต้องซิงค์กลับไปยังเซิร์ฟเวอร์ | |||
ContactsContract.RawContacts |
VERSION |
หมายเลขเวอร์ชันของแถวนี้ | Contacts Provider จะเพิ่มค่านี้โดยอัตโนมัติเมื่อใดก็ตามที่แถวหรือข้อมูลที่เกี่ยวข้องมีการเปลี่ยนแปลง |
ContactsContract.Data |
DATA_VERSION |
หมายเลขเวอร์ชันของแถวนี้ | Contacts Provider จะเพิ่มค่านี้โดยอัตโนมัติเมื่อใดก็ตามที่มีการเปลี่ยนแปลงแถวข้อมูล |
ContactsContract.RawContacts |
SOURCE_ID |
ค่าสตริงที่ระบุข้อมูลติดต่อดิบนี้โดยไม่ซ้ำกันในบัญชีที่สร้างข้อมูลติดต่อดิบนี้ขึ้นมา |
เมื่ออะแดปเตอร์การซิงค์สร้างข้อมูลติดต่อดิบใหม่ คอลัมน์นี้ควรตั้งค่าเป็นรหัสที่ไม่ซ้ำกันของเซิร์ฟเวอร์สำหรับข้อมูลติดต่อดิบ เมื่อแอปพลิเคชัน Android สร้างข้อมูลติดต่อดิบใหม่ แอปพลิเคชันควรปล่อยคอลัมน์นี้ว่างไว้ ซึ่งจะส่งสัญญาณไปยังอะแดปเตอร์การซิงค์ว่าควรสร้างข้อมูลติดต่อดิบใหม่บนเซิร์ฟเวอร์ และรับค่าสำหรับ SOURCE_ID
โดยเฉพาะอย่างยิ่ง รหัสแหล่งที่มาต้องไม่ซ้ำกันสำหรับบัญชีแต่ละประเภทและควรคงที่ในการซิงค์แต่ละครั้ง
|
ContactsContract.Groups |
GROUP_VISIBLE |
"0" - รายชื่อติดต่อในกลุ่มนี้ไม่ควรแสดงใน UI ของแอปพลิเคชัน Android | คอลัมน์นี้มีไว้เพื่อความเข้ากันได้กับเซิร์ฟเวอร์ที่อนุญาตให้ผู้ใช้ซ่อนรายชื่อติดต่อในบางกลุ่มได้ |
| "1" - อนุญาตให้รายชื่อติดต่อในกลุ่มนี้ปรากฏใน UI ของแอปพลิเคชัน | |||
ContactsContract.Settings |
UNGROUPED_VISIBLE |
"0" - สำหรับบัญชีและประเภทบัญชีนี้ รายชื่อติดต่อที่ไม่ได้อยู่ในกลุ่มจะมองไม่เห็นใน UI ของแอปพลิเคชัน Android |
โดยค่าเริ่มต้น รายชื่อติดต่อจะมองไม่เห็นหากไม่มีข้อมูลติดต่อดิบอยู่ในกลุ่ม (การเป็นสมาชิกกลุ่มสำหรับข้อมูลติดต่อดิบจะระบุด้วยแถว ContactsContract.CommonDataKinds.GroupMembership อย่างน้อย 1 แถวในตาราง ContactsContract.Data)
การตั้งค่าแฟล็กนี้ในแถวตาราง ContactsContract.Settings สำหรับประเภทบัญชีและบัญชีจะบังคับให้รายชื่อติดต่อที่ไม่มีกลุ่มปรากฏขึ้น
การใช้แฟล็กนี้อย่างหนึ่งคือการแสดงรายชื่อติดต่อจากเซิร์ฟเวอร์ที่ไม่ได้ใช้กลุ่ม
|
| "1" - สำหรับบัญชีและประเภทบัญชีนี้ รายชื่อติดต่อที่ไม่ได้อยู่ในกลุ่มจะปรากฏใน UI ของแอปพลิเคชัน | |||
ContactsContract.SyncState |
(ทั้งหมด) | ใช้ตารางนี้เพื่อจัดเก็บข้อมูลเมตาสำหรับอะแดปเตอร์การซิงค์ | ตารางนี้ช่วยให้คุณจัดเก็บสถานะการซิงค์และข้อมูลอื่นๆ ที่เกี่ยวข้องกับการซิงค์ไว้ในอุปกรณ์ได้อย่างถาวร |
สิทธิ์เข้าถึง Contacts Provider
ส่วนนี้จะอธิบายหลักเกณฑ์ในการเข้าถึงข้อมูลจาก Contacts Provider โดยมุ่งเน้นที่สิ่งต่อไปนี้
- การค้นหาเอนทิตี
- การแก้ไขเป็นกลุ่ม
- การดึงข้อมูลและการแก้ไขด้วย Intent
- ความสมบูรณ์ของข้อมูล
การแก้ไขจากอะแดปเตอร์การซิงค์จะอธิบายไว้อย่างละเอียดในส่วนอะแดปเตอร์การซิงค์ Contacts Provider
กำลังค้นหาเอนทิตี
เนื่องจากตาราง Contacts Provider มีการจัดระเบียบตามลำดับชั้น การดึงข้อมูลแถวและแถว "ย่อย" ทั้งหมดที่ลิงก์กับแถวดังกล่าวจึงมักมีประโยชน์ ตัวอย่างเช่น หากต้องการแสดงข้อมูลทั้งหมดของบุคคล คุณอาจต้องดึงข้อมูลแถว ContactsContract.RawContacts ทั้งหมดสำหรับแถว ContactsContract.Contacts แถวเดียว หรือแถว ContactsContract.CommonDataKinds.Email ทั้งหมดสำหรับแถว ContactsContract.RawContacts แถวเดียว เพื่ออำนวยความสะดวกในเรื่องนี้ Contacts Provider จึงมีโครงสร้างเอนทิตี ซึ่งทำหน้าที่เหมือนการรวมฐานข้อมูลระหว่างตาราง
เอนทิตีเป็นเหมือนตารางที่ประกอบด้วยคอลัมน์ที่เลือกจากตารางหลักและตารางย่อย
เมื่อค้นหาเอนทิตี คุณจะต้องระบุการฉายภาพและเกณฑ์การค้นหาตามคอลัมน์ที่มีอยู่ในเอนทิตี ผลลัพธ์คือ Cursor ที่มี 1 แถวสำหรับแถวตารางย่อยแต่ละแถวที่ดึงข้อมูลมา ตัวอย่างเช่น หากคุณค้นหา ContactsContract.Contacts.Entity สำหรับชื่อผู้ติดต่อและแถว ContactsContract.CommonDataKinds.Email ทั้งหมดสำหรับข้อมูลติดต่อดิบทั้งหมดสำหรับชื่อนั้น คุณจะได้รับ Cursor ที่มี 1 แถวสำหรับแถว ContactsContract.CommonDataKinds.Email แต่ละแถว
เอนทิตีช่วยให้การค้นหาง่ายขึ้น เมื่อใช้เอนทิตี คุณจะดึงข้อมูลรายชื่อติดต่อทั้งหมดสำหรับรายชื่อติดต่อหรือข้อมูลติดต่อดิบได้พร้อมกัน แทนที่จะต้องค้นหาตารางหลักก่อนเพื่อรับรหัส แล้วจึงค้นหาตารางย่อยด้วยรหัสนั้น นอกจากนี้ Contacts Provider ยังประมวลผลการค้นหาเอนทิตีในธุรกรรมเดียว ซึ่งช่วยให้มั่นใจว่าข้อมูลที่ดึงมามีความสอดคล้องกันภายใน
หมายเหตุ: โดยปกติแล้ว เอนทิตีจะไม่มีคอลัมน์ทั้งหมดของตารางหลักและตารางย่อย หากพยายามใช้ชื่อคอลัมน์ที่ไม่ได้อยู่ในรายการค่าคงที่ของชื่อคอลัมน์สำหรับเอนทิตี คุณจะได้รับ Exception
ข้อมูลโค้ดต่อไปนี้แสดงวิธีดึงข้อมูลแถวข้อมูลติดต่อดิบทั้งหมดสำหรับรายชื่อติดต่อ ข้อมูลโค้ดนี้เป็นส่วนหนึ่งของแอปพลิเคชันขนาดใหญ่ที่มีกิจกรรม 2 อย่าง ได้แก่ "หลัก" และ "รายละเอียด" กิจกรรมหลักจะแสดงรายการแถวรายชื่อติดต่อ เมื่อผู้ใช้เลือกแถวใดแถวหนึ่ง กิจกรรมจะส่งรหัสของแถวนั้นไปยังกิจกรรมรายละเอียด กิจกรรมรายละเอียดจะใช้ ContactsContract.Contacts.Entity เพื่อแสดงแถวข้อมูลทั้งหมดจากข้อมูลติดต่อดิบทั้งหมดที่เชื่อมโยงกับรายชื่อติดต่อที่เลือก
ตัวอย่างโค้ดนี้มาจากกิจกรรม "detail"
Kotlin
... /* * Appends the entity path to the URI. In the case of the Contacts Provider, the * expected URI is content://com.google.contacts/#/entity (# is the ID value). */ contactUri = Uri.withAppendedPath( contactUri, ContactsContract.Contacts.Entity.CONTENT_DIRECTORY ) // Initializes the loader identified by LOADER_ID. loaderManager.initLoader( LOADER_ID, // The identifier of the loader to initialize null, // Arguments for the loader (in this case, none) this // The context of the activity ) // Creates a new cursor adapter to attach to the list view cursorAdapter = SimpleCursorAdapter( this, // the context of the activity R.layout.detail_list_item, // the view item containing the detail widgets mCursor, // the backing cursor fromColumns, // the columns in the cursor that provide the data toViews, // the views in the view item that display the data 0) // flags // Sets the ListView's backing adapter. rawContactList.adapter = cursorAdapter ... override fun onCreateLoader(id: Int, args: Bundle?): Loader<Cursor> { /* * Sets the columns to retrieve. * RAW_CONTACT_ID is included to identify the raw contact associated with the data row. * DATA1 contains the first column in the data row (usually the most important one). * MIMETYPE indicates the type of data in the data row. */ val projection: Array<String> = arrayOf( ContactsContract.Contacts.Entity.RAW_CONTACT_ID, ContactsContract.Contacts.Entity.DATA1, ContactsContract.Contacts.Entity.MIMETYPE ) /* * Sorts the retrieved cursor by raw contact id, to keep all data rows for a single raw * contact collated together. */ val sortOrder = "${ContactsContract.Contacts.Entity.RAW_CONTACT_ID} ASC" /* * Returns a new CursorLoader. The arguments are similar to * ContentResolver.query(), except for the Context argument, which supplies the location of * the ContentResolver to use. */ return CursorLoader( applicationContext, // The activity's context contactUri, // The entity content URI for a single contact projection, // The columns to retrieve null, // Retrieve all the raw contacts and their data rows. null, // sortOrder // Sort by the raw contact ID. ) }
Java
... /* * Appends the entity path to the URI. In the case of the Contacts Provider, the * expected URI is content://com.google.contacts/#/entity (# is the ID value). */ contactUri = Uri.withAppendedPath( contactUri, ContactsContract.Contacts.Entity.CONTENT_DIRECTORY); // Initializes the loader identified by LOADER_ID. getLoaderManager().initLoader( LOADER_ID, // The identifier of the loader to initialize null, // Arguments for the loader (in this case, none) this); // The context of the activity // Creates a new cursor adapter to attach to the list view cursorAdapter = new SimpleCursorAdapter( this, // the context of the activity R.layout.detail_list_item, // the view item containing the detail widgets mCursor, // the backing cursor fromColumns, // the columns in the cursor that provide the data toViews, // the views in the view item that display the data 0); // flags // Sets the ListView's backing adapter. rawContactList.setAdapter(cursorAdapter); ... @Override public Loader<Cursor> onCreateLoader(int id, Bundle args) { /* * Sets the columns to retrieve. * RAW_CONTACT_ID is included to identify the raw contact associated with the data row. * DATA1 contains the first column in the data row (usually the most important one). * MIMETYPE indicates the type of data in the data row. */ String[] projection = { ContactsContract.Contacts.Entity.RAW_CONTACT_ID, ContactsContract.Contacts.Entity.DATA1, ContactsContract.Contacts.Entity.MIMETYPE }; /* * Sorts the retrieved cursor by raw contact id, to keep all data rows for a single raw * contact collated together. */ String sortOrder = ContactsContract.Contacts.Entity.RAW_CONTACT_ID + " ASC"; /* * Returns a new CursorLoader. The arguments are similar to * ContentResolver.query(), except for the Context argument, which supplies the location of * the ContentResolver to use. */ return new CursorLoader( getApplicationContext(), // The activity's context contactUri, // The entity content URI for a single contact projection, // The columns to retrieve null, // Retrieve all the raw contacts and their data rows. null, // sortOrder); // Sort by the raw contact ID. }
เมื่อโหลดเสร็จแล้ว LoaderManager จะเรียกใช้ Callback ไปยัง onLoadFinished() อาร์กิวเมนต์ขาเข้าอย่างหนึ่งของเมธอดนี้คือ Cursor ที่มีผลลัพธ์ของคำค้นหา ในแอปของคุณเอง คุณสามารถรับข้อมูลจาก Cursor นี้เพื่อแสดงหรือทำงานกับข้อมูลดังกล่าวต่อไปได้
การแก้ไขเป็นกลุ่ม
คุณควรแทรก อัปเดต และลบข้อมูลใน Contacts Provider ใน "โหมดกลุ่ม" ทุกครั้งที่ทำได้ โดยสร้าง ArrayList ของออบเจ็กต์ ContentProviderOperation แล้วเรียกใช้ applyBatch() เนื่องจาก Contacts Provider จะดำเนินการทั้งหมดใน applyBatch() ในธุรกรรมเดียว การแก้ไขของคุณจึงจะไม่ทำให้ที่เก็บรายชื่อติดต่ออยู่ในสถานะที่ไม่สอดคล้องกัน การแก้ไขแบบกลุ่มยังช่วยให้แทรกข้อมูลติดต่อดิบและข้อมูลรายละเอียดพร้อมกันได้ด้วย
หมายเหตุ: หากต้องการแก้ไขข้อมูลติดต่อดิบรายการเดียว ให้พิจารณาส่ง Intent ไปยังแอปพลิเคชันรายชื่อผู้ติดต่อของอุปกรณ์แทนที่จะจัดการการแก้ไขในแอปของคุณ ดูรายละเอียดเพิ่มเติมเกี่ยวกับการดำเนินการนี้ได้ในส่วนการดึงข้อมูลและการแก้ไขด้วย Intent
จุดหยุดชั่วคราว
การแก้ไขแบบกลุ่มที่มีการดำเนินการจำนวนมากอาจบล็อกกระบวนการอื่นๆ ซึ่งส่งผลให้ผู้ใช้ได้รับประสบการณ์การใช้งานโดยรวมที่ไม่ดี หากต้องการจัดระเบียบการแก้ไขทั้งหมดที่ต้องการดำเนินการในรายการแยกต่างหากให้น้อยที่สุดเท่าที่จะเป็นไปได้ และในขณะเดียวกันก็ป้องกันไม่ให้การแก้ไขเหล่านั้นบล็อกระบบ คุณควรตั้งค่าจุดหยุดชั่วคราวสำหรับการดำเนินการอย่างน้อย 1 รายการ
จุดหยุดชั่วคราวคือออบเจ็กต์ ContentProviderOperation ที่มีค่า isYieldAllowed() ตั้งค่าเป็น true เมื่อ Contacts Provider พบจุดหยุดชั่วคราว ระบบจะหยุดการทำงานชั่วคราวเพื่อให้กระบวนการอื่นๆ ทำงานและปิดธุรกรรมปัจจุบัน เมื่อผู้ให้บริการเริ่มทำงานอีกครั้ง ระบบจะดำเนินการต่อด้วยการดำเนินการถัดไปใน ArrayList และเริ่มธุรกรรมใหม่
จุดหยุดชั่วคราวส่งผลให้เกิดธุรกรรมมากกว่า 1 รายการต่อการเรียก applyBatch() ด้วยเหตุนี้ คุณจึงควรตั้งค่าจุดหยุดชั่วคราวสำหรับการดำเนินการล่าสุดสำหรับชุดแถวที่เกี่ยวข้อง
เช่น คุณควรตั้งค่าจุดหยุดชั่วคราวสำหรับการดำเนินการสุดท้ายในชุดที่เพิ่มแถวข้อมูลติดต่อดิบและแถวข้อมูลที่เกี่ยวข้อง หรือการดำเนินการสุดท้ายสำหรับชุดแถวที่เกี่ยวข้องกับรายชื่อติดต่อเดียว
จุดหยุดชั่วคราวยังเป็นหน่วยของการดำเนินการแบบอะตอมมิกด้วย การเข้าถึงทั้งหมดระหว่างจุดหยุดชั่วคราว 2 จุดจะสำเร็จหรือล้มเหลวเป็นหน่วยเดียว หากคุณไม่ได้ตั้งค่าจุดหยุดชั่วคราว การดำเนินการแบบอะตอมที่เล็กที่สุดคือการดำเนินการทั้งชุด หากใช้จุดหยุดชั่วคราว คุณจะป้องกันไม่ให้การดำเนินการลดประสิทธิภาพของระบบ ขณะเดียวกันก็มั่นใจได้ว่าชุดย่อยของการดำเนินการเป็นแบบอะตอมมิก
การอ้างอิงกลับของการแก้ไข
เมื่อแทรกแถวข้อมูลติดต่อดิบใหม่และแถวข้อมูลที่เกี่ยวข้องเป็นชุดออบเจ็กต์ ContentProviderOperation คุณต้องลิงก์แถวข้อมูลกับแถวข้อมูลติดต่อดิบโดยแทรกค่า _ID ของข้อมูลติดต่อดิบเป็นค่า RAW_CONTACT_ID อย่างไรก็ตาม ค่านี้จะไม่พร้อมใช้งานเมื่อคุณสร้าง ContentProviderOperation สำหรับแถวข้อมูล เนื่องจากคุณยังไม่ได้ใช้ ContentProviderOperation สำหรับแถวข้อมูลติดต่อดิบ เพื่อหลีกเลี่ยงปัญหานี้ คลาส ContentProviderOperation.Builder จึงมีเมธอด withValueBackReference()
วิธีนี้ช่วยให้คุณแทรกหรือแก้ไขคอลัมน์ด้วยผลลัพธ์ของการดำเนินการก่อนหน้าได้
เมธอด withValueBackReference() มีอาร์กิวเมนต์ 2 รายการ ดังนี้
-
key - คีย์ของคู่คีย์-ค่า ค่าของอาร์กิวเมนต์นี้ควรเป็นชื่อคอลัมน์ในตารางที่คุณกำลังแก้ไข
-
previousResult -
ดัชนีแบบ 0 ของค่าในอาร์เรย์ของออบเจ็กต์
ContentProviderResultจากapplyBatch()เมื่อใช้การดำเนินการแบบกลุ่ม ผลลัพธ์ของการดำเนินการแต่ละรายการจะจัดเก็บไว้ในอาร์เรย์ผลลัพธ์ระดับกลาง ค่าpreviousResultคือดัชนี ของผลลัพธ์รายการใดรายการหนึ่งเหล่านี้ ซึ่งจะดึงข้อมูลและจัดเก็บไว้พร้อมกับค่าkeyซึ่งจะช่วยให้คุณแทรกระเบียนข้อมูลติดต่อดิบใหม่และรับค่า_IDกลับมา จากนั้นจึงสร้าง "การอ้างอิงกลับ" ไปยังค่าดังกล่าวเมื่อเพิ่มแถวContactsContract.Dataระบบจะสร้างอาร์เรย์ผลลัพธ์ทั้งหมดเมื่อคุณเรียกใช้
applyBatch()เป็นครั้งแรก โดยมีขนาดเท่ากับขนาดของArrayListของออบเจ็กต์ContentProviderOperationที่คุณระบุ อย่างไรก็ตาม องค์ประกอบทั้งหมดในอาร์เรย์ผลลัพธ์จะตั้งค่าเป็นnullและหากคุณพยายามอ้างอิงกลับไปยังผลลัพธ์สำหรับการดำเนินการที่ยังไม่ได้ใช้withValueBackReference()จะแสดงException
ข้อมูลโค้ดต่อไปนี้แสดงวิธีแทรกข้อมูลติดต่อดิบและข้อมูลใหม่ในกลุ่ม โดยมีโค้ดที่สร้างจุดหยุดชั่วคราวและใช้การอ้างอิงย้อนกลับ
ข้อมูลโค้ดแรกจะดึงข้อมูลรายชื่อติดต่อจาก UI ณ จุดนี้ ผู้ใช้ได้เลือกบัญชีที่ควรเพิ่มข้อมูลติดต่อดิบใหม่แล้ว
Kotlin
// Creates a contact entry from the current UI values, using the currently-selected account. private fun createContactEntry() { /* * Gets values from the UI */ val name = contactNameEditText.text.toString() val phone = contactPhoneEditText.text.toString() val email = contactEmailEditText.text.toString() val phoneType: String = contactPhoneTypes[mContactPhoneTypeSpinner.selectedItemPosition] val emailType: String = contactEmailTypes[mContactEmailTypeSpinner.selectedItemPosition]
Java
// Creates a contact entry from the current UI values, using the currently-selected account. protected void createContactEntry() { /* * Gets values from the UI */ String name = contactNameEditText.getText().toString(); String phone = contactPhoneEditText.getText().toString(); String email = contactEmailEditText.getText().toString(); int phoneType = contactPhoneTypes.get( contactPhoneTypeSpinner.getSelectedItemPosition()); int emailType = contactEmailTypes.get( contactEmailTypeSpinner.getSelectedItemPosition());
ข้อมูลโค้ดถัดไปจะสร้างการดำเนินการเพื่อแทรกแถวข้อมูลติดต่อดิบลงในตาราง ContactsContract.RawContacts ดังนี้
Kotlin
/* * Prepares the batch operation for inserting a new raw contact and its data. Even if * the Contacts Provider does not have any data for this person, you can't add a Contact, * only a raw contact. The Contacts Provider will then add a Contact automatically. */ // Creates a new array of ContentProviderOperation objects. val ops = arrayListOf<ContentProviderOperation>() /* * Creates a new raw contact with its account type (server type) and account name * (user's account). Remember that the display name is not stored in this row, but in a * StructuredName data row. No other data is required. */ var op: ContentProviderOperation.Builder = ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI) .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, selectedAccount.name) .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, selectedAccount.type) // Builds the operation and adds it to the array of operations ops.add(op.build())
Java
/* * Prepares the batch operation for inserting a new raw contact and its data. Even if * the Contacts Provider does not have any data for this person, you can't add a Contact, * only a raw contact. The Contacts Provider will then add a Contact automatically. */ // Creates a new array of ContentProviderOperation objects. ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>(); /* * Creates a new raw contact with its account type (server type) and account name * (user's account). Remember that the display name is not stored in this row, but in a * StructuredName data row. No other data is required. */ ContentProviderOperation.Builder op = ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI) .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, selectedAccount.getType()) .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, selectedAccount.getName()); // Builds the operation and adds it to the array of operations ops.add(op.build());
จากนั้นโค้ดจะสร้างแถวข้อมูลสำหรับแถวชื่อที่แสดง โทรศัพท์ และอีเมล
ออบเจ็กต์ OperationBuilder แต่ละรายการจะใช้
withValueBackReference()
เพื่อรับ
RAW_CONTACT_ID การอ้างอิงจะชี้กลับไปที่ออบเจ็กต์ ContentProviderResult จากการดำเนินการแรก ซึ่งจะเพิ่มแถวข้อมูลติดต่อดิบและแสดงผลค่า _ID ใหม่ ด้วยเหตุนี้ ข้อมูลแต่ละแถวจึงลิงก์โดยอัตโนมัติด้วย RAW_CONTACT_ID ไปยังแถว ContactsContract.RawContacts ใหม่ที่เกี่ยวข้อง
ระบบจะแจ้งว่าออบเจ็กต์ ContentProviderOperation.Builder ที่เพิ่มแถวอีเมลมี withYieldAllowed() ซึ่งจะกำหนดจุดหยุดชั่วคราว
Kotlin
// Creates the display name for the new raw contact, as a StructuredName data row. op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) /* * withValueBackReference sets the value of the first argument to the value of * the ContentProviderResult indexed by the second argument. In this particular * call, the raw contact ID column of the StructuredName data row is set to the * value of the result returned by the first operation, which is the one that * actually adds the raw contact row. */ .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) // Sets the data row's MIME type to StructuredName .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) // Sets the data row's display name to the name in the UI. .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, name) // Builds the operation and adds it to the array of operations ops.add(op.build()) // Inserts the specified phone number and type as a Phone data row op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) /* * Sets the value of the raw contact id column to the new raw contact ID returned * by the first operation in the batch. */ .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) // Sets the data row's MIME type to Phone .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE) // Sets the phone number and type .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, phone) .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, phoneType) // Builds the operation and adds it to the array of operations ops.add(op.build()) // Inserts the specified email and type as a Phone data row op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) /* * Sets the value of the raw contact id column to the new raw contact ID returned * by the first operation in the batch. */ .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) // Sets the data row's MIME type to Email .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE) // Sets the email address and type .withValue(ContactsContract.CommonDataKinds.Email.ADDRESS, email) .withValue(ContactsContract.CommonDataKinds.Email.TYPE, emailType) /* * Demonstrates a yield point. At the end of this insert, the batch operation's thread * will yield priority to other threads. Use after every set of operations that affect a * single contact, to avoid degrading performance. */ op.withYieldAllowed(true) // Builds the operation and adds it to the array of operations ops.add(op.build())
Java
// Creates the display name for the new raw contact, as a StructuredName data row. op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) /* * withValueBackReference sets the value of the first argument to the value of * the ContentProviderResult indexed by the second argument. In this particular * call, the raw contact ID column of the StructuredName data row is set to the * value of the result returned by the first operation, which is the one that * actually adds the raw contact row. */ .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) // Sets the data row's MIME type to StructuredName .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) // Sets the data row's display name to the name in the UI. .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, name); // Builds the operation and adds it to the array of operations ops.add(op.build()); // Inserts the specified phone number and type as a Phone data row op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) /* * Sets the value of the raw contact id column to the new raw contact ID returned * by the first operation in the batch. */ .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) // Sets the data row's MIME type to Phone .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE) // Sets the phone number and type .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, phone) .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, phoneType); // Builds the operation and adds it to the array of operations ops.add(op.build()); // Inserts the specified email and type as a Phone data row op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) /* * Sets the value of the raw contact id column to the new raw contact ID returned * by the first operation in the batch. */ .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) // Sets the data row's MIME type to Email .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE) // Sets the email address and type .withValue(ContactsContract.CommonDataKinds.Email.ADDRESS, email) .withValue(ContactsContract.CommonDataKinds.Email.TYPE, emailType); /* * Demonstrates a yield point. At the end of this insert, the batch operation's thread * will yield priority to other threads. Use after every set of operations that affect a * single contact, to avoid degrading performance. */ op.withYieldAllowed(true); // Builds the operation and adds it to the array of operations ops.add(op.build());
ข้อมูลโค้ดสุดท้ายแสดงการเรียกใช้ applyBatch() ที่แทรกข้อมูลติดต่อดิบและแถวข้อมูลใหม่
Kotlin
// Ask the Contacts Provider to create a new contact Log.d(TAG, "Selected account: ${mSelectedAccount.name} (${mSelectedAccount.type})") Log.d(TAG, "Creating contact: $name") /* * Applies the array of ContentProviderOperation objects in batch. The results are * discarded. */ try { contentResolver.applyBatch(ContactsContract.AUTHORITY, ops) } catch (e: Exception) { // Display a warning val txt: String = getString(R.string.contactCreationFailure) Toast.makeText(applicationContext, txt, Toast.LENGTH_SHORT).show() // Log exception Log.e(TAG, "Exception encountered while inserting contact: $e") } }
Java
// Ask the Contacts Provider to create a new contact Log.d(TAG,"Selected account: " + selectedAccount.getName() + " (" + selectedAccount.getType() + ")"); Log.d(TAG,"Creating contact: " + name); /* * Applies the array of ContentProviderOperation objects in batch. The results are * discarded. */ try { getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops); } catch (Exception e) { // Display a warning Context ctx = getApplicationContext(); CharSequence txt = getString(R.string.contactCreationFailure); int duration = Toast.LENGTH_SHORT; Toast toast = Toast.makeText(ctx, txt, duration); toast.show(); // Log exception Log.e(TAG, "Exception encountered while inserting contact: " + e); } }
การดำเนินการแบบกลุ่มยังช่วยให้คุณใช้การควบคุมการเกิดขึ้นพร้อมกันในเชิงบวก ซึ่งเป็นวิธีการใช้ธุรกรรมการแก้ไขโดยไม่ต้องล็อกที่เก็บข้อมูลที่เกี่ยวข้อง หากต้องการใช้วิธีนี้ คุณต้องใช้ธุรกรรมแล้วตรวจสอบการแก้ไขอื่นๆ ที่อาจทำพร้อมกัน หากพบว่ามีการแก้ไขที่ไม่สอดคล้องกัน คุณจะย้อนกลับธุรกรรมและลองอีกครั้ง
การควบคุมการเกิดขึ้นพร้อมกันในแง่ดีมีประโยชน์สำหรับอุปกรณ์เคลื่อนที่ซึ่งมีผู้ใช้เพียงคนเดียวในแต่ละครั้ง และการเข้าถึงที่เก็บข้อมูลพร้อมกันนั้นเกิดขึ้นไม่บ่อยนัก เนื่องจากไม่มีการใช้การล็อก จึงไม่ต้องเสียเวลาตั้งค่าการล็อกหรือรอให้ธุรกรรมอื่นๆ ปลดล็อก
หากต้องการใช้การควบคุมการเกิดขึ้นพร้อมกันในแง่ดีขณะอัปเดตแถว ContactsContract.RawContacts แถวเดียว ให้ทำตามขั้นตอนต่อไปนี้
-
ดึงข้อมูลคอลัมน์
VERSIONของข้อมูลติดต่อดิบพร้อมกับข้อมูลอื่นๆ ที่คุณดึงข้อมูล -
สร้างออบเจ็กต์
ContentProviderOperation.Builderที่เหมาะสำหรับการบังคับใช้ข้อจำกัดโดยใช้เมธอดnewAssertQuery(Uri)สำหรับ URI เนื้อหา ให้ใช้RawContacts.CONTENT_URIโดยต่อท้ายด้วย_IDของข้อมูลติดต่อดิบ -
สำหรับออบเจ็กต์
ContentProviderOperation.Builderให้เรียกใช้withValue()เพื่อเปรียบเทียบคอลัมน์VERSIONกับหมายเลขเวอร์ชันที่คุณเพิ่งดึงข้อมูลมา -
สำหรับ
ContentProviderOperation.Builderเดียวกัน ให้เรียกใช้withExpectedCount()เพื่อให้แน่ใจว่าการยืนยันนี้จะทดสอบแถวเดียวเท่านั้น -
เรียกใช้
build()เพื่อสร้างออบเจ็กต์ContentProviderOperationจากนั้นเพิ่มออบเจ็กต์นี้เป็นออบเจ็กต์แรกในArrayListที่คุณส่งไปยังapplyBatch() - ใช้ธุรกรรมแบบกลุ่ม
หากแถวข้อมูลติดต่อดิบได้รับการอัปเดตโดยการดำเนินการอื่นระหว่างเวลาที่คุณอ่านแถวและเวลาที่คุณพยายามแก้ไข "assert" ContentProviderOperation จะไม่สำเร็จ และการดำเนินการทั้งชุดจะถูกยกเลิก จากนั้นคุณสามารถเลือกที่จะลองใช้กลุ่มนั้นอีกครั้งหรือดำเนินการอื่นๆ
ข้อมูลโค้ดต่อไปนี้แสดงวิธีสร้าง "assert"
ContentProviderOperation หลังจากค้นหาข้อมูลติดต่อดิบรายการเดียวโดยใช้
CursorLoader:
Kotlin
/* * The application uses CursorLoader to query the raw contacts table. The system calls this method * when the load is finished. */ override fun onLoadFinished(loader: Loader<Cursor>, cursor: Cursor) { // Gets the raw contact's _ID and VERSION values rawContactID = cursor.getLong(cursor.getColumnIndex(BaseColumns._ID)) mVersion = cursor.getInt(cursor.getColumnIndex(SyncColumns.VERSION)) } ... // Sets up a Uri for the assert operation val rawContactUri: Uri = ContentUris.withAppendedId( ContactsContract.RawContacts.CONTENT_URI, rawContactID ) // Creates a builder for the assert operation val assertOp: ContentProviderOperation.Builder = ContentProviderOperation.newAssertQuery(rawContactUri).apply { // Adds the assertions to the assert operation: checks the version withValue(SyncColumns.VERSION, mVersion) // and count of rows tested withExpectedCount(1) } // Creates an ArrayList to hold the ContentProviderOperation objects val ops = arrayListOf<ContentProviderOperation>() ops.add(assertOp.build()) // You would add the rest of your batch operations to "ops" here ... // Applies the batch. If the assert fails, an Exception is thrown try { val results: Array<ContentProviderResult> = contentResolver.applyBatch(AUTHORITY, ops) } catch (e: OperationApplicationException) { // Actions you want to take if the assert operation fails go here }
Java
/* * The application uses CursorLoader to query the raw contacts table. The system calls this method * when the load is finished. */ public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) { // Gets the raw contact's _ID and VERSION values rawContactID = cursor.getLong(cursor.getColumnIndex(BaseColumns._ID)); mVersion = cursor.getInt(cursor.getColumnIndex(SyncColumns.VERSION)); } ... // Sets up a Uri for the assert operation Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactID); // Creates a builder for the assert operation ContentProviderOperation.Builder assertOp = ContentProviderOperation.newAssertQuery(rawContactUri); // Adds the assertions to the assert operation: checks the version and count of rows tested assertOp.withValue(SyncColumns.VERSION, mVersion); assertOp.withExpectedCount(1); // Creates an ArrayList to hold the ContentProviderOperation objects ArrayList ops = new ArrayList<ContentProviderOperation>; ops.add(assertOp.build()); // You would add the rest of your batch operations to "ops" here ... // Applies the batch. If the assert fails, an Exception is thrown try { ContentProviderResult[] results = getContentResolver().applyBatch(AUTHORITY, ops); } catch (OperationApplicationException e) { // Actions you want to take if the assert operation fails go here }
การดึงข้อมูลและการแก้ไขด้วย Intent
การส่ง Intent ไปยังแอปพลิเคชันรายชื่อผู้ติดต่อของอุปกรณ์ช่วยให้คุณเข้าถึง Contacts Provider โดยอ้อมได้ Intent จะเริ่ม UI ของแอปพลิเคชันรายชื่อผู้ติดต่อของอุปกรณ์ ซึ่งผู้ใช้สามารถทำงานที่เกี่ยวข้องกับรายชื่อผู้ติดต่อได้ เมื่อมีสิทธิ์เข้าถึงประเภทนี้ ผู้ใช้จะทำสิ่งต่อไปนี้ได้
- เลือกรายชื่อติดต่อจากรายการและส่งกลับไปยังแอปเพื่อดำเนินการต่อ
- แก้ไขข้อมูลของรายชื่อติดต่อที่มีอยู่
- แทรกข้อมูลติดต่อดิบใหม่สำหรับบัญชีใดก็ได้
- ลบข้อมูลรายชื่อติดต่ออย่างน้อย 1 รายการ
หากผู้ใช้กำลังแทรกหรืออัปเดตข้อมูล คุณสามารถรวบรวมข้อมูลก่อนแล้วส่งเป็นส่วนหนึ่งของ Intent
เมื่อใช้ Intent เพื่อเข้าถึง Contacts Provider ผ่านแอปพลิเคชันรายชื่อผู้ติดต่อของอุปกรณ์ คุณไม่จำเป็นต้องเขียน UI หรือโค้ดของคุณเองเพื่อเข้าถึงผู้ให้บริการ นอกจากนี้ คุณยังไม่ต้องขอสิทธิ์ในการอ่านหรือเขียนไปยังผู้ให้บริการด้วย แอปพลิเคชันรายชื่อผู้ติดต่อของอุปกรณ์สามารถมอบสิทธิ์อ่านรายชื่อผู้ติดต่อให้คุณได้ และเนื่องจากคุณกำลังทำการแก้ไขผู้ให้บริการผ่านแอปพลิเคชันอื่น คุณจึงไม่จำเป็นต้องมีสิทธิ์เขียน
กระบวนการทั่วไปในการส่ง Intent เพื่อเข้าถึงผู้ให้บริการมีอธิบายไว้อย่างละเอียดในคู่มือ
พื้นฐานของ Content Provider ในส่วน "การเข้าถึงข้อมูลผ่าน Intent" การดำเนินการ ประเภท MIME และค่าข้อมูลที่คุณใช้สำหรับงานที่พร้อมใช้งานจะสรุปไว้ในตารางที่ 4 ส่วนค่าเพิ่มเติมที่คุณใช้กับ putExtra() ได้จะแสดงอยู่ในเอกสารอ้างอิงสำหรับ ContactsContract.Intents.Insert ดังนี้
ตารางที่ 4 Intent ของ Contacts Provider
| งาน | การทำงาน | ข้อมูล | ประเภท MIME | หมายเหตุ |
|---|---|---|---|---|
| เลือกรายชื่อติดต่อจากรายการ | ACTION_PICK |
หนึ่งใน
|
ไม่ได้ใช้ |
แสดงรายการข้อมูลติดต่อดิบหรือรายการข้อมูลจากข้อมูลติดต่อดิบ โดยขึ้นอยู่กับประเภท URI เนื้อหาที่คุณระบุ
เรียกใช้
|
| แทรกข้อมูลติดต่อดิบใหม่ | Insert.ACTION |
ไม่มี |
RawContacts.CONTENT_TYPE, ประเภท MIME สำหรับชุดข้อมูลติดต่อดิบ
|
แสดงหน้าจอเพิ่มรายชื่อติดต่อของแอปพลิเคชันรายชื่อผู้ติดต่อของอุปกรณ์ ค่าเพิ่มเติมที่คุณเพิ่มลงใน Intent จะแสดงขึ้น หากส่งด้วย startActivityForResult() ระบบจะส่ง URI เนื้อหาของข้อมูลติดต่อดิบที่เพิ่มใหม่กลับไปยังเมธอด Callback onActivityResult() ของกิจกรรมในอาร์กิวเมนต์ Intent ในช่อง "data" หากต้องการรับค่า ให้เรียกใช้ getData()
|
| แก้ไขรายชื่อติดต่อ | ACTION_EDIT |
CONTENT_LOOKUP_URIสำหรับรายชื่อติดต่อ กิจกรรมของเอดิเตอร์จะช่วยให้ผู้ใช้แก้ไขข้อมูลที่เชื่อมโยงกับรายชื่อติดต่อนี้ได้
|
Contacts.CONTENT_ITEM_TYPE รายชื่อติดต่อเดียว |
แสดงหน้าจอแก้ไขรายชื่อติดต่อในแอปพลิเคชันรายชื่อผู้ติดต่อ ค่าเพิ่มเติมที่คุณเพิ่มลงใน Intent จะแสดงขึ้น เมื่อผู้ใช้คลิกเสร็จสิ้นเพื่อบันทึกการแก้ไข กิจกรรมของคุณจะกลับมาที่เบื้องหน้า |
| แสดงเครื่องมือเลือกที่เพิ่มข้อมูลได้ด้วย | ACTION_INSERT_OR_EDIT |
ไม่มี |
CONTENT_ITEM_TYPE
|
Intent นี้จะแสดงหน้าจอเครื่องมือเลือกของแอปรายชื่อติดต่อเสมอ ผู้ใช้สามารถเลือกรายชื่อติดต่อเพื่อแก้ไขหรือเพิ่มรายชื่อติดต่อใหม่ก็ได้ หน้าจอแก้ไขหรือเพิ่มจะปรากฏขึ้น ทั้งนี้ขึ้นอยู่กับตัวเลือกของผู้ใช้ และระบบจะแสดงข้อมูลเพิ่มเติมที่คุณส่งใน Intent หากแอปแสดงข้อมูลติดต่อ เช่น อีเมลหรือหมายเลขโทรศัพท์ ให้ใช้ Intent นี้เพื่อให้ผู้ใช้เพิ่มข้อมูลลงในรายชื่อติดต่อที่มีอยู่ได้
รายชื่อติดต่อ
หมายเหตุ: ไม่จำเป็นต้องส่งค่าชื่อในส่วนเสริมของ Intent นี้ เนื่องจากผู้ใช้จะเลือกชื่อที่มีอยู่หรือเพิ่มชื่อใหม่เสมอ นอกจากนี้ หากคุณส่งชื่อและผู้ใช้เลือกที่จะแก้ไข แอปรายชื่อติดต่อจะแสดงชื่อที่คุณส่ง โดยเขียนทับค่าก่อนหน้า หากผู้ใช้ไม่สังเกตเห็นและบันทึกการแก้ไข ค่าเก่าจะหายไป |
แอปรายชื่อติดต่อของอุปกรณ์ไม่อนุญาตให้คุณลบข้อมูลติดต่อดิบหรือข้อมูลใดๆ ของข้อมูลติดต่อดิบด้วย Intent แต่หากต้องการลบข้อมูลติดต่อดิบ ให้ใช้ ContentResolver.delete() หรือ ContentProviderOperation.newDelete() แทน
ข้อมูลโค้ดต่อไปนี้แสดงวิธีสร้างและส่ง Intent ที่แทรกข้อมูลติดต่อดิบและข้อมูลใหม่
Kotlin
// Gets values from the UI val name = contactNameEditText.text.toString() val phone = contactPhoneEditText.text.toString() val email = contactEmailEditText.text.toString() val company = companyName.text.toString() val jobtitle = jobTitle.text.toString() /* * Demonstrates adding data rows as an array list associated with the DATA key */ // Defines an array list to contain the ContentValues objects for each row val contactData = arrayListOf<ContentValues>() /* * Defines the raw contact row */ // Sets up the row as a ContentValues object val rawContactRow = ContentValues().apply { // Adds the account type and name to the row put(ContactsContract.RawContacts.ACCOUNT_TYPE, selectedAccount.type) put(ContactsContract.RawContacts.ACCOUNT_NAME, selectedAccount.name) } // Adds the row to the array contactData.add(rawContactRow) /* * Sets up the phone number data row */ // Sets up the row as a ContentValues object val phoneRow = ContentValues().apply { // Specifies the MIME type for this data row (all data rows must be marked by their type) put(ContactsContract.Data.MIMETYPE,ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE) // Adds the phone number and its type to the row put(ContactsContract.CommonDataKinds.Phone.NUMBER, phone) } // Adds the row to the array contactData.add(phoneRow) /* * Sets up the email data row */ // Sets up the row as a ContentValues object val emailRow = ContentValues().apply { // Specifies the MIME type for this data row (all data rows must be marked by their type) put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE) // Adds the email address and its type to the row put(ContactsContract.CommonDataKinds.Email.ADDRESS, email) } // Adds the row to the array contactData.add(emailRow) // Creates a new intent for sending to the device's contacts application val insertIntent = Intent(ContactsContract.Intents.Insert.ACTION).apply { // Sets the MIME type to the one expected by the insertion activity type = ContactsContract.RawContacts.CONTENT_TYPE // Sets the new contact name putExtra(ContactsContract.Intents.Insert.NAME, name) // Sets the new company and job title putExtra(ContactsContract.Intents.Insert.COMPANY, company) putExtra(ContactsContract.Intents.Insert.JOB_TITLE, jobtitle) /* * Adds the array to the intent's extras. It must be a parcelable object in order to * travel between processes. The device's contacts app expects its key to be * Intents.Insert.DATA */ putParcelableArrayListExtra(ContactsContract.Intents.Insert.DATA, contactData) } // Send out the intent to start the device's contacts app in its add contact activity. startActivity(insertIntent)
Java
// Gets values from the UI String name = contactNameEditText.getText().toString(); String phone = contactPhoneEditText.getText().toString(); String email = contactEmailEditText.getText().toString(); String company = companyName.getText().toString(); String jobtitle = jobTitle.getText().toString(); // Creates a new intent for sending to the device's contacts application Intent insertIntent = new Intent(ContactsContract.Intents.Insert.ACTION); // Sets the MIME type to the one expected by the insertion activity insertIntent.setType(ContactsContract.RawContacts.CONTENT_TYPE); // Sets the new contact name insertIntent.putExtra(ContactsContract.Intents.Insert.NAME, name); // Sets the new company and job title insertIntent.putExtra(ContactsContract.Intents.Insert.COMPANY, company); insertIntent.putExtra(ContactsContract.Intents.Insert.JOB_TITLE, jobtitle); /* * Demonstrates adding data rows as an array list associated with the DATA key */ // Defines an array list to contain the ContentValues objects for each row ArrayList<ContentValues> contactData = new ArrayList<ContentValues>(); /* * Defines the raw contact row */ // Sets up the row as a ContentValues object ContentValues rawContactRow = new ContentValues(); // Adds the account type and name to the row rawContactRow.put(ContactsContract.RawContacts.ACCOUNT_TYPE, selectedAccount.getType()); rawContactRow.put(ContactsContract.RawContacts.ACCOUNT_NAME, selectedAccount.getName()); // Adds the row to the array contactData.add(rawContactRow); /* * Sets up the phone number data row */ // Sets up the row as a ContentValues object ContentValues phoneRow = new ContentValues(); // Specifies the MIME type for this data row (all data rows must be marked by their type) phoneRow.put( ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE ); // Adds the phone number and its type to the row phoneRow.put(ContactsContract.CommonDataKinds.Phone.NUMBER, phone); // Adds the row to the array contactData.add(phoneRow); /* * Sets up the email data row */ // Sets up the row as a ContentValues object ContentValues emailRow = new ContentValues(); // Specifies the MIME type for this data row (all data rows must be marked by their type) emailRow.put( ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE ); // Adds the email address and its type to the row emailRow.put(ContactsContract.CommonDataKinds.Email.ADDRESS, email); // Adds the row to the array contactData.add(emailRow); /* * Adds the array to the intent's extras. It must be a parcelable object in order to * travel between processes. The device's contacts app expects its key to be * Intents.Insert.DATA */ insertIntent.putParcelableArrayListExtra(ContactsContract.Intents.Insert.DATA, contactData); // Send out the intent to start the device's contacts app in its add contact activity. startActivity(insertIntent);
ความสมบูรณ์ของข้อมูล
เนื่องจากที่เก็บข้อมูลรายชื่อติดต่อมีข้อมูลสำคัญและละเอียดอ่อนที่ผู้ใช้คาดหวังว่าจะถูกต้องและเป็นปัจจุบัน Contacts Provider จึงมีกฎที่กำหนดไว้อย่างชัดเจนสำหรับความสมบูรณ์ของข้อมูล คุณมีหน้าที่รับผิดชอบในการปฏิบัติตามกฎเหล่านี้เมื่อแก้ไขข้อมูลรายชื่อติดต่อ กฎสำคัญมีดังนี้
-
เพิ่มแถว
ContactsContract.CommonDataKinds.StructuredNameเสมอสำหรับทุกแถวContactsContract.RawContactsที่คุณเพิ่ม -
แถว
ContactsContract.RawContactsที่ไม่มีแถวContactsContract.CommonDataKinds.StructuredNameในตารางContactsContract.Dataอาจทำให้เกิดปัญหาในระหว่างการรวมข้อมูล -
ลิงก์แถว
ContactsContract.Dataใหม่กับแถวContactsContract.RawContactsระดับบนเสมอ - แถว
ContactsContract.Dataที่ไม่ได้ลิงก์กับContactsContract.RawContactsจะไม่ปรากฏในแอปพลิเคชันรายชื่อผู้ติดต่อของอุปกรณ์ และอาจทำให้เกิดปัญหากับอะแดปเตอร์การซิงค์ - เปลี่ยนข้อมูลเฉพาะสำหรับข้อมูลติดต่อดิบที่คุณเป็นเจ้าของ
- โปรดทราบว่า Contacts Provider มักจะจัดการข้อมูลจากบัญชี/บริการออนไลน์ประเภทต่างๆ คุณต้องตรวจสอบว่าแอปพลิเคชันของคุณแก้ไขหรือลบข้อมูลสำหรับแถวที่เป็นของคุณเท่านั้น และแทรกเฉพาะข้อมูลที่มีประเภทบัญชีและชื่อที่คุณควบคุมเท่านั้น
-
ใช้ค่าคงที่ที่กำหนดไว้ใน
ContactsContractและคลาสย่อยสำหรับหน่วยงาน, URI เนื้อหา, เส้นทาง URI, ชื่อคอลัมน์, ประเภท MIME และค่าTYPEเสมอ - การใช้ค่าคงที่เหล่านี้จะช่วยให้คุณหลีกเลี่ยงข้อผิดพลาดได้ นอกจากนี้ คุณจะได้รับแจ้งเตือนจากคอมไพเลอร์หากค่าคงที่ใดๆ เลิกใช้งานแล้ว
แถวข้อมูลที่กำหนดเอง
การสร้างและใช้ประเภท MIME ที่กำหนดเองจะช่วยให้คุณแทรก แก้ไข ลบ และดึงข้อมูลแถวของคุณเองในตาราง ContactsContract.Data ได้ แถวของคุณถูกจำกัดให้ใช้คอลัมน์ที่กำหนดไว้ใน ContactsContract.DataColumns แม้ว่าคุณจะแมปชื่อคอลัมน์เฉพาะประเภทของคุณเองกับชื่อคอลัมน์เริ่มต้นได้ก็ตาม ในแอปพลิเคชันรายชื่อผู้ติดต่อของอุปกรณ์ ข้อมูลสำหรับแถวของคุณจะแสดงขึ้นแต่ไม่สามารถแก้ไขหรือลบได้ และผู้ใช้จะเพิ่มข้อมูลเพิ่มเติมไม่ได้ หากต้องการอนุญาตให้ผู้ใช้แก้ไขแถวข้อมูลที่กำหนดเอง คุณต้องจัดเตรียมกิจกรรมของเครื่องมือแก้ไขในแอปพลิเคชันของคุณเอง
หากต้องการแสดงข้อมูลที่กำหนดเอง ให้ระบุไฟล์ contacts.xml ที่มีองค์ประกอบ <ContactsAccountType> และองค์ประกอบย่อย <ContactsDataKind> อย่างน้อย 1 รายการ ซึ่งอธิบายไว้อย่างละเอียดในส่วน <ContactsDataKind> element
ดูข้อมูลเพิ่มเติมเกี่ยวกับประเภท MIME ที่กำหนดเองได้ที่คู่มือ สร้าง Content Provider
อะแดปเตอร์การซิงค์ Contacts Provider
Contacts Provider ได้รับการออกแบบมาโดยเฉพาะเพื่อจัดการการซิงค์ข้อมูลรายชื่อติดต่อระหว่างอุปกรณ์กับบริการออนไลน์ ซึ่งจะช่วยให้ผู้ใช้ดาวน์โหลดข้อมูลที่มีอยู่ไปยังอุปกรณ์ใหม่และอัปโหลดข้อมูลที่มีอยู่ไปยังบัญชีใหม่ได้ นอกจากนี้ การซิงค์ยังช่วยให้ผู้ใช้มีข้อมูลล่าสุดอยู่เสมอ ไม่ว่าข้อมูลที่เพิ่มและเปลี่ยนแปลงจะมาจากแหล่งใดก็ตาม ข้อดีอีกอย่างของการซิงค์คือช่วยให้ข้อมูลรายชื่อติดต่อพร้อมใช้งานแม้ว่าอุปกรณ์จะไม่ได้เชื่อมต่อกับเครือข่ายก็ตาม
แม้ว่าคุณจะใช้การซิงค์ได้หลายวิธี แต่ระบบ Android ก็มีเฟรมเวิร์กการซิงค์ปลั๊กอินที่ทำงานอัตโนมัติสำหรับงานต่อไปนี้
- กำลังตรวจสอบความพร้อมให้บริการของเครือข่าย
- การกำหนดเวลาและการดำเนินการซิงค์ตามค่ากำหนดของผู้ใช้
- การรีสตาร์ทการซิงค์ที่หยุดไปแล้ว
หากต้องการใช้เฟรมเวิร์กนี้ คุณต้องระบุปลั๊กอินอะแดปเตอร์การซิงค์ อะแดปเตอร์การซิงค์แต่ละรายการจะเจาะจงสำหรับบริการและ Content Provider แต่สามารถจัดการชื่อบัญชีหลายชื่อสำหรับบริการเดียวกันได้ นอกจากนี้ เฟรมเวิร์กยังอนุญาตให้มีอะแดปเตอร์การซิงค์หลายรายการสำหรับบริการและผู้ให้บริการเดียวกัน
คลาสและไฟล์ของอะแดปเตอร์การซิงค์
คุณใช้อะแดปเตอร์การซิงค์เป็นคลาสย่อยของ AbstractThreadedSyncAdapter และติดตั้งเป็นส่วนหนึ่งของแอปพลิเคชัน Android ระบบจะเรียนรู้เกี่ยวกับอะแดปเตอร์การซิงค์จากองค์ประกอบในไฟล์ Manifest ของแอปพลิเคชัน และจากไฟล์ XML พิเศษที่ไฟล์ Manifest ชี้ไป ไฟล์ XML จะกำหนดประเภทบัญชีสำหรับบริการออนไลน์และสิทธิ์สำหรับ Content Provider ซึ่งจะระบุอะแดปเตอร์โดยไม่ซ้ำกัน อะแดปเตอร์การซิงค์จะไม่ทำงานจนกว่าผู้ใช้จะเพิ่มบัญชีสำหรับประเภทบัญชีของอะแดปเตอร์การซิงค์ และเปิดใช้การซิงค์ข้อมูลสำหรับ Content Provider ที่อะแดปเตอร์การซิงค์จะซิงค์ด้วย เมื่อถึงจุดนั้น ระบบจะเริ่มจัดการอะแดปเตอร์ โดยเรียกใช้อะแดปเตอร์ตามความจำเป็นเพื่อซิงค์ระหว่าง Content Provider กับเซิร์ฟเวอร์
หมายเหตุ: การใช้ประเภทบัญชีเป็นส่วนหนึ่งของการระบุอะแดปเตอร์การซิงค์ช่วยให้ระบบตรวจจับและจัดกลุ่มอะแดปเตอร์การซิงค์ที่เข้าถึงบริการต่างๆ จากองค์กรเดียวกันเข้าด้วยกันได้ ตัวอย่างเช่น อะแดปเตอร์การซิงค์สำหรับบริการออนไลน์ของ Google ทั้งหมดมีประเภทบัญชี com.google เหมือนกัน เมื่อผู้ใช้เพิ่มบัญชี Google ลงในอุปกรณ์ อะแดปเตอร์การซิงค์ที่ติดตั้งไว้ทั้งหมดสำหรับบริการของ Google จะแสดงรวมกัน โดยอะแดปเตอร์การซิงค์แต่ละรายการที่แสดงจะซิงค์กับ Content Provider อื่นในอุปกรณ์
เนื่องจากบริการส่วนใหญ่กำหนดให้ผู้ใช้ยืนยันตัวตนก่อนเข้าถึงข้อมูล ระบบ Android จึงมีเฟรมเวิร์กการตรวจสอบสิทธิ์ที่คล้ายกับและมักใช้ร่วมกับเฟรมเวิร์กอะแดปเตอร์การซิงค์ เฟรมเวิร์กการตรวจสอบสิทธิ์ใช้ตัวตรวจสอบสิทธิ์ปลั๊กอินซึ่งเป็นคลาสย่อยของ AbstractAccountAuthenticator โปรแกรมตรวจสอบสิทธิ์จะยืนยันตัวตนของผู้ใช้ตามขั้นตอนต่อไปนี้
- รวบรวมชื่อ รหัสผ่าน หรือข้อมูลที่คล้ายกันของผู้ใช้ (ข้อมูลเข้าสู่ระบบของผู้ใช้)
- ส่งข้อมูลเข้าสู่ระบบไปยังบริการ
- ตรวจสอบการตอบกลับของบริการ
หากบริการยอมรับข้อมูลเข้าสู่ระบบ เครื่องมือตรวจสอบสิทธิ์จะจัดเก็บข้อมูลเข้าสู่ระบบไว้เพื่อใช้ในภายหลังได้ เนื่องจากเฟรมเวิร์กของตัวตรวจสอบสิทธิ์ปลั๊กอิน AccountManager จึงให้สิทธิ์เข้าถึงโทเค็นการตรวจสอบสิทธิ์ที่ตัวตรวจสอบสิทธิ์รองรับและเลือกที่จะแสดงได้ เช่น โทเค็นการตรวจสอบสิทธิ์ OAuth2
แม้ว่าจะไม่จำเป็นต้องมีการตรวจสอบสิทธิ์ แต่บริการรายชื่อติดต่อส่วนใหญ่ก็ใช้การตรวจสอบสิทธิ์ อย่างไรก็ตาม คุณไม่จำเป็นต้องใช้เฟรมเวิร์กการตรวจสอบสิทธิ์ของ Android เพื่อทำการตรวจสอบสิทธิ์
การติดตั้งใช้งานอะแดปเตอร์การซิงค์
หากต้องการติดตั้งใช้งานอะแดปเตอร์การซิงค์สำหรับ Contacts Provider ให้เริ่มด้วยการสร้างแอปพลิเคชัน Android ที่มีสิ่งต่อไปนี้
-
คอมโพเนนต์
Serviceที่ตอบสนองต่อคำขอจากระบบเพื่อเชื่อมโยงกับอะแดปเตอร์การซิงค์ -
เมื่อระบบต้องการเรียกใช้การซิงค์ ระบบจะเรียกใช้เมธอด
onBind()ของบริการเพื่อรับIBinderสำหรับอะแดปเตอร์การซิงค์ ซึ่งจะช่วยให้ระบบทำการเรียกใช้เมธอดของอะแดปเตอร์ข้ามกระบวนการได้ -
อะแดปเตอร์การซิงค์จริงที่ใช้เป็นคลาสย่อยที่เป็นรูปธรรมของ
AbstractThreadedSyncAdapter -
คลาสนี้จะทำหน้าที่ดาวน์โหลดข้อมูลจากเซิร์ฟเวอร์ อัปโหลดข้อมูลจากอุปกรณ์ และแก้ไขข้อขัดแย้ง งานหลักของอะแดปเตอร์จะดำเนินการในเมธอด
onPerformSync()ต้องสร้างอินสแตนซ์คลาสนี้เป็น Singleton -
คลาสย่อยของ
Application -
คลาสนี้ทำหน้าที่เป็นโรงงานสำหรับอะแดปเตอร์การซิงค์ซิงเกิลตัน ใช้เมธอด
onCreate()เพื่อสร้างอินสแตนซ์อะแดปเตอร์การซิงค์ และระบุเมธอด "getter" แบบคงที่เพื่อแสดงผลซิงเกิลตันไปยังเมธอดonBind()ของบริการของอะแดปเตอร์การซิงค์ -
ไม่บังคับ: คอมโพเนนต์
Serviceที่ตอบสนองต่อคำขอจากระบบสำหรับการตรวจสอบสิทธิ์ผู้ใช้ -
AccountManagerเริ่มบริการนี้เพื่อเริ่มกระบวนการตรวจสอบสิทธิ์ เมธอดonCreate()ของบริการจะสร้างอินสแตนซ์ออบเจ็กต์ Authenticator เมื่อระบบต้องการตรวจสอบสิทธิ์บัญชีผู้ใช้สำหรับอะแดปเตอร์การซิงค์ของแอปพลิเคชัน ระบบจะเรียกใช้เมธอดonBind()ของบริการเพื่อรับIBinderสำหรับโปรแกรมตรวจสอบสิทธิ์ ซึ่งจะช่วยให้ระบบทำการเรียกข้ามกระบวนการไปยังเมธอดของ Authenticator ได้ -
ไม่บังคับ: คลาสย่อยที่เป็นรูปธรรมของ
AbstractAccountAuthenticatorที่จัดการคำขอการตรวจสอบสิทธิ์ -
คลาสนี้มีเมธอดที่
AccountManagerเรียกใช้เพื่อตรวจสอบสิทธิ์ข้อมูลเข้าสู่ระบบของผู้ใช้กับเซิร์ฟเวอร์ รายละเอียดของกระบวนการตรวจสอบสิทธิ์จะแตกต่างกันไปอย่างมาก โดยขึ้นอยู่กับเทคโนโลยีเซิร์ฟเวอร์ที่ใช้ คุณควรอ่านเอกสารประกอบสำหรับซอฟต์แวร์ของเซิร์ฟเวอร์เพื่อดูข้อมูลเพิ่มเติมเกี่ยวกับการตรวจสอบสิทธิ์ - ไฟล์ XML ที่กำหนดอะแดปเตอร์การซิงค์และโปรแกรมตรวจสอบสิทธิ์ให้กับระบบ
-
คอมโพเนนต์บริการของอะแดปเตอร์การซิงค์และโปรแกรมตรวจสอบสิทธิ์ที่อธิบายไว้ก่อนหน้านี้จะกำหนดไว้ในองค์ประกอบ
<service>ในไฟล์ Manifest ของแอปพลิเคชัน องค์ประกอบเหล่านี้มีองค์ประกอบย่อย<meta-data>ที่ให้ข้อมูลที่เฉพาะเจาะจงแก่ระบบ ดังนี้-
องค์ประกอบ
<meta-data>สำหรับบริการอะแดปเตอร์การซิงค์จะชี้ไปยังไฟล์ XMLres/xml/syncadapter.xmlในทางกลับกัน ไฟล์นี้จะระบุ URI สำหรับบริการบนเว็บที่จะซิงค์กับ Contacts Provider และประเภทบัญชีสำหรับบริการบนเว็บ -
ไม่บังคับ: องค์ประกอบ
<meta-data>สำหรับ Authenticator จะชี้ไปยังไฟล์ XMLres/xml/authenticator.xmlในทางกลับกัน ไฟล์นี้จะระบุประเภทบัญชีที่ตัวตรวจสอบสิทธิ์นี้รองรับ รวมถึงทรัพยากร UI ที่ปรากฏในระหว่างกระบวนการตรวจสอบสิทธิ์ ประเภทบัญชีที่ระบุในองค์ประกอบนี้ต้องเหมือนกับประเภทบัญชีที่ระบุสำหรับอะแดปเตอร์การซิงค์
-
องค์ประกอบ
ข้อมูลสตรีมโซเชียล
ตาราง android.provider.ContactsContract.StreamItems และ android.provider.ContactsContract.StreamItemPhotos จะจัดการข้อมูลขาเข้าจากโซเชียลเน็ตเวิร์ก คุณสามารถเขียนอะแดปเตอร์การซิงค์ที่เพิ่มข้อมูลสตรีมจากเครือข่ายของคุณเองลงในตารางเหล่านี้ หรือจะอ่านข้อมูลสตรีมจากตารางเหล่านี้และแสดงในแอปพลิเคชันของคุณเอง หรือทั้ง 2 อย่างก็ได้ ฟีเจอร์เหล่านี้ช่วยให้คุณผสานรวมบริการและแอปพลิเคชันเครือข่ายสังคมเข้ากับประสบการณ์การใช้งานเครือข่ายสังคมของ Android ได้
ข้อความสตรีมโซเชียล
รายการสตรีมจะเชื่อมโยงกับข้อมูลติดต่อดิบเสมอ android.provider.ContactsContract.StreamItemsColumns#RAW_CONTACT_ID จะลิงก์กับค่า _ID สำหรับข้อมูลติดต่อดิบ ประเภทบัญชีและชื่อบัญชีของข้อมูลติดต่อดิบจะจัดเก็บไว้ในแถวรายการสตรีมด้วย
จัดเก็บข้อมูลจากสตรีมในคอลัมน์ต่อไปนี้
- android.provider.ContactsContract.StreamItemsColumns#ACCOUNT_TYPE
- ต้องระบุ ประเภทบัญชีของผู้ใช้สำหรับข้อมูลติดต่อดิบที่เชื่อมโยงกับรายการสตรีมนี้ อย่าลืมตั้งค่านี้เมื่อแทรกรายการสตรีม
- android.provider.ContactsContract.StreamItemsColumns#ACCOUNT_NAME
- ต้องระบุ ชื่อบัญชีของผู้ใช้สำหรับข้อมูลติดต่อดิบที่เชื่อมโยงกับรายการสตรีมนี้ อย่าลืมตั้งค่านี้เมื่อแทรกรายการสตรีม
- คอลัมน์ตัวระบุ
-
ต้องระบุ คุณต้องแทรกคอลัมน์ตัวระบุต่อไปนี้เมื่อแทรกรายการสตรีม
- android.provider.ContactsContract.StreamItemsColumns#CONTACT_ID: ค่า android.provider.BaseColumns#_ID ของรายชื่อติดต่อที่เชื่อมโยงกับรายการสตรีมนี้
- android.provider.ContactsContract.StreamItemsColumns#CONTACT_LOOKUP_KEY: ค่า android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY ของรายชื่อติดต่อที่เชื่อมโยงกับรายการสตรีมนี้
- android.provider.ContactsContract.StreamItemsColumns#RAW_CONTACT_ID: ค่า android.provider.BaseColumns#_ID ของข้อมูลติดต่อดิบที่รายการสตรีมนี้เชื่อมโยงด้วย
- android.provider.ContactsContract.StreamItemsColumns#COMMENTS
- ไม่บังคับ จัดเก็บข้อมูลสรุปที่คุณแสดงได้ที่จุดเริ่มต้นของรายการสตรีม
- android.provider.ContactsContract.StreamItemsColumns#TEXT
-
ข้อความของรายการสตรีม ไม่ว่าจะเป็นเนื้อหาที่โพสต์โดยแหล่งที่มาของรายการ หรือคำอธิบายการดำเนินการบางอย่างที่สร้างรายการสตรีม คอลัมน์นี้สามารถมีรูปภาพทรัพยากรที่ฝังและการจัดรูปแบบใดก็ได้ที่
fromHtml()แสดงผลได้ ผู้ให้บริการอาจตัดหรือละเนื้อหาที่ยาว แต่จะพยายามหลีกเลี่ยงการตัดแท็ก - android.provider.ContactsContract.StreamItemsColumns#TIMESTAMP
- สตริงข้อความที่มีเวลาที่แทรกหรืออัปเดตรายการสตรีมในรูปแบบมิลลิวินาทีนับตั้งแต่ Epoch แอปพลิเคชันที่แทรกหรืออัปเดตรายการสตรีมมีหน้าที่รับผิดชอบในการดูแลรักษาคอลัมน์นี้ Contacts Provider จะไม่ดูแลรักษาคอลัมน์นี้โดยอัตโนมัติ
หากต้องการแสดงข้อมูลระบุตัวบุคคลสำหรับรายการสตรีม ให้ใช้ android.provider.ContactsContract.StreamItemsColumns#RES_ICON, android.provider.ContactsContract.StreamItemsColumns#RES_LABEL และ android.provider.ContactsContract.StreamItemsColumns#RES_PACKAGE เพื่อลิงก์กับทรัพยากรในแอปพลิเคชัน
ตาราง android.provider.ContactsContract.StreamItems ยังมีคอลัมน์ android.provider.ContactsContract.StreamItemsColumns#SYNC1 ถึง android.provider.ContactsContract.StreamItemsColumns#SYNC4 สำหรับการใช้งานเฉพาะของอะแดปเตอร์การซิงค์ด้วย
รูปภาพในสตรีมโซเชียล
ตาราง android.provider.ContactsContract.StreamItemPhotos จะจัดเก็บรูปภาพที่เชื่อมโยงกับรายการสตรีม คอลัมน์ android.provider.ContactsContract.StreamItemPhotosColumns#STREAM_ITEM_ID ของตารางลิงก์กับค่าในคอลัมน์ _ID ของตาราง android.provider.ContactsContract.StreamItems ข้อมูลอ้างอิงรูปภาพจะจัดเก็บไว้ในตารางในคอลัมน์ต่อไปนี้
- คอลัมน์ android.provider.ContactsContract.StreamItemPhotos#PHOTO (BLOB)
- การแสดงรูปภาพแบบไบนารี ซึ่งผู้ให้บริการปรับขนาดเพื่อจัดเก็บและแสดงผล คอลัมน์นี้มีไว้เพื่อความเข้ากันได้แบบย้อนหลังกับ Contacts Provider เวอร์ชันก่อนหน้าที่ใช้คอลัมน์นี้เพื่อจัดเก็บรูปภาพ อย่างไรก็ตาม ในเวอร์ชันปัจจุบัน คุณไม่ควรใช้คอลัมน์นี้เพื่อจัดเก็บรูปภาพ แต่ให้ใช้ android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_FILE_ID หรือ android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_URI (ซึ่งทั้ง 2 รายการอธิบายไว้ในประเด็นต่อไปนี้) เพื่อจัดเก็บรูปภาพในไฟล์แทน ตอนนี้คอลัมน์นี้มีภาพขนาดย่อของรูปภาพ ซึ่งพร้อมให้อ่านแล้ว
- android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_FILE_ID
-
ตัวระบุที่เป็นตัวเลขของรูปภาพสำหรับข้อมูลติดต่อดิบ เพิ่มค่านี้ต่อท้ายค่าคงที่
DisplayPhoto.CONTENT_URIเพื่อรับ URI เนื้อหาที่ชี้ไปยังไฟล์รูปภาพไฟล์เดียว แล้วเรียกใช้openAssetFileDescriptor()เพื่อรับแฮนเดิลไปยังไฟล์รูปภาพ - android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_URI
-
URI เนื้อหาที่ชี้ไปยังไฟล์รูปภาพโดยตรงสำหรับรูปภาพที่แสดงโดยแถวนี้
เรียกใช้
openAssetFileDescriptor()ด้วย URI นี้เพื่อรับแฮนเดิลไปยังไฟล์รูปภาพ
การใช้ตารางสตรีมโซเชียล
ตารางเหล่านี้ทำงานเหมือนกับตารางหลักอื่นๆ ใน Contacts Provider ยกเว้นว่า
- ตารางเหล่านี้ต้องมีสิทธิ์เข้าถึงเพิ่มเติม หากต้องการอ่านข้อมูลจากผู้ให้บริการเหล่านี้ แอปพลิเคชันของคุณต้องมีสิทธิ์ android.Manifest.permission#READ_SOCIAL_STREAM หากต้องการแก้ไข แอปพลิเคชันของคุณต้องมีสิทธิ์ android.Manifest.permission#WRITE_SOCIAL_STREAM
-
สำหรับตาราง android.provider.ContactsContract.StreamItems ระบบจะจำกัดจำนวนแถวที่จัดเก็บไว้สำหรับข้อมูลติดต่อดิบแต่ละรายการ เมื่อถึงขีดจำกัดนี้ Contacts Provider จะเพิ่มพื้นที่ว่างสำหรับแถวรายการสตรีมใหม่โดยการลบแถวที่มี android.provider.ContactsContract.StreamItemsColumns#TIMESTAMP ที่เก่าที่สุดโดยอัตโนมัติ หากต้องการดูขีดจำกัด ให้ส่งคำค้นหาไปยัง URI เนื้อหา android.provider.ContactsContract.StreamItems#CONTENT_LIMIT_URI คุณสามารถปล่อยอาร์กิวเมนต์ทั้งหมดนอกเหนือจาก URI เนื้อหาที่ตั้งค่าเป็น
nullไว้ได้ การค้นหาจะแสดงผลเคอร์เซอร์ที่มีแถวเดียว โดยมีคอลัมน์เดียวคือ android.provider.ContactsContract.StreamItems#MAX_ITEMS
คลาส android.provider.ContactsContract.StreamItems.StreamItemPhotos กำหนดตารางย่อยของ android.provider.ContactsContract.StreamItemPhotos ที่มีแถวรูปภาพสำหรับรายการสตรีมรายการเดียว
การโต้ตอบในสตรีมโซเชียล
ข้อมูลสตรีมโซเชียลที่จัดการโดย Contacts Provider ร่วมกับแอปพลิเคชันรายชื่อผู้ติดต่อของอุปกรณ์ช่วยให้คุณเชื่อมต่อระบบเครือข่ายสังคมกับรายชื่อผู้ติดต่อที่มีอยู่ได้อย่างมีประสิทธิภาพ ฟีเจอร์ที่พร้อมใช้งานมีดังนี้
- การซิงค์บริการเครือข่ายสังคมกับ Contacts Provider ด้วยอะแดปเตอร์การซิงค์จะช่วยให้คุณดึงข้อมูลกิจกรรมล่าสุดสำหรับรายชื่อติดต่อของผู้ใช้และจัดเก็บไว้ในตาราง android.provider.ContactsContract.StreamItems และ android.provider.ContactsContract.StreamItemPhotos เพื่อใช้ในภายหลังได้
- นอกจากการซิงค์ตามปกติแล้ว คุณยังเรียกใช้อะแดปเตอร์การซิงค์เพื่อดึงข้อมูลเพิ่มเติมเมื่อผู้ใช้เลือกรายชื่อติดต่อเพื่อดูได้ด้วย ซึ่งจะช่วยให้อะแดปเตอร์การซิงค์ดึงรูปภาพความละเอียดสูงและรายการสตรีมล่าสุดสำหรับรายชื่อติดต่อได้
- การลงทะเบียนการแจ้งเตือนกับแอปพลิเคชันรายชื่อผู้ติดต่อของอุปกรณ์และ Contacts Provider จะช่วยให้คุณรับ Intent เมื่อมีการดูรายชื่อติดต่อ และอัปเดตสถานะของรายชื่อติดต่อจากบริการของคุณ ณ จุดนั้น วิธีนี้อาจเร็วกว่าและใช้แบนด์วิดท์น้อยกว่าการซิงค์แบบเต็มด้วยอะแดปเตอร์การซิงค์
- ผู้ใช้สามารถเพิ่มรายชื่อติดต่อลงในบริการเครือข่ายสังคมของคุณขณะดูรายชื่อติดต่อในแอปพลิเคชันรายชื่อผู้ติดต่อของอุปกรณ์ คุณเปิดใช้ฟีเจอร์นี้ได้ด้วยฟีเจอร์ "เชิญรายชื่อติดต่อ" ซึ่งคุณเปิดใช้ได้ด้วยการรวมกิจกรรมที่เพิ่มรายชื่อติดต่อที่มีอยู่ลงในเครือข่ายของคุณ และไฟล์ XML ที่ให้แอปพลิเคชันรายชื่อผู้ติดต่อของอุปกรณ์และ Contacts Provider พร้อมรายละเอียดของแอปพลิเคชันของคุณ
การซิงค์รายการสตรีมกับ Contacts Provider เป็นประจำจะเหมือนกับการซิงค์อื่นๆ ดูข้อมูลเพิ่มเติมเกี่ยวกับการซิงค์ได้ที่ส่วนอะแดปเตอร์การซิงค์ Contacts Provider การลงทะเบียนการแจ้งเตือนและเชิญผู้ติดต่อจะอธิบายไว้ใน 2 ส่วนถัดไป
กำลังลงทะเบียนเพื่อจัดการมุมมองโซเชียลเน็ตเวิร์ก
วิธีลงทะเบียนอะแดปเตอร์การซิงค์เพื่อรับการแจ้งเตือนเมื่อผู้ใช้ดูรายชื่อติดต่อที่จัดการโดยอะแดปเตอร์การซิงค์มีดังนี้
-
สร้างไฟล์ชื่อ
contacts.xmlในไดเรกทอรีres/xml/ของโปรเจ็กต์ หากมีไฟล์นี้อยู่แล้ว คุณสามารถข้ามขั้นตอนนี้ได้ -
ในไฟล์นี้ ให้เพิ่มองค์ประกอบ
<ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android">หากมีองค์ประกอบนี้อยู่แล้ว คุณสามารถข้ามขั้นตอนนี้ได้ -
หากต้องการลงทะเบียนบริการที่ได้รับการแจ้งเตือนเมื่อผู้ใช้เปิดหน้ารายละเอียดของรายชื่อติดต่อในแอปพลิเคชันรายชื่อผู้ติดต่อของอุปกรณ์ ให้เพิ่มแอตทริบิวต์
viewContactNotifyService="serviceclass"ลงในองค์ประกอบ โดยที่serviceclassคือชื่อคลาสที่สมบูรณ์ของบริการที่ควรได้รับ Intent จากแอปพลิเคชันรายชื่อผู้ติดต่อของอุปกรณ์ สำหรับบริการแจ้งเตือน ให้ใช้คลาสที่ขยายIntentServiceเพื่อให้บริการรับ Intent ได้ ข้อมูลใน Intent ขาเข้ามี URI เนื้อหาของข้อมูลติดต่อดิบที่ผู้ใช้คลิก จากบริการแจ้งเตือน คุณสามารถเชื่อมโยงและเรียกใช้อะแดปเตอร์การซิงค์เพื่ออัปเดตข้อมูลสำหรับข้อมูลติดต่อดิบได้
วิธีลงทะเบียนกิจกรรมที่จะเรียกใช้เมื่อผู้ใช้คลิกรายการสตรีมหรือรูปภาพ หรือทั้ง 2 อย่าง
-
สร้างไฟล์ชื่อ
contacts.xmlในไดเรกทอรีres/xml/ของโปรเจ็กต์ หากมีไฟล์นี้อยู่แล้ว คุณสามารถข้ามขั้นตอนนี้ได้ -
ในไฟล์นี้ ให้เพิ่มองค์ประกอบ
<ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android">หากมีองค์ประกอบนี้อยู่แล้ว คุณสามารถข้ามขั้นตอนนี้ได้ -
หากต้องการลงทะเบียนกิจกรรมอย่างใดอย่างหนึ่งเพื่อจัดการผู้ใช้ที่คลิกรายการสตรีมในแอปพลิเคชันรายชื่อผู้ติดต่อของอุปกรณ์ ให้เพิ่มแอตทริบิวต์
viewStreamItemActivity="activityclass"ลงในองค์ประกอบ โดยที่activityclassคือชื่อคลาสที่สมบูรณ์ของกิจกรรมที่ควรได้รับ Intent จากแอปพลิเคชันรายชื่อผู้ติดต่อของอุปกรณ์ -
หากต้องการลงทะเบียนกิจกรรมอย่างใดอย่างหนึ่งเพื่อจัดการผู้ใช้ที่คลิกรูปภาพสตรีมในแอปพลิเคชันรายชื่อผู้ติดต่อของอุปกรณ์ ให้เพิ่มแอตทริบิวต์
viewStreamItemPhotoActivity="activityclass"ลงในองค์ประกอบ โดยที่activityclassคือชื่อคลาสที่สมบูรณ์ของกิจกรรมที่ควรได้รับ Intent จากแอปพลิเคชันรายชื่อผู้ติดต่อของอุปกรณ์
องค์ประกอบ <ContactsAccountType> มีคำอธิบายโดยละเอียดเพิ่มเติมในส่วนองค์ประกอบ<ContactsAccountType>
Intent ขาเข้าจะมี URI เนื้อหาของรายการหรือรูปภาพที่ผู้ใช้คลิก หากต้องการให้มีกิจกรรมแยกต่างหากสำหรับรายการข้อความและรูปภาพ ให้ใช้ทั้ง 2 แอตทริบิวต์ในไฟล์เดียวกัน
การโต้ตอบกับบริการเครือข่ายสังคมออนไลน์
ผู้ใช้ไม่จำเป็นต้องออกจากแอปพลิเคชันรายชื่อผู้ติดต่อของอุปกรณ์เพื่อเชิญรายชื่อติดต่อให้เข้าร่วมเว็บไซต์โซเชียลเน็ตเวิร์กของคุณ แต่คุณสามารถให้แอปรายชื่อติดต่อของอุปกรณ์ส่ง Intent เพื่อเชิญรายชื่อติดต่อให้เข้าร่วมกิจกรรมของคุณได้ โดยมีวิธีการตั้งค่าดังนี้
-
สร้างไฟล์ชื่อ
contacts.xmlในไดเรกทอรีres/xml/ของโปรเจ็กต์ หากมีไฟล์นี้อยู่แล้ว คุณสามารถข้ามขั้นตอนนี้ได้ -
ในไฟล์นี้ ให้เพิ่มองค์ประกอบ
<ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android">หากมีองค์ประกอบนี้อยู่แล้ว คุณสามารถข้ามขั้นตอนนี้ได้ -
เพิ่มแอตทริบิวต์ต่อไปนี้
inviteContactActivity="activityclass"-
inviteContactActionLabel="@string/invite_action_label"
activityclassคือชื่อคลาสที่สมบูรณ์ในตัวเองของกิจกรรมที่ควรได้รับ Intent ค่าinvite_action_labelเป็นสตริงข้อความที่แสดงในเมนูเพิ่มการเชื่อมต่อในแอปพลิเคชันรายชื่อผู้ติดต่อของอุปกรณ์
หมายเหตุ: ContactsSource เป็นชื่อแท็กที่เลิกใช้งานแล้วสำหรับ ContactsAccountType
ข้อมูลอ้างอิง contacts.xml
ไฟล์ contacts.xml มีองค์ประกอบ XML ที่ควบคุมการโต้ตอบของอะแดปเตอร์การซิงค์และแอปพลิเคชันกับแอปพลิเคชันรายชื่อผู้ติดต่อและ Contacts Provider เราจะอธิบายองค์ประกอบเหล่านี้ในส่วนต่อไปนี้
องค์ประกอบ <ContactsAccountType>
องค์ประกอบ <ContactsAccountType> จะควบคุมการโต้ตอบของแอปพลิเคชันกับแอปพลิเคชันรายชื่อผู้ติดต่อ โดยมีไวยากรณ์ดังนี้
<ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android" inviteContactActivity="activity_name" inviteContactActionLabel="invite_command_text" viewContactNotifyService="view_notify_service" viewGroupActivity="group_view_activity" viewGroupActionLabel="group_action_text" viewStreamItemActivity="viewstream_activity_name" viewStreamItemPhotoActivity="viewphotostream_activity_name">
มีอยู่ใน:
res/xml/contacts.xml
มีข้อมูลต่อไปนี้ได้
<ContactsDataKind>
คำอธิบาย:
ประกาศคอมโพเนนต์ Android และป้ายกำกับ UI ที่อนุญาตให้ผู้ใช้เชิญรายชื่อติดต่อคนใดคนหนึ่งให้เข้าร่วมโซเชียลเน็ตเวิร์ก แจ้งเตือนผู้ใช้เมื่อมีการอัปเดตสตรีมโซเชียลเน็ตเวิร์กรายการใดรายการหนึ่ง และอื่นๆ
โปรดทราบว่าคำนำหน้าแอตทริบิวต์ android: ไม่จำเป็นสำหรับแอตทริบิวต์ของ <ContactsAccountType>
แอตทริบิวต์
inviteContactActivity- ชื่อคลาสที่สมบูรณ์ของกิจกรรมในแอปพลิเคชันที่คุณต้องการเปิดใช้งานเมื่อผู้ใช้เลือกเพิ่มการเชื่อมต่อจากแอปพลิเคชันรายชื่อผู้ติดต่อของอุปกรณ์
inviteContactActionLabel-
สตริงข้อความที่แสดงสำหรับกิจกรรมที่ระบุใน
inviteContactActivityในเมนูเพิ่มการเชื่อมต่อ เช่น คุณสามารถใช้สตริง "ติดตามในเครือข่ายของฉัน" คุณสามารถใช้ตัวระบุทรัพยากรสตริงสำหรับป้ายกำกับนี้ได้ viewContactNotifyService- ชื่อคลาสที่สมบูรณ์ในตัวเองของบริการในแอปพลิเคชันของคุณที่ควรได้รับการแจ้งเตือนเมื่อผู้ใช้ดูรายชื่อติดต่อ การแจ้งเตือนนี้ส่งโดยแอปพลิเคชันรายชื่อผู้ติดต่อของอุปกรณ์ ซึ่งช่วยให้แอปพลิเคชันของคุณเลื่อนการดำเนินการที่ใช้ข้อมูลจำนวนมากออกไปจนกว่าจะจำเป็น ตัวอย่างเช่น แอปพลิเคชันของคุณสามารถตอบสนองต่อการแจ้งเตือนนี้โดยการอ่านและแสดงรูปภาพความละเอียดสูงของรายชื่อติดต่อและรายการสตรีมโซเชียลล่าสุด ฟีเจอร์นี้อธิบายไว้อย่างละเอียดในส่วนการโต้ตอบกับสตรีมโซเชียล
viewGroupActivity- ชื่อคลาสที่สมบูรณ์ในตัวเองของกิจกรรมในแอปพลิเคชันที่แสดงข้อมูลกลุ่มได้ เมื่อผู้ใช้คลิกป้ายกำกับกลุ่มในแอปพลิเคชันรายชื่อติดต่อของอุปกรณ์ ระบบจะแสดง UI สำหรับกิจกรรมนี้
viewGroupActionLabel-
ป้ายกำกับที่แอปพลิเคชันรายชื่อผู้ติดต่อแสดงสำหรับตัวควบคุม UI ที่อนุญาตให้ผู้ใช้ดูกลุ่มในแอปพลิเคชันของคุณ
แอตทริบิวต์นี้อนุญาตให้ใช้ตัวระบุทรัพยากรสตริง
viewStreamItemActivity- ชื่อคลาสที่สมบูรณ์ในตัวเองของกิจกรรมในแอปพลิเคชันที่แอปพลิเคชันรายชื่อผู้ติดต่อของอุปกรณ์เปิดขึ้นเมื่อผู้ใช้คลิกรายการสตรีมสำหรับข้อมูลติดต่อดิบ
viewStreamItemPhotoActivity- ชื่อคลาสที่มีคุณสมบัติครบถ้วนของกิจกรรมในแอปพลิเคชันของคุณที่แอปพลิเคชันรายชื่อผู้ติดต่อของอุปกรณ์เปิดขึ้นเมื่อผู้ใช้คลิกรูปภาพในรายการสตรีมสำหรับข้อมูลติดต่อดิบ
องค์ประกอบ <ContactsDataKind>
องค์ประกอบ <ContactsDataKind> จะควบคุมการแสดงแถวข้อมูลที่กำหนดเองของแอปพลิเคชันใน UI ของแอปพลิเคชันรายชื่อผู้ติดต่อ โดยมีไวยากรณ์ดังนี้
<ContactsDataKind android:mimeType="MIMEtype" android:icon="icon_resources" android:summaryColumn="column_name" android:detailColumn="column_name">
มีอยู่ใน:
<ContactsAccountType>
คำอธิบาย:
ใช้องค์ประกอบนี้เพื่อให้แอปพลิเคชันรายชื่อผู้ติดต่อแสดงเนื้อหาของแถวข้อมูลที่กำหนดเองเป็นส่วนหนึ่งของรายละเอียดของข้อมูลติดต่อดิบ เอลิเมนต์ย่อย <ContactsDataKind> แต่ละรายการของ <ContactsAccountType> แสดงถึงประเภทของแถวข้อมูลที่กำหนดเองซึ่งอะแดปเตอร์การซิงค์เพิ่มลงในตาราง ContactsContract.Data เพิ่มองค์ประกอบ <ContactsDataKind> 1 รายการสำหรับประเภท MIME ที่กำหนดเองแต่ละประเภทที่คุณใช้ คุณไม่จำเป็นต้องเพิ่มองค์ประกอบหากมีแถวข้อมูลที่กำหนดเองซึ่งไม่ต้องการแสดงข้อมูล
แอตทริบิวต์
android:mimeType-
ประเภท MIME ที่กำหนดเองซึ่งคุณได้กำหนดไว้สำหรับประเภทแถวข้อมูลที่กำหนดเองประเภทหนึ่งในตาราง
ContactsContract.Dataเช่น ค่าvnd.android.cursor.item/vnd.example.locationstatusอาจเป็นประเภท MIME ที่กำหนดเองสำหรับแถวข้อมูลที่บันทึกตำแหน่งที่ทราบล่าสุดของรายชื่อติดต่อ android:icon- ทรัพยากรที่ถอนออกได้ของ Android ที่แอปพลิเคชันรายชื่อผู้ติดต่อแสดงข้างข้อมูลของคุณ ใช้ข้อมูลนี้เพื่อระบุให้ผู้ใช้ทราบว่าข้อมูลมาจากบริการของคุณ
android:summaryColumn- ชื่อคอลัมน์สำหรับค่าแรกจาก 2 ค่าที่ดึงมาจากแถวข้อมูล ค่าจะแสดงเป็นบรรทัดแรกของรายการสำหรับแถวข้อมูลนี้ บรรทัดแรกมีไว้เพื่อใช้เป็นข้อมูลสรุป แต่ไม่บังคับ ดู android:detailColumn ด้วย
android:detailColumn-
ชื่อคอลัมน์สำหรับค่าที่ 2 จาก 2 ค่าที่ดึงมาจากแถวข้อมูล ค่าจะแสดงเป็นบรรทัดที่ 2 ของรายการสำหรับแถวข้อมูลนี้ ดูเพิ่มเติม
android:summaryColumn
ฟีเจอร์เพิ่มเติมของ Contacts Provider
นอกจากฟีเจอร์หลักที่อธิบายไว้ในส่วนก่อนหน้าแล้ว Contacts Provider ยังมีฟีเจอร์ที่มีประโยชน์ต่อไปนี้สำหรับการทำงานกับข้อมูลรายชื่อติดต่อ
- กลุ่มรายชื่อติดต่อ
- ฟีเจอร์รูปภาพ
กลุ่มรายชื่อติดต่อ
Contacts Provider สามารถเลือกติดป้ายกำกับคอลเล็กชันของรายชื่อติดต่อที่เกี่ยวข้องด้วยข้อมูลกลุ่มได้ หากเซิร์ฟเวอร์ที่เชื่อมโยงกับบัญชีผู้ใช้ต้องการรักษากลุ่มไว้ อะแดปเตอร์การซิงค์สำหรับประเภทบัญชีของบัญชีควรโอนข้อมูลกลุ่มระหว่าง Contacts Provider กับเซิร์ฟเวอร์ เมื่อผู้ใช้เพิ่มรายชื่อติดต่อใหม่ลงในเซิร์ฟเวอร์แล้วใส่รายชื่อติดต่อนี้ไว้ในกลุ่มใหม่ อะแดปเตอร์การซิงค์ต้องเพิ่มกลุ่มใหม่ลงในตาราง ContactsContract.Groups ระบบจะจัดเก็บกลุ่มที่ข้อมูลติดต่อดิบอยู่ไว้ในตาราง ContactsContract.Data โดยใช้ประเภท MIME ContactsContract.CommonDataKinds.GroupMembership
หากคุณกำลังออกแบบอะแดปเตอร์การซิงค์ที่จะเพิ่มข้อมูลติดต่อดิบจากเซิร์ฟเวอร์ไปยัง Contacts Provider และไม่ได้ใช้กลุ่ม คุณต้องแจ้งให้ผู้ให้บริการทำให้ข้อมูลของคุณปรากฏ ในโค้ดที่ทำงานเมื่อผู้ใช้เพิ่มบัญชีลงในอุปกรณ์ ให้อัปเดตแถว ContactsContract.Settings ที่ Contacts Provider เพิ่มสำหรับบัญชีนั้น ในแถวนี้ ให้ตั้งค่าของคอลัมน์ Settings.UNGROUPED_VISIBLE เป็น 1 เมื่อดำเนินการนี้ Contacts Provider จะทำให้ข้อมูลรายชื่อติดต่อของคุณปรากฏอยู่เสมอ แม้ว่าคุณจะไม่ได้ใช้กลุ่มก็ตาม
รูปภาพผู้ติดต่อ
ตาราง ContactsContract.Data จะจัดเก็บรูปภาพเป็นแถวที่มีประเภท MIME Photo.CONTENT_ITEM_TYPE คอลัมน์ CONTACT_ID ของแถวจะลิงก์กับคอลัมน์ _ID ของข้อมูลติดต่อดิบที่เป็นเจ้าของ
คลาส ContactsContract.Contacts.Photo กำหนดตารางย่อยของ ContactsContract.Contacts ที่มีข้อมูลรูปภาพสำหรับรูปภาพหลักของผู้ติดต่อ ซึ่งเป็นรูปภาพหลักของข้อมูลติดต่อดิบหลักของผู้ติดต่อ ในทำนองเดียวกัน คลาส ContactsContract.RawContacts.DisplayPhoto จะกำหนดตารางย่อยของ ContactsContract.RawContacts ที่มีข้อมูลรูปภาพสำหรับรูปภาพหลักของข้อมูลติดต่อดิบ
เอกสารอ้างอิงสำหรับ ContactsContract.Contacts.Photo และ ContactsContract.RawContacts.DisplayPhoto มีตัวอย่างการดึงข้อมูลรูปภาพ ไม่มีคลาสอำนวยความสะดวกสำหรับการดึงข้อมูลภาพขนาดย่อหลักสำหรับข้อมูลติดต่อดิบ แต่คุณสามารถส่งคำค้นหาไปยังตาราง ContactsContract.Data โดยเลือก _ID ของข้อมูลติดต่อดิบ, Photo.CONTENT_ITEM_TYPE และคอลัมน์ IS_PRIMARY เพื่อค้นหาแถวรูปภาพหลักของข้อมูลติดต่อดิบ
ข้อมูลสตรีมโซเชียลของบุคคลอาจรวมถึงรูปภาพด้วย ซึ่งจะจัดเก็บไว้ในตาราง android.provider.ContactsContract.StreamItemPhotos ซึ่งอธิบายไว้อย่างละเอียดในส่วนรูปภาพสตรีมโซเชียล