Der Kontaktdatenanbieter ist eine leistungsstarke und flexible Android-Komponente, die das zentrale Repository des Geräts mit Daten zu Personen verwaltet. Der Kontaktdatenanbieter ist die Quelle der Daten, die in der Kontakte-App des Geräts angezeigt werden. Sie können auch in Ihrer eigenen App auf seine Daten zugreifen und Daten zwischen dem Gerät und Onlinediensten übertragen. Der Anbieter unterstützt eine Vielzahl von Datenquellen und versucht, so viele Daten wie möglich für jede Person zu verwalten. Das führt zu einer komplexen Organisation. Aus diesem Grund enthält die API des Anbieters eine umfangreiche Reihe von Vertragsklassen und ‑schnittstellen, die sowohl den Abruf als auch die Änderung von Daten erleichtern.
In diesem Leitfaden wird Folgendes beschrieben:
- Die grundlegende Anbieterstruktur.
- So rufen Sie Daten vom Anbieter ab.
- So ändern Sie Daten beim Anbieter.
- So schreiben Sie einen Synchronisierungsadapter zum Synchronisieren von Daten von Ihrem Server mit dem Kontaktdatenanbieter.
In diesem Leitfaden wird davon ausgegangen, dass Sie mit den Grundlagen von Android-Inhaltsanbietern vertraut sind. Weitere Informationen zu Android-Inhaltsanbietern finden Sie im Leitfaden Grundlagen von Inhaltsanbietern.
Organisation des Anbieters von Kontaktdaten
Der Contacts Provider ist eine Android-Contentanbieterkomponente. Es werden drei Arten von Daten zu einer Person gespeichert, die jeweils einer vom Anbieter angebotenen Tabelle entsprechen, wie in Abbildung 1 dargestellt:

Abbildung 1: Tabellenstruktur des Kontaktdatenanbieters
Die drei Tabellen werden häufig nach den Namen ihrer Vertragsklassen bezeichnet. Die Klassen definieren Konstanten für Inhalts-URIs, Spaltennamen und Spaltenwerte, die in den Tabellen verwendet werden:
-
Tabelle
ContactsContract.Contacts
- Zeilen, die verschiedene Personen repräsentieren, basierend auf Aggregationen von Rohkontaktzeilen.
-
Tabelle
ContactsContract.RawContacts
- Zeilen mit einer Zusammenfassung der Daten einer Person, die für ein Nutzerkonto und einen Nutzertyp spezifisch sind.
-
Tabelle
ContactsContract.Data
- Zeilen mit den Details für den Rohkontakt, z. B. E-Mail-Adressen oder Telefonnummern.
Die anderen Tabellen, die in ContactsContract
durch Vertragsklassen dargestellt werden, sind Hilfstabellen, mit denen der Kontaktdatenanbieter seine Abläufe verwaltet oder bestimmte Funktionen in den Kontakt- oder Telefonieanwendungen des Geräts unterstützt.
Rohkontakte
Ein Rohkontakt stellt die Daten einer Person dar, die aus einem einzelnen Kontotyp und einem einzelnen Kontonamen stammen. Da der Kontaktdatenanbieter mehrere Onlinedienste als Datenquelle für eine Person zulässt, erlaubt er auch mehrere Rohkontakte für dieselbe Person. Mit mehreren Rohkontakten können Nutzer die Daten einer Person aus mehreren Konten desselben Kontotyps kombinieren.
Die meisten Daten für einen Rohkontakt werden nicht in der Tabelle ContactsContract.RawContacts
gespeichert. Stattdessen werden sie in einer oder mehreren Zeilen in der Tabelle ContactsContract.Data
gespeichert. Jede Datenzeile hat eine Spalte Data.RAW_CONTACT_ID
, die den RawContacts._ID
-Wert der übergeordneten Zeile ContactsContract.RawContacts
enthält.
Wichtige Spalten für Rohkontakte
Die wichtigsten Spalten in der Tabelle ContactsContract.RawContacts
sind in Tabelle 1 aufgeführt. Bitte lesen Sie sich die Anmerkungen nach der Tabelle durch:
Tabelle 1 Wichtige Spalten für Rohkontakte.
Spaltenname | Verwenden | Hinweise |
---|---|---|
ACCOUNT_NAME
|
Der Kontoname für den Kontotyp, der die Quelle dieses Rohkontakts ist.
Der Kontoname eines Google-Kontos ist beispielsweise eine der Gmail-Adressen des Geräteeigentümers. Weitere Informationen finden Sie im nächsten Eintrag für ACCOUNT_TYPE .
|
Das Format dieses Namens ist vom Kontotyp abhängig. Es muss sich nicht unbedingt um eine E-Mail-Adresse handeln. |
ACCOUNT_TYPE
|
Der Kontotyp, der die Quelle dieses Rohkontakts ist. Der Kontotyp eines Google-Kontos ist beispielsweise com.google . Geben Sie den Kontotyp immer mit einer Domain-ID für eine Domain an, deren Inhaber Sie sind oder die Sie verwalten. So wird sichergestellt, dass Ihr Kontotyp eindeutig ist.
|
Ein Kontotyp, der Kontaktdaten anbietet, hat in der Regel einen zugehörigen Synchronisierungsadapter, der mit dem Kontaktdatenanbieter synchronisiert wird. |
DELETED
|
Das Flag „deleted“ (gelöscht) für einen Rohkontakt. | Mit diesem Flag kann der Kontaktdatenanbieter die Zeile intern verwalten, bis Synchronisierungsadapter die Zeile von ihren Servern löschen und dann schließlich aus dem Repository löschen können. |
Hinweise
Im Folgenden finden Sie wichtige Hinweise zur Tabelle ContactsContract.RawContacts
:
-
Der Name eines Rohkontakts wird nicht in seiner Zeile in
ContactsContract.RawContacts
gespeichert. Stattdessen wird sie in der TabelleContactsContract.Data
in einer Zeile vom TypContactsContract.CommonDataKinds.StructuredName
gespeichert. Ein Rohkontakt hat nur eine Zeile dieses Typs in der TabelleContactsContract.Data
. -
Achtung:Wenn Sie Ihre eigenen Kontodaten in einer Zeile mit Rohkontakten verwenden möchten, müssen sie zuerst bei der
AccountManager
registriert werden. Bitten Sie die Nutzer, den Kontotyp und den Kontonamen der Liste der Konten hinzuzufügen. Andernfalls löscht der Kontaktdatenanbieter die Zeile mit den Rohkontakten automatisch.Wenn Sie beispielsweise möchten, dass Ihre App Kontaktdaten für Ihren webbasierten Dienst mit der Domain
com.example.dataservice
verwaltet und das Konto des Nutzers für Ihren Dienstbecky.sharp@dataservice.example.com
lautet, muss der Nutzer zuerst den Kontotyp (com.example.dataservice
) und den Kontonamen (becky.smart@dataservice.example.com
) hinzufügen, bevor Ihre App Rohkontaktzeilen hinzufügen kann. Sie können diese Anforderung in der Dokumentation erläutern oder den Nutzer auffordern, den Typ und den Namen hinzuzufügen. Kontotypen und Kontonamen werden im nächsten Abschnitt ausführlicher beschrieben.
Quellen für Rohkontaktdaten
Um zu verstehen, wie Rohkontakte funktionieren, nehmen wir an, dass die Nutzerin „Emily Dickinson“ die folgenden drei Nutzerkonten auf ihrem Gerät definiert hat:
emily.dickinson@gmail.com
emilyd@gmail.com
- Twitter-Konto „belle_of_amherst“
Dieser Nutzer hat in den Einstellungen unter Konten die Option Kontakte synchronisieren für alle drei Konten aktiviert.
Angenommen, Emily Dickinson öffnet ein Browserfenster, meldet sich als emily.dickinson@gmail.com
in Gmail an, öffnet „Kontakte“ und fügt „Thomas Higginson“ hinzu. Später meldet sie sich als emilyd@gmail.com
in Gmail an und sendet eine E-Mail an „Thomas Higginson“. Dadurch wird er automatisch als Kontakt hinzugefügt. Außerdem folgt sie „colonel_tom“ (der Twitter-ID von Thomas Higginson) auf Twitter.
Der Kontaktdatenanbieter erstellt als Ergebnis dieser Arbeit drei Rohkontakte:
-
Ein Rohkontakt für „Thomas Higginson“, der mit
emily.dickinson@gmail.com
verknüpft ist. Der Nutzerkontotyp ist „Google“. -
Ein zweiter Rohkontakt für „Thomas Higginson“, der mit
emilyd@gmail.com
verknüpft ist. Der Nutzerkontotyp ist ebenfalls „Google“. Es gibt einen zweiten Rohkontakt, obwohl der Name mit einem früheren Namen identisch ist, weil die Person für ein anderes Nutzerkonto hinzugefügt wurde. - Ein dritter Rohkontakt für „Thomas Higginson“, der mit „belle_of_amherst“ verknüpft ist. Der Nutzerkontotyp ist Twitter.
Daten
Wie bereits erwähnt, werden die Daten für einen Rohkontakt in einer ContactsContract.Data
-Zeile gespeichert, die mit dem _ID
-Wert des Rohkontakts verknüpft ist. So kann ein einzelner Rohkontakt mehrere Instanzen desselben Datentyps wie E-Mail-Adressen oder Telefonnummern haben. Wenn „Thomas Higginson“ für emilyd@gmail.com
(die Zeile mit den Rohkontaktdaten für Thomas Higginson, die mit dem Google-Konto emilyd@gmail.com
verknüpft ist) beispielsweise die private E-Mail-Adresse thigg@gmail.com
und die geschäftliche E-Mail-Adresse thomas.higginson@gmail.com
hat, speichert der Kontaktdatenanbieter die beiden E-Mail-Adressenzeilen und verknüpft sie beide mit dem Rohkontakt.
Beachten Sie, dass in dieser einzelnen Tabelle verschiedene Datentypen gespeichert sind. Die Zeilen für Anzeigenamen, Telefonnummer, E-Mail-Adresse, Postanschrift, Foto und Websitedetails finden Sie in der Tabelle ContactsContract.Data
. Zur besseren Übersicht haben einige Spalten in der Tabelle ContactsContract.Data
beschreibende Namen, andere allgemeine Namen. Der Inhalt einer Spalte mit beschreibendem Namen hat unabhängig vom Datentyp in der Zeile dieselbe Bedeutung. Der Inhalt einer Spalte mit generischen Namen hat je nach Datentyp unterschiedliche Bedeutungen.
Beschreibende Spaltennamen
Beispiele für beschreibende Spaltennamen:
-
RAW_CONTACT_ID
-
Der Wert der Spalte
_ID
des Rohkontakts für diese Daten. -
MIMETYPE
-
Der Datentyp, der in dieser Zeile gespeichert ist, als benutzerdefinierter MIME-Typ. Der Contacts Provider verwendet die MIME-Typen, die in den Unterklassen von
ContactsContract.CommonDataKinds
definiert sind. Diese MIME-Typen sind Open Source und können von jeder Anwendung oder jedem Synchronisierungsadapter verwendet werden, der mit dem Kontaktdatenanbieter funktioniert. -
IS_PRIMARY
-
Wenn diese Art von Datenzeile für einen Rohkontakt mehrmals vorkommen kann, wird in der Spalte
IS_PRIMARY
die Datenzeile mit den primären Daten für den Typ gekennzeichnet. Wenn der Nutzer beispielsweise lange auf eine Telefonnummer für einen Kontakt drückt und Als Standard festlegen auswählt, wird in der ZeileContactsContract.Data
mit dieser Nummer in der SpalteIS_PRIMARY
ein Wert ungleich 0 festgelegt.
Generische Spaltennamen
Es gibt 15 generische Spalten mit den Namen DATA1
bis DATA15
, die allgemein verfügbar sind, sowie vier weitere generische Spalten mit den Namen SYNC1
bis SYNC4
, die nur von Synchronadaptern verwendet werden sollten. Die generischen Spaltennamenkonstanten funktionieren immer, unabhängig vom Datentyp der Zeile.
Die Spalte DATA1
ist indexiert. Der Kontaktdatenanbieter verwendet diese Spalte immer für die Daten, die seiner Meinung nach am häufigsten das Ziel einer Abfrage sind. In einer E-Mail-Zeile enthält diese Spalte beispielsweise die tatsächliche E-Mail-Adresse.
Die Spalte DATA15
ist standardmäßig für das Speichern von BLOB-Daten (Binary Large Object) wie Foto-Thumbnails reserviert.
Typspezifische Spaltennamen
Um die Arbeit mit den Spalten für einen bestimmten Zeilentyp zu erleichtern, stellt der Kontaktdatenanbieter auch typspezifische Konstanten für Spaltennamen bereit, die in Unterklassen von ContactsContract.CommonDataKinds
definiert sind. Die Konstanten geben demselben Spaltennamen einfach einen anderen Konstantennamen, damit Sie auf Daten in einer Zeile eines bestimmten Typs zugreifen können.
Die Klasse ContactsContract.CommonDataKinds.Email
definiert beispielsweise typspezifische Spaltennamenkonstanten für eine ContactsContract.Data
-Zeile mit dem MIME-Typ Email.CONTENT_ITEM_TYPE
. Die Klasse enthält die Konstante ADDRESS
für die Spalte mit E-Mail-Adressen. Der tatsächliche Wert von ADDRESS
ist „data1“, was dem generischen Namen der Spalte entspricht.
Achtung:Fügen Sie der Tabelle ContactsContract.Data
keine benutzerdefinierten Daten über eine Zeile mit einem der vordefinierten MIME-Typen des Anbieters hinzu. Andernfalls können die Daten verloren gehen oder der Anbieter kann nicht mehr ordnungsgemäß funktionieren. Sie sollten beispielsweise keine Zeile mit dem MIME-Typ Email.CONTENT_ITEM_TYPE
hinzufügen, die in der Spalte DATA1
einen Nutzernamen anstelle einer E-Mail-Adresse enthält. Wenn Sie für die Zeile einen benutzerdefinierten MIME-Typ verwenden, können Sie eigene typspezifische Spaltennamen definieren und die Spalten nach Belieben verwenden.
Abbildung 2 zeigt, wie Beschreibungsspalten und Datenspalten in einer Zeile ContactsContract.Data
angezeigt werden und wie sich typspezifische Spaltennamen über die generischen Spaltennamen legen.

Abbildung 2: Typspezifische und generische Spaltennamen.
Typspezifische Klassen für Spaltennamen
In Tabelle 2 sind die am häufigsten verwendeten typenspezifischen Klassen für Spaltennamen aufgeführt:
Tabelle 2 Typspezifische Klassen für Spaltennamen
Zuordnungsklasse | Datentyp | Hinweise |
---|---|---|
ContactsContract.CommonDataKinds.StructuredName |
Die Namensdaten für den Rohkontakt, der mit dieser Datenzeile verknüpft ist. | Ein Rohkontakt hat nur eine dieser Zeilen. |
ContactsContract.CommonDataKinds.Photo |
Das Hauptfoto des Rohkontakts, der mit dieser Datenzeile verknüpft ist. | Ein Rohkontakt hat nur eine dieser Zeilen. |
ContactsContract.CommonDataKinds.Email |
Eine E-Mail-Adresse für den Rohkontakt, der mit dieser Datenzeile verknüpft ist. | Ein Rohkontakt kann mehrere E-Mail-Adressen haben. |
ContactsContract.CommonDataKinds.StructuredPostal |
Eine Postadresse für den Rohkontakt, der mit dieser Datenzeile verknüpft ist. | Ein Rohkontakt kann mehrere Postadressen haben. |
ContactsContract.CommonDataKinds.GroupMembership |
Eine Kennung, die den Rohkontakt mit einer der Gruppen im Kontaktdatenanbieter verknüpft. | Gruppen sind eine optionale Funktion eines Kontotyps und Kontonamens. Sie werden im Abschnitt Kontaktgruppen ausführlicher beschrieben. |
Kontakte
Der Kontaktdatenanbieter kombiniert die Rohzeilen der Kontakte aller Kontotypen und Kontonamen zu einem Kontakt. So können alle Daten, die ein Nutzer für eine Person erfasst hat, angezeigt und geändert werden. Der Kontaktdatenanbieter verwaltet das Erstellen neuer Kontaktdatenzeilen und die Zusammenführung von Rohkontakten mit einer vorhandenen Kontaktdatenzeile. Weder Anwendungen noch Synchronisierungsadapter dürfen Kontakte hinzufügen. Außerdem sind einige Spalten in einer Kontaktzeile schreibgeschützt.
Hinweis:Wenn Sie versuchen, dem Kontaktdatenanbieter einen Kontakt mit einer insert()
hinzuzufügen, erhalten Sie eine UnsupportedOperationException
-Ausnahme. Wenn Sie versuchen, eine Spalte zu aktualisieren, die als „schreibgeschützt“ aufgeführt ist, wird die Aktualisierung ignoriert.
Der Kontaktdatenanbieter erstellt einen neuen Kontakt, wenn ein neuer Rohkontakt hinzugefügt wird, der keinem vorhandenen Kontakt entspricht. Dies geschieht auch, wenn sich die Daten eines vorhandenen Kontakts so ändern, dass sie nicht mehr mit dem Kontakt übereinstimmen, mit dem sie zuvor verknüpft waren. Wenn eine Anwendung oder ein Synchronisierungsadapter einen neuen Rohkontakt erstellt, der mit einem vorhandenen Kontakt übereinstimmt, wird der neue Rohkontakt mit dem vorhandenen Kontakt zusammengefasst.
Der Kontaktdatenanbieter verknüpft eine Kontaktdatenzeile mit ihren Rohdatenzeilen über die Spalte _ID
der Kontaktdatenzeile in der Tabelle Contacts
. Die Spalte CONTACT_ID
der Tabelle „Raw Contacts“ (Rohkontakte) ContactsContract.RawContacts
enthält _ID
-Werte für die Kontaktzeile, die jeder Zeile der Tabelle „Raw Contacts“ zugeordnet ist.
Die Tabelle ContactsContract.Contacts
enthält außerdem die Spalte LOOKUP_KEY
, die einen „permanenten“ Link zur Kontaktzeile darstellt. Da der Kontaktdatenanbieter Kontakte automatisch verwaltet, kann er den Wert _ID
einer Kontaktzeile als Reaktion auf eine Aggregation oder Synchronisierung ändern. In diesem Fall verweist der Inhalts-URI CONTENT_LOOKUP_URI
in Kombination mit der LOOKUP_KEY
des Kontakts weiterhin auf die Kontaktzeile. Sie können also weiterhin LOOKUP_KEY
verwenden, um Links zu „Favoritenkontakten“ usw. zu speichern. Diese Spalte hat ein eigenes Format, das sich vom Format der Spalte _ID
unterscheidet.
Abbildung 3 zeigt, wie die drei Haupttabellen zueinander in Beziehung stehen.

Abbildung 3: Beziehungen zwischen den Tabellen „Contacts“, „Raw Contacts“ und „Details“
Achtung : Wenn Sie Ihre App im Google Play Store veröffentlichen oder Ihre App auf einem Gerät mit Android 10 (API-Level 29) oder höher ausgeführt wird, beachten Sie, dass einige Datenfelder und Methoden für Kontaktdaten veraltet sind.
Unter den genannten Bedingungen löscht das System regelmäßig alle Werte, die in diese Datenfelder geschrieben wurden:
-
ContactsContract.ContactOptionsColumns.LAST_TIME_CONTACTED
-
ContactsContract.ContactOptionsColumns.TIMES_CONTACTED
-
ContactsContract.DataUsageStatColumns.LAST_TIME_USED
-
ContactsContract.DataUsageStatColumns.TIMES_USED
Auch die APIs, mit denen die oben genannten Datenfelder festgelegt wurden, sind nicht mehr verfügbar:
Außerdem werden in den folgenden Feldern keine häufigen Kontakte mehr zurückgegeben. Einige dieser Felder wirken sich nur dann auf das Ranking von Kontakten aus, wenn die Kontakte zu einer bestimmten Datenart gehören.
-
ContactsContract.Contacts.CONTENT_FREQUENT_URI
-
ContactsContract.Contacts.CONTENT_STREQUENT_URI
-
ContactsContract.Contacts.CONTENT_STREQUENT_FILTER_URI
-
CONTENT_FILTER_URI
(gilt nur für die Datentypen E-Mail, Telefon, Anrufbar und Kontaktbar) -
ENTERPRISE_CONTENT_FILTER_URI
(betrifft nur die Datentypen E-Mail, Telefon und Anrufbar)
Wenn Ihre Apps auf diese Felder oder APIs zugreifen oder sie aktualisieren, verwenden Sie alternative Methoden. So können Sie beispielsweise bestimmte Anwendungsfälle erfüllen, indem Sie private Contentanbieter oder andere Daten verwenden, die in Ihrer App oder Ihren Backend-Systemen gespeichert sind.
Sie können diese Datenfelder manuell löschen, um zu prüfen, ob sich die Funktionalität Ihrer App durch diese Änderung nicht beeinträchtigt. Führen Sie dazu auf einem Gerät mit Android 4.1 (API-Ebene 16) oder höher den folgenden ADB-Befehl aus:
adb shell content delete \ --uri content://com.android.contacts/contacts/delete_usage
Daten von Synchronisierungsadaptern
Nutzer geben Kontaktdaten direkt auf dem Gerät ein. Daten werden aber auch über Synchronadapter aus Webdiensten an den Kontaktdatenanbieter gesendet, wodurch die Übertragung von Daten zwischen dem Gerät und den Diensten automatisiert wird. Synchronisierungsadapter werden vom System im Hintergrund gesteuert und rufen ContentResolver
-Methoden auf, um Daten zu verwalten.
Unter Android wird der Webdienst, mit dem ein Synchronisierungsadapter zusammenarbeitet, durch einen Kontotyp identifiziert. Jeder Synchronisierungsadapter funktioniert mit einem Kontotyp, kann aber mehrere Kontonamen für diesen Typ unterstützen. Kontotypen und Kontonamen werden im Abschnitt Quellen für Rohkontaktdaten kurz beschrieben. In den folgenden Definitionen finden Sie weitere Informationen dazu, wie sich der Kontotyp und der Name auf Synchronadapter und ‑dienste beziehen.
- Kontotyp
-
Gibt einen Dienst an, in dem der Nutzer Daten gespeichert hat. In den meisten Fällen muss sich der Nutzer beim Dienst authentifizieren. Google Kontakte ist beispielsweise ein Kontotyp, der durch den Code
google.com
gekennzeichnet ist. Dieser Wert entspricht dem Kontotyp, der vonAccountManager
verwendet wird. - Kontoname
- Kennzeichnet ein bestimmtes Konto oder Login für einen Kontotyp. Google Kontakte-Konten sind mit Google-Konten identisch, die eine E-Mail-Adresse als Kontonamen haben. Andere Dienste verwenden möglicherweise einen einsilbigen Nutzernamen oder eine numerische ID.
Kontotypen müssen nicht eindeutig sein. Ein Nutzer kann mehrere Google Kontakte-Konten konfigurieren und seine Daten zum Kontaktdatenanbieter herunterladen. Das kann beispielsweise der Fall sein, wenn der Nutzer einen Satz privater Kontakte für einen privaten Kontonamen und einen anderen Satz für die Arbeit hat. Kontonamen sind in der Regel eindeutig. Zusammen identifizieren sie einen bestimmten Datenfluss zwischen dem Kontaktdatenanbieter und einem externen Dienst.
Wenn Sie die Daten Ihres Dienstes an den Kontaktanbieter übertragen möchten, müssen Sie Ihren eigenen Synchronisierungsadapter schreiben. Weitere Informationen finden Sie im Abschnitt Synchronadapter für Kontaktdatenanbieter.
Abbildung 4 zeigt, wie der Kontaktdatenanbieter in den Datenfluss zu Personen passt. Im Feld „Synchronisierungsadapter“ ist jeder Adapter mit seinem Kontotyp gekennzeichnet.

Abbildung 4: Der Datenfluss des Contacts Providers.
Erforderliche Berechtigungen
Apps, die auf den Kontaktdatenanbieter zugreifen möchten, müssen die folgenden Berechtigungen anfordern:
- Lesezugriff auf eine oder mehrere Tabellen
-
READ_CONTACTS
, inAndroidManifest.xml
mit dem<uses-permission>
-Element als<uses-permission android:name="android.permission.READ_CONTACTS">
angegeben. - Schreibzugriff auf eine oder mehrere Tabellen
-
WRITE_CONTACTS
, inAndroidManifest.xml
mit dem<uses-permission>
-Element als<uses-permission android:name="android.permission.WRITE_CONTACTS">
angegeben.
Diese Berechtigungen gelten nicht für die Nutzerprofildaten. Das Nutzerprofil und die erforderlichen Berechtigungen werden im folgenden Abschnitt Nutzerprofil beschrieben.
Denken Sie daran, dass die Kontaktdaten des Nutzers personenbezogen und vertraulich sind. Nutzer legen großen Wert auf den Datenschutz und möchten nicht, dass Apps Daten über sie oder ihre Kontakte erheben. Wenn nicht klar ist, warum Sie die Berechtigung zum Zugriff auf die Kontaktdaten benötigen, geben Nutzer Ihrer App möglicherweise eine schlechte Bewertung oder installieren sie gar nicht.
Das Nutzerprofil
Die Tabelle ContactsContract.Contacts
enthält eine einzige Zeile mit Profildaten für den Nutzer des Geräts. Diese Daten beschreiben die user
des Geräts und nicht einen der Kontakte des Nutzers. Die Zeile „Profile Contacts“ ist für jedes System, das ein Profil verwendet, mit einer Zeile mit Rohkontakten verknüpft.
Jede Zeile mit Profil-Raw-Kontakten kann mehrere Datenzeilen enthalten. Konstanten für den Zugriff auf das Nutzerprofil sind in der Klasse ContactsContract.Profile
verfügbar.
Für den Zugriff auf das Nutzerprofil sind spezielle Berechtigungen erforderlich. Zusätzlich zu den Berechtigungen READ_CONTACTS
und WRITE_CONTACTS
, die für das Lesen und Schreiben erforderlich sind, sind für den Zugriff auf das Nutzerprofil die Berechtigungen android.Manifest.permission#READ_PROFILE und android.Manifest.permission#WRITE_PROFILE für Lese- bzw. Schreibzugriff erforderlich.
Denken Sie daran, dass das Profil eines Nutzers vertraulich ist. Mit der Berechtigung „android.Manifest.permission#READ_PROFILE“ können Sie auf die personenidentifizierbaren Daten des Gerätenutzers zugreifen. Erläutern Sie in der Beschreibung Ihrer App, warum Sie Berechtigungen für den Zugriff auf Nutzerprofile benötigen.
Rufen Sie ContentResolver.query()
auf, um die Kontaktzeile mit dem Profil des Nutzers abzurufen. Legen Sie den Inhalts-URI auf CONTENT_URI
fest und geben Sie keine Auswahlkriterien an. Sie können diesen Inhalts-URI auch als Basis-URI zum Abrufen von Rohkontakten oder Daten für das Profil verwenden. Mit diesem Snippet werden beispielsweise Daten für das Profil abgerufen:
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);
Hinweis:Wenn Sie mehrere Kontaktzeilen abrufen und feststellen möchten, ob eine davon das Nutzerprofil ist, prüfen Sie die Spalte IS_USER_PROFILE
der Zeile. Diese Spalte ist auf „1“ gesetzt, wenn der Kontakt das Nutzerprofil ist.
Metadaten des Anbieters von Kontakten
Der Kontaktdatenanbieter verwaltet Daten, die den Status der Kontaktdaten im Repository im Blick behalten. Diese Metadaten zum Repository werden an verschiedenen Stellen gespeichert, einschließlich der Tabellenzeilen „Raw Contacts“, „Data“ und „Contacts“, der Tabelle ContactsContract.Settings
und der Tabelle ContactsContract.SyncState
. In der folgenden Tabelle sehen Sie die Auswirkungen dieser Metadaten:
Tabelle 3 Metadaten im Contacts Provider
Tabelle | Spalte | Werte | Bedeutung |
---|---|---|---|
ContactsContract.RawContacts |
DIRTY |
„0“ – seit der letzten Synchronisierung nicht geändert |
Kennzeichnet Rohkontakte, die auf dem Gerät geändert wurden und mit dem Server synchronisiert werden müssen. Der Wert wird vom Kontaktdatenanbieter automatisch festgelegt, wenn Android-Apps eine Zeile aktualisieren.
Synchronisatoren, die die Rohkontakt- oder Datentabellen ändern, sollten dem verwendeten Inhalts-URI immer den String |
„1“ – seit der letzten Synchronisierung geändert, muss mit dem Server synchronisiert werden | |||
ContactsContract.RawContacts |
VERSION |
Die Versionsnummer dieser Zeile. | Der Kontaktdatenanbieter erhöht diesen Wert automatisch, wenn sich die Zeile oder die zugehörigen Daten ändern. |
ContactsContract.Data |
DATA_VERSION |
Die Versionsnummer dieser Zeile. | Der Kontaktdatenanbieter erhöht diesen Wert automatisch, wenn sich die Datenzeile ändert. |
ContactsContract.RawContacts |
SOURCE_ID |
Ein Stringwert, der diesen Rohkontakt eindeutig mit dem Konto identifiziert, in dem er erstellt wurde. |
Wenn ein Synchronisierungsadapter einen neuen Rohkontakt erstellt, sollte diese Spalte auf die eindeutige ID des Servers für den Rohkontakt festgelegt werden. Wenn eine Android-Anwendung einen neuen Rohkontakt erstellt, sollte diese Spalte leer bleiben. Dies signalisiert dem Synchronisierungsadapter, dass er einen neuen Rohkontakt auf dem Server erstellen und einen Wert für die SOURCE_ID abrufen soll.
Insbesondere muss die Quell-ID für jeden Kontotyp eindeutig sein und bei jeder Synchronisierung gleich bleiben:
|
ContactsContract.Groups |
GROUP_VISIBLE |
„0“: Kontakte in dieser Gruppe sollten in der Benutzeroberfläche von Android-Apps nicht sichtbar sein. | Diese Spalte dient der Kompatibilität mit Servern, auf denen Nutzer Kontakte in bestimmten Gruppen ausblenden können. |
„1“: Kontakte in dieser Gruppe dürfen in der Benutzeroberfläche der App sichtbar sein. | |||
ContactsContract.Settings |
UNGROUPED_VISIBLE |
„0“: Für dieses Konto und diesen Kontotyp sind Kontakte, die keiner Gruppe angehören, für die Benutzeroberfläche von Android-Apps nicht sichtbar. |
Kontakte sind standardmäßig nicht sichtbar, wenn keiner ihrer Rohkontakte zu einer Gruppe gehört. Die Gruppenzugehörigkeit eines Rohkontakts wird durch eine oder mehrere ContactsContract.CommonDataKinds.GroupMembership -Zeilen in der ContactsContract.Data -Tabelle angezeigt.
Wenn Sie dieses Flag in der Tabellenzeile ContactsContract.Settings für einen Kontotyp und ein Konto setzen, werden Kontakte ohne Gruppen erzwungen angezeigt.
Dieses Flag kann beispielsweise verwendet werden, um Kontakte von Servern anzuzeigen, die keine Gruppen verwenden.
|
„1“: Für dieses Konto und diesen Kontotyp sind Kontakte, die keiner Gruppe angehören, für die Benutzeroberfläche der Anwendung sichtbar. | |||
ContactsContract.SyncState |
(alle) | Verwenden Sie diese Tabelle, um Metadaten für Ihren Synchronisierungsadapter zu speichern. | Mit dieser Tabelle können Sie den Synchronisierungsstatus und andere synchronisierungsbezogene Daten dauerhaft auf dem Gerät speichern. |
Zugriff des Anbieters für Kontakte
In diesem Abschnitt werden Richtlinien für den Zugriff auf Daten des Kontaktdatenanbieters beschrieben. Dabei liegt der Schwerpunkt auf den folgenden Themen:
- Entitätsabfragen
- Batch-Änderung
- Abrufen und Ändern mit Intents
- Datenintegrität.
Änderungen über einen Synchronisierungsadapter werden auch im Abschnitt Synchronisierungsadapter für Kontaktdatenanbieter ausführlicher behandelt.
Entitäten abfragen
Da die Tabellen der Kontaktdatenanbieter hierarchisch organisiert sind, ist es oft nützlich, eine Zeile und alle zugehörigen untergeordneten Zeilen abzurufen. Wenn Sie beispielsweise alle Informationen zu einer Person anzeigen lassen möchten, können Sie alle ContactsContract.RawContacts
-Zeilen für eine einzelne ContactsContract.Contacts
-Zeile oder alle ContactsContract.CommonDataKinds.Email
-Zeilen für eine einzelne ContactsContract.RawContacts
-Zeile abrufen. Dazu bietet der Kontaktdatenanbieter Entitätskonstrukte, die wie Datenbank-Joins zwischen Tabellen funktionieren.
Eine Entität ist wie eine Tabelle, die aus ausgewählten Spalten aus einer übergeordneten Tabelle und ihrer untergeordneten Tabelle besteht.
Wenn Sie eine Entität abfragen, geben Sie eine Projektion und Suchkriterien an, die auf den für die Entität verfügbaren Spalten basieren. Das Ergebnis ist eine Cursor
, die eine Zeile für jede Zeile der untergeordneten Tabelle enthält, die abgerufen wurde. Wenn Sie beispielsweise ContactsContract.Contacts.Entity
nach einem Kontaktnamen und alle ContactsContract.CommonDataKinds.Email
-Zeilen nach allen Rohkontakten für diesen Namen abfragen, erhalten Sie eine Cursor
mit einer Zeile für jede ContactsContract.CommonDataKinds.Email
-Zeile zurück.
Entitäten vereinfachen Abfragen. Mit einem Entitätsobjekt können Sie alle Kontaktdaten für einen Kontakt oder einen Rohkontakt auf einmal abrufen, anstatt zuerst die übergeordnete Tabelle abfragen zu müssen, um eine ID zu erhalten, und dann die untergeordnete Tabelle mit dieser ID abfragen zu müssen. Außerdem verarbeitet der Kontaktdatenanbieter eine Abfrage für ein Entitätsobjekt in einer einzelnen Transaktion, wodurch sichergestellt wird, dass die abgerufenen Daten intern konsistent sind.
Hinweis:Eine Entität enthält in der Regel nicht alle Spalten der übergeordneten und untergeordneten Tabelle. Wenn Sie versuchen, mit einem Spaltennamen zu arbeiten, der nicht in der Liste der Spaltennamenkonstanten für das Entitätsobjekt enthalten ist, wird Exception
zurückgegeben.
Im folgenden Snippet wird gezeigt, wie Sie alle Zeilen mit Rohkontaktdaten für einen Kontakt abrufen. Das Snippet ist Teil einer größeren Anwendung mit zwei Aktivitäten, „main“ und „detail“. Die Hauptaktivität zeigt eine Liste von Kontaktzeilen an. Wenn der Nutzer eine auswählt, sendet die Aktivität ihre ID an die Detailaktivität. Bei der Detailaktivität werden mithilfe von ContactsContract.Contacts.Entity
alle Datenzeilen aus allen Rohkontakten angezeigt, die mit dem ausgewählten Kontakt verknüpft sind.
Dieses Snippet stammt aus der Aktivität „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. }
Nach Abschluss des Ladens ruft LoaderManager
einen Rückruf an onLoadFinished()
auf. Eines der Eingabeargumente dieser Methode ist ein Cursor
mit den Ergebnissen der Abfrage. In Ihrer eigenen App können Sie die Daten aus diesem Cursor
abrufen, um sie anzuzeigen oder weiterzuverarbeiten.
Batch-Änderung
Sofern möglich, sollten Sie Daten im Kontaktdatenanbieter im „Batch-Modus“ einfügen, aktualisieren und löschen. Erstellen Sie dazu eine ArrayList
von ContentProviderOperation
-Objekten und rufen Sie applyBatch()
auf. Da der Kontaktdatenanbieter alle Vorgänge in einer applyBatch()
in einer einzigen Transaktion ausführt, wird das Kontaktdatenverzeichnis durch Ihre Änderungen nie in einen inkonsistenten Zustand versetzt. Eine Batch-Änderung erleichtert auch das gleichzeitige Einfügen eines Rohkontakts und seiner Detaildaten.
Hinweis:Wenn Sie einen einzelnen Rohkontakt ändern möchten, sollten Sie eine Intent an die Kontakte-App des Geräts senden, anstatt die Änderung in Ihrer App vorzunehmen. Weitere Informationen dazu finden Sie im Abschnitt Abrufen und Ändern mit Intents.
Ertragspunkte
Eine Batch-Änderung mit einer großen Anzahl von Vorgängen kann andere Prozesse blockieren und so die Nutzerfreundlichkeit beeinträchtigen. Wenn Sie alle gewünschten Änderungen in möglichst wenigen separaten Listen organisieren und gleichzeitig verhindern möchten, dass sie das System blockieren, sollten Sie für einen oder mehrere Vorgänge Ertragspunkte festlegen.
Ein Yield-Punkt ist ein ContentProviderOperation
-Objekt, dessen isYieldAllowed()
-Wert auf true
festgelegt ist. Wenn der Kontaktdatenanbieter einen Yield-Punkt erreicht, hält er seine Arbeit an, damit andere Prozesse ausgeführt werden können, und schließt die aktuelle Transaktion. Wenn der Anbieter neu gestartet wird, fährt er mit dem nächsten Vorgang in der ArrayList
fort und startet eine neue Transaktion.
Ertragspunkte führen zu mehr als einer Transaktion pro Aufruf von applyBatch()
. Aus diesem Grund sollten Sie für den letzten Vorgang für eine Reihe verknüpfter Zeilen einen Endpunkt festlegen.
Sie sollten beispielsweise einen Ertragspunkt für den letzten Vorgang in einem Satz festlegen, mit dem Rohkontaktzeilen und die zugehörigen Datenzeilen hinzugefügt werden, oder für den letzten Vorgang für einen Satz von Zeilen, die sich auf einen einzelnen Kontakt beziehen.
Yield-Punkte sind auch eine Einheit für atomaren Betrieb. Alle Zugriffe zwischen zwei Yield-Punkten verlaufen entweder als Einheit erfolgreich oder schlagen als solche fehl. Wenn Sie keine Yield-Punkte festlegen, ist der kleinste atomare Vorgang der gesamte Batch von Vorgängen. Wenn Sie Yield-Punkte verwenden, verhindern Sie, dass Vorgänge die Systemleistung beeinträchtigen, und sorgen gleichzeitig dafür, dass ein Teil der Vorgänge atomar ist.
Rückverweise auf Änderungen
Wenn Sie eine neue Zeile mit Rohkontakten und die zugehörigen Datenzeilen als Satz von ContentProviderOperation
-Objekten einfügen, müssen Sie die Datenzeilen mit der Zeile mit Rohkontakten verknüpfen, indem Sie den _ID
-Wert des Rohkontakts als RAW_CONTACT_ID
-Wert einfügen. Dieser Wert ist jedoch nicht verfügbar, wenn Sie die ContentProviderOperation
für die Datenzeile erstellen, da Sie die ContentProviderOperation
für die Rohkontaktzeile noch nicht angewendet haben. Um dieses Problem zu umgehen, hat die Klasse ContentProviderOperation.Builder
die Methode withValueBackReference()
.
Mit dieser Methode können Sie eine Spalte mit dem Ergebnis eines vorherigen Vorgangs einfügen oder ändern.
Die Methode withValueBackReference()
hat zwei Argumente:
-
key
- Der Schlüssel eines Schlüssel/Wert-Paars. Der Wert dieses Arguments sollte der Name einer Spalte in der Tabelle sein, die Sie ändern.
-
previousResult
-
Der Index eines Werts im Array der
ContentProviderResult
-Objekte ausapplyBatch()
, beginnend bei 0. Während die Batchvorgänge angewendet werden, wird das Ergebnis jeder Operation in einem Zwischenergebnisarray gespeichert. DerpreviousResult
-Wert ist der Index eines dieser Ergebnisse, der abgerufen und mit demkey
-Wert gespeichert wird. So können Sie einen neuen Raw-Kontakteintrag einfügen und den_ID
-Wert zurückgeben. Anschließend können Sie beim Hinzufügen einerContactsContract.Data
-Zeile einen Verweis auf den Wert herstellen.Das gesamte Ergebnisarray wird beim ersten Aufruf von
applyBatch()
erstellt und hat die Größe derArrayList
der von dir bereitgestelltenContentProviderOperation
-Objekte. Alle Elemente im Ergebnisarray sind jedoch aufnull
gesetzt. Wenn Sie versuchen, einen Rückverweis auf ein Ergebnis für einen Vorgang auszuführen, der noch nicht angewendet wurde, wirdwithValueBackReference()
ausgegeben.Exception
In den folgenden Snippets wird gezeigt, wie Sie einen neuen Rohkontakt und Daten im Batch einfügen. Sie enthalten Code, der einen Yield-Punkt festlegt und einen Rückverweise verwendet.
Im ersten Snippet werden Kontaktdaten aus der Benutzeroberfläche abgerufen. Der Nutzer hat bereits das Konto ausgewählt, dem der neue Rohkontakt hinzugefügt werden soll.
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());
Im nächsten Snippet wird ein Vorgang erstellt, um die Zeile mit den Rohkontakten in die Tabelle ContactsContract.RawContacts
einzufügen:
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());
Als Nächstes werden Datenzeilen für die Zeilen „Anzeigename“, „Telefon“ und „E-Mail“ erstellt.
Jedes Objekt vom Typ „Vorlage für Vorgänge“ verwendet withValueBackReference()
, um den RAW_CONTACT_ID
abzurufen. Die Referenzen verweisen auf das ContentProviderResult
-Objekt aus dem ersten Vorgang, wodurch die Zeile mit den Rohkontakten hinzugefügt und der neue _ID
-Wert zurückgegeben wird. Daher wird jede Datenzeile automatisch über ihr RAW_CONTACT_ID
mit der neuen ContactsContract.RawContacts
-Zeile verknüpft, zu der sie gehört.
Das ContentProviderOperation.Builder
-Objekt, das die E-Mail-Zeile hinzufügt, ist mit withYieldAllowed()
gekennzeichnet, wodurch ein Yield-Punkt festgelegt wird:
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());
Im letzten Snippet ist der Aufruf von applyBatch()
zu sehen, über den die neuen Zeilen für Rohkontakte und Daten eingefügt werden.
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); } }
Mit Batch-Vorgängen können Sie auch die optimistische Parallelitätssteuerung implementieren, eine Methode zum Anwenden von Änderungstransaktionen, ohne das zugrunde liegende Repository sperren zu müssen. Bei dieser Methode wenden Sie die Transaktion an und prüfen dann, ob gleichzeitig andere Änderungen vorgenommen wurden. Wenn Sie feststellen, dass eine inkonsistente Änderung vorgenommen wurde, können Sie die Transaktion rückgängig machen und noch einmal versuchen.
Die optimistische Parallelitätssteuerung ist für Mobilgeräte geeignet, auf denen jeweils nur ein Nutzer aktiv ist und gleichzeitige Zugriffe auf ein Datenrepository selten sind. Da keine Sperren verwendet werden, wird keine Zeit verschwendet, um Sperren festzulegen oder darauf zu warten, dass andere Transaktionen ihre Sperren aufheben.
So verwenden Sie die optimistische Datenbanküberprüfung beim Aktualisieren einer einzelnen ContactsContract.RawContacts
-Zeile:
-
Rufen Sie die Spalte
VERSION
des Rohkontakts zusammen mit den anderen abgerufenen Daten ab. -
Erstellen Sie mit der Methode
newAssertQuery(Uri)
einContentProviderOperation.Builder
-Objekt, das zum Erzwingen einer Einschränkung geeignet ist. Verwenden Sie für den Inhalts-URIRawContacts.CONTENT_URI
, an den die_ID
des Rohkontakts angehängt wird. -
Rufen Sie für das
ContentProviderOperation.Builder
-ObjektwithValue()
auf, um die SpalteVERSION
mit der gerade abgerufenen Versionsnummer zu vergleichen. -
Rufen Sie für dieselbe
ContentProviderOperation.Builder
withExpectedCount()
auf, damit nur eine Zeile durch diese Behauptung getestet wird. -
Rufen Sie
build()
auf, um dasContentProviderOperation
-Objekt zu erstellen, und fügen Sie es dann als erstes Objekt in dieArrayList
ein, die Sie anapplyBatch()
übergeben. - Wenden Sie die Batch-Transaktion an.
Wenn die Zeile mit den Rohkontakten zwischen dem Lesen der Zeile und dem Versuch, sie zu ändern, durch einen anderen Vorgang aktualisiert wird, schlägt die „assert“-Anweisung ContentProviderOperation
fehl und die gesamte Batch-Operation wird rückgängig gemacht. Sie können dann den Batch noch einmal versuchen oder eine andere Aktion ausführen.
Im folgenden Snippet wird gezeigt, wie Sie eine ContentProviderOperation
vom Typ „assert“ erstellen, nachdem Sie mit einer CursorLoader
nach einem einzelnen Rohkontakt gesucht haben:
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 }
Abrufen und Ändern mit Intents
Wenn Sie eine Intent an die Kontaktanwendung des Geräts senden, können Sie indirekt auf den Kontaktdatenanbieter zugreifen. Der Intent startet die Benutzeroberfläche der Kontaktanwendung des Geräts, in der Nutzer kontaktbezogene Aufgaben ausführen können. Mit diesem Zugriffstyp können Nutzer Folgendes tun:
- Wählen Sie einen Kontakt aus einer Liste aus und lassen Sie ihn zur weiteren Bearbeitung an Ihre App zurückgeben.
- Bearbeiten Sie die Daten eines vorhandenen Kontakts.
- Fügen Sie einen neuen Rohkontakt für eines der Konten ein.
- Daten eines Kontakts oder mehrerer Kontakte löschen
Wenn der Nutzer Daten einfügt oder aktualisiert, können Sie die Daten zuerst erfassen und als Teil der Intent-Aktion senden.
Wenn Sie über die Kontakte-App des Geräts über Intents auf den Kontaktdatenanbieter zugreifen, müssen Sie keine eigene Benutzeroberfläche oder keinen eigenen Code für den Zugriff auf den Anbieter schreiben. Außerdem müssen Sie keine Lese- oder Schreibberechtigung für den Anbieter anfordern. Die Kontakte-App des Geräts kann Ihnen die Leseberechtigung für einen Kontakt delegieren. Da Sie Änderungen am Anbieter über eine andere App vornehmen, benötigen Sie keine Schreibberechtigungen.
Das allgemeine Verfahren zum Senden einer Intent zum Zugriff auf einen Anbieter wird im Leitfaden
Grundlagen von Contentanbietern im Abschnitt „Datenzugriff über Intents“ ausführlich beschrieben. Die Aktion, der MIME-Typ und die Datenwerte, die Sie für die verfügbaren Aufgaben verwenden, sind in Tabelle 4 zusammengefasst. Die Extras-Werte, die Sie mit putExtra()
verwenden können, sind in der Referenzdokumentation für ContactsContract.Intents.Insert
aufgeführt:
Tabelle 4 Intents des Anbieters von Kontakten
Aufgabe | Aktion | Daten | MIME-Typ | Hinweise |
---|---|---|---|---|
Kontakt aus einer Liste auswählen | ACTION_PICK |
Eine der folgenden Möglichkeiten:
|
Nicht verwendet |
Je nach angegebenem Inhalts-URI-Typ wird eine Liste der Rohkontakte oder eine Liste der Daten aus einem Rohkontakt angezeigt.
Rufen Sie |
Neuen Rohkontakt einfügen | Insert.ACTION |
– |
RawContacts.CONTENT_TYPE , MIME-Typ für eine Reihe von Rohkontakten.
|
Der Bildschirm Kontakt hinzufügen der Kontakte App des Geräts wird angezeigt. Die zusätzlichen Werte, die Sie dem Intent hinzufügen, werden angezeigt. Wenn die Anfrage mit startActivityForResult() gesendet wird, wird der Inhalts-URI des neu hinzugefügten Rohkontakts an die onActivityResult() -Callback-Methode Ihrer Aktivität im Intent -Argument im Feld „data“ zurückgegeben. Rufen Sie getData() auf, um den Wert abzurufen.
|
Kontakt bearbeiten | ACTION_EDIT |
CONTENT_LOOKUP_URI für den Kontakt. Mit der Bearbeitungsaktivität kann der Nutzer alle mit diesem Kontakt verknüpften Daten bearbeiten.
|
Contacts.CONTENT_ITEM_TYPE , einen einzelnen Kontakt. |
Zeigt den Bildschirm „Kontakt bearbeiten“ in der Kontakte App an. Die zusätzlichen Werte, die Sie der Absicht hinzufügen, werden angezeigt. Wenn der Nutzer auf Fertig klickt, um die Änderungen zu speichern, kehrt Ihre Aktivität in den Vordergrund zurück. |
Eine Auswahl anzeigen, mit der auch Daten hinzugefügt werden können. | ACTION_INSERT_OR_EDIT |
– |
CONTENT_ITEM_TYPE
|
Bei dieser Intent-Art wird immer der Auswahlbildschirm der Kontakte-App angezeigt. Der Nutzer kann entweder einen Kontakt zum Bearbeiten auswählen oder einen neuen Kontakt hinzufügen. Je nach Auswahl des Nutzers wird entweder der Bearbeitungs- oder der Hinzufügungsbildschirm angezeigt. Die zusätzlichen Daten, die Sie in der Intent-Anfrage übergeben, werden angezeigt. Wenn in Ihrer App Kontaktdaten wie eine E-Mail-Adresse oder Telefonnummer angezeigt werden, verwenden Sie diesen Intent, damit Nutzer die Daten einem vorhandenen Kontakt hinzufügen können.
Kontakt,
Hinweis:Sie müssen in den Extras dieses Intents keinen Namens-Wert senden, da der Nutzer immer einen vorhandenen Namen auswählt oder einen neuen hinzufügt. Wenn Sie einen Namen senden und der Nutzer eine Änderung vornimmt, wird in der Kontakt-App der von Ihnen gesendete Name angezeigt und der vorherige Wert überschrieben. Wenn der Nutzer dies nicht bemerkt und die Änderung speichert, geht der alte Wert verloren. |
In der Kontakt-App des Geräts können Sie einen Rohkontakt oder seine Daten nicht mit einem Intent löschen. Verwenden Sie stattdessen ContentResolver.delete()
oder ContentProviderOperation.newDelete()
, um einen Rohkontakt zu löschen.
Im folgenden Snippet wird gezeigt, wie ein Intent erstellt und gesendet wird, mit dem ein neuer Rohkontakt und Daten eingefügt werden:
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);
Datenintegrität
Da das Kontaktdatenverzeichnis wichtige und vertrauliche Daten enthält, die Nutzer als korrekt und aktuell erwarten, hat der Kontaktdatenanbieter klar definierte Regeln für die Datenintegrität. Es liegt in Ihrer Verantwortung, diese Regeln einzuhalten, wenn Sie Kontaktdaten ändern. Die wichtigsten Regeln sind hier aufgeführt:
-
Fügen Sie immer eine
ContactsContract.CommonDataKinds.StructuredName
-Zeile für jede hinzugefügteContactsContract.RawContacts
-Zeile hinzu. -
Eine
ContactsContract.RawContacts
-Zeile ohneContactsContract.CommonDataKinds.StructuredName
-Zeile in derContactsContract.Data
-Tabelle kann bei der Aggregation zu Problemen führen. -
Verknüpfen Sie neue
ContactsContract.Data
-Zeilen immer mit der übergeordnetenContactsContract.RawContacts
-Zeile. -
Eine
ContactsContract.Data
-Zeile, die nicht mit einerContactsContract.RawContacts
verknüpft ist, wird in der Kontakte-App des Geräts nicht angezeigt und kann Probleme mit Synchronisatoren verursachen. - Ändern Sie nur Daten für Rohkontakte, deren Inhaber Sie sind.
- Denken Sie daran, dass der Kontaktanbieter in der Regel Daten aus mehreren verschiedenen Kontotypen/Onlinediensten verwaltet. Sie müssen dafür sorgen, dass Ihre Anwendung nur Daten für Zeilen ändert oder löscht, die Ihnen gehören, und dass nur Daten mit einem Kontotyp und Namen eingefügt werden, den Sie verwalten.
-
Verwenden Sie immer die in
ContactsContract
und den zugehörigen Unterklassen definierten Konstanten für Autoritäten, Inhalts-URIs, URI-Pfade, Spaltennamen, MIME-Typen undTYPE
-Werte. - Mit diesen Konstanten können Sie Fehler vermeiden. Außerdem werden Sie über Compilerwarnungen benachrichtigt, wenn eine der Konstanten eingestellt wird.
Zeilen mit benutzerdefinierten Daten
Wenn Sie eigene benutzerdefinierte MIME-Typen erstellen und verwenden, können Sie eigene Datenzeilen in die Tabelle ContactsContract.Data
einfügen, bearbeiten, löschen und abrufen. Für die Zeilen kann nur die in ContactsContract.DataColumns
definierte Spalte verwendet werden. Sie können jedoch eigene typspezifische Spaltennamen den Standardspaltennamen zuordnen. In der Kontaktanwendung des Geräts werden die Daten für Ihre Zeilen angezeigt, können aber nicht bearbeitet oder gelöscht werden. Nutzer können auch keine zusätzlichen Daten hinzufügen. Wenn Nutzer Ihre benutzerdefinierten Datenzeilen ändern sollen, müssen Sie in Ihrer eigenen Anwendung eine Editoraktivität bereitstellen.
Wenn Sie benutzerdefinierte Daten anzeigen möchten, stellen Sie eine contacts.xml
-Datei mit einem <ContactsAccountType>
-Element und einem oder mehreren seiner <ContactsDataKind>
-untergeordneten Elemente bereit. Weitere Informationen finden Sie im Abschnitt <ContactsDataKind> element
.
Weitere Informationen zu benutzerdefinierten MIME-Typen findest du im Leitfaden Inhaltsanbieter erstellen.
Synchronisierungsadapter für Kontaktdatenanbieter
Der Kontaktdatenanbieter ist speziell für die Synchronisierung von Kontaktdaten zwischen einem Gerät und einem Onlinedienst konzipiert. So können Nutzer vorhandene Daten auf ein neues Gerät herunterladen und vorhandene Daten in ein neues Konto hochladen. Durch die Synchronisierung haben Nutzer unabhängig von der Quelle der Ergänzungen und Änderungen immer die neuesten Daten zur Hand. Ein weiterer Vorteil der Synchronisierung besteht darin, dass Kontaktdaten auch dann verfügbar sind, wenn das Gerät nicht mit dem Netzwerk verbunden ist.
Sie können die Synchronisierung zwar auf verschiedene Arten implementieren, das Android-System bietet jedoch ein Plug-in-Synchronisierungs-Framework, mit dem die folgenden Aufgaben automatisiert werden:
- Netzwerkverfügbarkeit wird geprüft.
- Synchronisierung basierend auf den Nutzereinstellungen planen und ausführen
- Angehaltene Synchronisierungen werden neu gestartet.
Wenn Sie dieses Framework verwenden möchten, müssen Sie ein Synchronisierungsadapter-Plug-in bereitstellen. Jeder Synchronisierungsadapter ist für einen Dienst und einen Inhaltsanbieter eindeutig, kann aber mehrere Kontonamen für denselben Dienst verarbeiten. Das Framework ermöglicht auch mehrere Synchronisierungsadapter für denselben Dienst und Anbieter.
Adapterklassen und ‑dateien synchronisieren
Sie implementieren einen Synchronisierungsadapter als Unterklasse von AbstractThreadedSyncAdapter
und installieren ihn als Teil einer Android-Anwendung. Das System erfährt über Elemente im Manifest Ihrer Anwendung und über eine spezielle XML-Datei, auf die das Manifest verweist, von dem Synchronisierungsadapter. Die XML-Datei definiert den Kontotyp für den Onlinedienst und die Autorität für den Inhaltsanbieter, die zusammen den Adapter eindeutig identifizieren. Der Synchronisierungsadapter wird erst aktiv, wenn der Nutzer ein Konto für den Kontotyp des Synchronisierungsadapters hinzufügt und die Synchronisierung für den Inhaltsanbieter aktiviert, mit dem der Synchronisierungsadapter synchronisiert wird. Das System beginnt dann, den Adapter zu verwalten und ruft ihn bei Bedarf auf, um die Synchronisierung zwischen dem Contentanbieter und dem Server vorzunehmen.
Hinweis:Wenn Sie einen Kontotyp als Teil der Identifizierung des Synchronisierungsadapters verwenden, kann das System Synchronisierungsadapter erkennen und gruppieren, die auf verschiedene Dienste derselben Organisation zugreifen. Beispielsweise haben Synchronadapter für Google-Onlinedienste alle denselben Kontotyp com.google
. Wenn Nutzer ihren Geräten ein Google-Konto hinzufügen, werden alle installierten Synchronadapter für Google-Dienste zusammen aufgelistet. Jeder aufgeführte Synchronadapter wird mit einem anderen Inhaltsanbieter auf dem Gerät synchronisiert.
Da Nutzer bei den meisten Diensten ihre Identität bestätigen müssen, bevor sie auf Daten zugreifen können, bietet das Android-System ein Authentifizierungsframework, das dem Sync Adapter Framework ähnelt und oft in Verbindung damit verwendet wird. Das Authentifizierungs-Framework verwendet Plug-in-Authentifikatoren, die Unterklassen von AbstractAccountAuthenticator
sind. Ein Authenticator bestätigt die Identität des Nutzers in den folgenden Schritten:
- Es werden der Name, das Passwort oder ähnliche Informationen des Nutzers (die Anmeldedaten des Nutzers) erhoben.
- Sendet die Anmeldedaten an den Dienst
- Prüft die Antwort des Dienstes.
Wenn der Dienst die Anmeldedaten akzeptiert, kann der Authenticator die Anmeldedaten für später speichern. Dank des Plug-in-Authentifizierungs-Frameworks kann die AccountManager
Zugriff auf alle Authentifizierungstokens gewähren, die ein Authentifikator unterstützt und freigibt, z. B. OAuth2-Authentifizierungstokens.
Die Authentifizierung ist zwar nicht erforderlich, wird aber von den meisten Kontaktdiensten verwendet. Sie müssen jedoch nicht das Android-Authentifizierungsframework für die Authentifizierung verwenden.
Implementierung des Synchronisierungsadapters
Wenn Sie einen Synchronisierungsadapter für den Kontaktdatenanbieter implementieren möchten, erstellen Sie zuerst eine Android-Anwendung mit den folgenden Elementen:
-
Eine
Service
-Komponente, die auf Anfragen des Systems reagiert, um eine Bindung an den Synchronisierungsadapter vorzunehmen. -
Wenn das System eine Synchronisierung ausführen möchte, ruft es die
onBind()
-Methode des Dienstes auf, um eineIBinder
für den Synchronisierungsadapter abzurufen. So kann das System prozessübergreifende Aufrufe der Methoden des Adapters ausführen. -
Der eigentliche Synchronisierungsadapter, der als konkrete Unterklasse von
AbstractThreadedSyncAdapter
implementiert ist. -
Diese Klasse lädt Daten vom Server herunter, lädt Daten vom Gerät hoch und behebt Konflikte. Die Hauptarbeit des Adapters wird in der Methode
onPerformSync()
ausgeführt. Diese Klasse muss als Singleton instanziiert werden. -
Eine abgeleitete Klasse von
Application
. -
Diese Klasse dient als Fabrik für das Sync-Adapter-Singleton. Verwenden Sie die Methode
onCreate()
, um den Synchronisierungsadapter zu instanziieren, und geben Sie eine statische „Getter“-Methode an, um das Singleton an dieonBind()
-Methode des Synchronisierungsadapter-Dienstes zurückzugeben. -
Optional:Eine
Service
-Komponente, die auf Anfragen des Systems zur Nutzerauthentifizierung reagiert. -
AccountManager
startet diesen Dienst, um den Authentifizierungsprozess zu starten. Mit deronCreate()
-Methode des Dienstes wird ein Authenticator-Objekt erstellt. Wenn das System ein Nutzerkonto für den Synchronisierungsadapter der Anwendung authentifizieren möchte, ruft es dieonBind()
-Methode des Dienstes auf, um eineIBinder
für den Authenticator abzurufen. So kann das System prozessübergreifende Aufrufe der Methoden des Authenticators ausführen. -
Optional:Eine konkrete Unterklasse von
AbstractAccountAuthenticator
, die Anfragen zur Authentifizierung verarbeitet. -
Diese Klasse bietet Methoden, die vom
AccountManager
aufgerufen werden, um die Anmeldedaten des Nutzers beim Server zu authentifizieren. Die Details des Authentifizierungsverfahrens variieren je nach verwendeter Servertechnologie stark. Weitere Informationen zur Authentifizierung finden Sie in der Dokumentation Ihrer Serversoftware. - XML-Dateien, die den Synchronisierungsadapter und den Authenticator für das System definieren.
-
Die zuvor beschriebenen Komponenten für den Synchronisierungsadapter und den Authentifizierungsdienst werden im Anwendungsmanifest in
<service>
-Elementen definiert. Diese Elemente enthalten untergeordnete<meta-data>
-Elemente, die dem System bestimmte Daten zur Verfügung stellen:-
Das Element
<meta-data>
für den Synchronisierungsadapterdienst verweist auf die XML-Dateires/xml/syncadapter.xml
. Diese Datei gibt wiederum einen URI für den Webdienst an, der mit dem Kontaktdatenanbieter synchronisiert wird, sowie einen Kontotyp für den Webdienst. -
Optional:Das Element
<meta-data>
für den Authenticator verweist auf die XML-Dateires/xml/authenticator.xml
. In dieser Datei wird der Kontotyp angegeben, den dieser Authenticator unterstützt, sowie die UI-Ressourcen, die während des Authentifizierungsprozesses angezeigt werden. Der in diesem Element angegebene Kontotyp muss mit dem für den Synchronisierungsadapter angegebenen Kontotyp übereinstimmen.
-
Das Element
Daten aus sozialen Netzwerken
In den Tabellen „android.provider.ContactsContract.StreamItems“ und „android.provider.ContactsContract.StreamItemPhotos“ werden eingehende Daten aus sozialen Netzwerken verwaltet. Sie können einen Synchronisierungsadapter schreiben, der diesen Tabellen Streamdaten aus Ihrem eigenen Netzwerk hinzufügt, oder Streamdaten aus diesen Tabellen lesen und in Ihrer eigenen Anwendung anzeigen. Mit diesen Funktionen können Ihre Dienste und Anwendungen für soziale Netzwerke in die sozialen Netzwerke von Android eingebunden werden.
Text im Social-Media-Stream
Streamelemente sind immer mit einem Rohkontakt verknüpft. Der Wert von android.provider.ContactsContract.StreamItemsColumns#RAW_CONTACT_ID ist mit dem Wert _ID
für den Rohkontakt verknüpft. Der Kontotyp und der Kontoname des Rohkontakts werden ebenfalls in der Zeile des Streamelements gespeichert.
Speichern Sie die Daten aus Ihrem Stream in den folgenden Spalten:
- android.provider.ContactsContract.StreamItemsColumns#ACCOUNT_TYPE
- Erforderlich. Der Kontotyp des Nutzers für den Rohkontakt, der mit diesem Streamelement verknüpft ist. Achten Sie darauf, diesen Wert festzulegen, wenn Sie ein Streamelement einfügen.
- android.provider.ContactsContract.StreamItemsColumns#ACCOUNT_NAME
- Erforderlich. Der Kontoname des Nutzers für den Rohkontakt, der mit diesem Streamelement verknüpft ist. Achten Sie darauf, diesen Wert festzulegen, wenn Sie ein Streamelement einfügen.
- ID-Spalten
-
Erforderlich. Wenn du ein Streamelement einfügst, musst du die folgenden Spalten mit IDs einfügen:
- android.provider.ContactsContract.StreamItemsColumns#CONTACT_ID: Der Wert von android.provider.BaseColumns#_ID des Kontakts, mit dem dieses Streamelement verknüpft ist.
- android.provider.ContactsContract.StreamItemsColumns#CONTACT_LOOKUP_KEY: Der Wert von android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY des Kontakts, mit dem dieses Streamelement verknüpft ist.
- android.provider.ContactsContract.StreamItemsColumns#RAW_CONTACT_ID: Der Wert von android.provider.BaseColumns#_ID des Rohkontakts, mit dem dieses Streamelement verknüpft ist.
- android.provider.ContactsContract.StreamItemsColumns#COMMENTS
- Optional. Hier werden Zusammenfassungsinformationen gespeichert, die am Anfang eines Streamelements angezeigt werden können.
- android.provider.ContactsContract.StreamItemsColumns#TEXT
-
Der Text des Streamelements, entweder der Inhalt, der von der Quelle des Elements gepostet wurde, oder eine Beschreibung einer Aktion, die das Streamelement generiert hat. Diese Spalte kann beliebige Formatierungen und eingebettete Ressourcenbilder enthalten, die von
fromHtml()
gerendert werden können. Der Anbieter kann lange Inhalte abschneiden oder aussparen, versucht aber, Trenn-Tags zu vermeiden. - android.provider.ContactsContract.StreamItemsColumns#TIMESTAMP
- Ein Textstring, der die Zeit enthält, zu der das Streamelement eingefügt oder aktualisiert wurde, in Form von Millisekunden seit der Epoche. Für die Pflege dieser Spalte sind die Anwendungen verantwortlich, die Streamelemente einfügen oder aktualisieren. Sie wird nicht automatisch vom Kontaktdatenanbieter verwaltet.
Wenn Sie identifizierende Informationen für Ihre Streamelemente anzeigen möchten, verwenden Sie die Spalten „android.provider.ContactsContract.StreamItemsColumns#RES_ICON“, „android.provider.ContactsContract.StreamItemsColumns#RES_LABEL“ und „android.provider.ContactsContract.StreamItemsColumns#RES_PACKAGE“, um Verknüpfungen zu Ressourcen in Ihrer Anwendung herzustellen.
Die Tabelle „android.provider.ContactsContract.StreamItems“ enthält außerdem die Spalten „android.provider.ContactsContract.StreamItemsColumns#SYNC1“ bis „android.provider.ContactsContract.StreamItemsColumns#SYNC4“, die ausschließlich für Synchronadapter verwendet werden.
Fotos aus dem Social Stream
In der Tabelle „android.provider.ContactsContract.StreamItemPhotos“ werden Fotos gespeichert, die mit einem Streamelement verknüpft sind. Die Spalte „android.provider.ContactsContract.StreamItemPhotosColumns#STREAM_ITEM_ID“ der Tabelle verweist auf Werte in der Spalte _ID
der Tabelle „android.provider.ContactsContract.StreamItems“. Fotoreferenzen werden in der Tabelle in den folgenden Spalten gespeichert:
- Spalte „PHOTO“ (BLOB) der Tabelle „android.provider.ContactsContract.StreamItemPhotos#PHOTO“
- Eine binäre Darstellung des Fotos, die vom Anbieter für die Speicherung und Anzeige neu skaliert wurde. Diese Spalte ist für die Abwärtskompatibilität mit früheren Versionen des Kontaktdatenanbieters verfügbar, in denen sie zum Speichern von Fotos verwendet wurde. In der aktuellen Version sollten Sie diese Spalte jedoch nicht zum Speichern von Fotos verwenden. Verwenden Sie stattdessen entweder android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_FILE_ID oder android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_URI (beide werden in den folgenden Punkten beschrieben), um Fotos in einer Datei zu speichern. Diese Spalte enthält jetzt ein Thumbnail des Fotos, das gelesen werden kann.
- android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_FILE_ID
-
Eine numerische Kennung eines Fotos für einen Rohkontakt. Füge diesen Wert der Konstante
DisplayPhoto.CONTENT_URI
hinzu, um einen Inhalts-URI zu erhalten, der auf eine einzelne Fotodatei verweist, und rufe dannopenAssetFileDescriptor()
auf, um einen Handle für die Fotodatei zu erhalten. - android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_URI
-
Ein Inhalts-URI, der direkt auf die Fotodatei für das Foto verweist, das durch diese Zeile dargestellt wird.
Rufen Sie
openAssetFileDescriptor()
mit diesem URI auf, um einen Handle für die Fotodatei zu erhalten.
Tabellen für den Social Stream verwenden
Diese Tabellen funktionieren genauso wie die anderen Haupttabellen im Kontaktdatenanbieter, mit folgenden Ausnahmen:
- Für diese Tabellen sind zusätzliche Zugriffsberechtigungen erforderlich. Damit Ihre App Daten aus diesen Streams lesen kann, benötigt sie die Berechtigung „android.Manifest.permission#READ_SOCIAL_STREAM“. Wenn Sie sie ändern möchten, muss Ihre Anwendung die Berechtigung „android.Manifest.permission#WRITE_SOCIAL_STREAM“ haben.
-
Für die Tabelle „android.provider.ContactsContract.StreamItems“ ist die Anzahl der Zeilen, die für jeden Rohkontakt gespeichert werden, begrenzt. Wenn dieses Limit erreicht ist, schafft der Contacts-Anbieter Platz für neue Streamelementzeilen, indem er automatisch die Zeilen mit dem ältesten TIMESTAMP löscht. Um das Limit abzurufen, stellen Sie eine Abfrage an den Inhalts-URI android.provider.ContactsContract.StreamItems#CONTENT_LIMIT_URI. Du kannst alle Argumente außer dem Inhalts-URI auf
null
belassen. Die Abfrage gibt einen Cursor mit einer einzelnen Zeile und der einzelnen Spalte „android.provider.ContactsContract.StreamItems#MAX_ITEMS“ zurück.
Die Klasse android.provider.ContactsContract.StreamItems.StreamItemPhotos definiert eine untergeordnete Tabelle von android.provider.ContactsContract.StreamItemPhotos, die die Fotozeilen für ein einzelnes Streamelement enthält.
Interaktionen im Social Stream
Die vom Kontaktdatenanbieter verwalteten Daten des Social Streams in Verbindung mit der Kontaktanwendung des Geräts bieten eine leistungsstarke Möglichkeit, Ihr Social-Networking-System mit vorhandenen Kontakten zu verknüpfen. Die folgenden Funktionen sind verfügbar:
- Wenn Sie Ihren Dienst für soziale Netzwerke mit einem Synchronisierungsadapter mit dem Kontaktdatenanbieter synchronisieren, können Sie die letzten Aktivitäten für die Kontakte eines Nutzers abrufen und zur späteren Verwendung in den Tabellen „android.provider.ContactsContract.StreamItems“ und „android.provider.ContactsContract.StreamItemPhotos“ speichern.
- Neben der regelmäßigen Synchronisierung können Sie Ihren Synchronisierungsadapter so konfigurieren, dass zusätzliche Daten abgerufen werden, wenn der Nutzer einen Kontakt zum Ansehen auswählt. So kann Ihr Synchronisierungsadapter hochauflösende Fotos und die neuesten Streamelemente für den Kontakt abrufen.
- Wenn Sie eine Benachrichtigung in der Kontaktanwendung des Geräts und beim Kontaktanbieter registrieren, können Sie eine Intent erhalten, wenn ein Kontakt aufgerufen wird, und den Status des Kontakts dann über Ihren Dienst aktualisieren. Dieser Ansatz ist möglicherweise schneller und benötigt weniger Bandbreite als eine vollständige Synchronisierung mit einem Synchronisierungsadapter.
- Nutzer können Ihrem sozialen Netzwerk einen Kontakt hinzufügen, während sie sich den Kontakt in der Kontakte-App des Geräts ansehen. Sie aktivieren diese Funktion mit der Funktion „Kontakt einladen“. Dazu kombinieren Sie eine Aktivität, durch die ein vorhandener Kontakt zu Ihrem Netzwerk hinzugefügt wird, mit einer XML-Datei, die der Kontaktanwendung des Geräts und dem Kontaktdatenanbieter die Details Ihrer Anwendung zur Verfügung stellt.
Die regelmäßige Synchronisierung von Streamelementen mit dem Kontaktanbieter erfolgt wie bei anderen Synchronisierungen. Weitere Informationen zur Synchronisierung finden Sie im Abschnitt Synchronadapter für Kontaktdatenanbieter. Das Registrieren von Benachrichtigungen und das Einladen von Kontakten wird in den nächsten beiden Abschnitten behandelt.
Registrierung für die Verarbeitung von Aufrufen in sozialen Netzwerken
So registrieren Sie Ihren Synchronisierungsadapter, um Benachrichtigungen zu erhalten, wenn der Nutzer einen Kontakt aufruft, der von Ihrem Synchronisierungsadapter verwaltet wird:
-
Erstellen Sie im Verzeichnis
res/xml/
Ihres Projekts eine Datei mit dem Namencontacts.xml
. Wenn Sie diese Datei bereits haben, können Sie diesen Schritt überspringen. -
Fügen Sie in dieser Datei das Element
<ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android">
hinzu. Wenn dieses Element bereits vorhanden ist, können Sie diesen Schritt überspringen. -
Wenn Sie einen Dienst registrieren möchten, der benachrichtigt wird, wenn der Nutzer die Detailseite eines Kontakts in der Kontaktanwendung des Geräts öffnet, fügen Sie dem Element das Attribut
viewContactNotifyService="serviceclass"
hinzu. Dabei istserviceclass
die vollqualifizierte Klassenname des Dienstes, der die Intent-Anfrage von der Kontaktanwendung des Geräts erhalten soll. Verwenden Sie für den Benachrichtigungsdienst eine Klasse, dieIntentService
erweitert, damit der Dienst Intents empfangen kann. Die Daten im eingehenden Intent enthalten den Content-URI der Kontaktperson, auf die der Nutzer geklickt hat. Über den Benachrichtigungsdienst können Sie Ihren Synchronisierungsadapter binden und dann aufrufen, um die Daten für den Rohkontakt zu aktualisieren.
So registrierst du eine Aktivität, die aufgerufen werden soll, wenn der Nutzer auf ein Streamelement, ein Foto oder beides klickt:
-
Erstellen Sie im Verzeichnis
res/xml/
Ihres Projekts eine Datei mit dem Namencontacts.xml
. Wenn Sie diese Datei bereits haben, können Sie diesen Schritt überspringen. -
Fügen Sie in dieser Datei das Element
<ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android">
hinzu. Wenn dieses Element bereits vorhanden ist, können Sie diesen Schritt überspringen. -
Wenn Sie eine Ihrer Aktivitäten registrieren möchten, um zu verarbeiten, wenn der Nutzer in der Kontakte-App des Geräts auf ein Streamelement klickt, fügen Sie dem Element das Attribut
viewStreamItemActivity="activityclass"
hinzu. Dabei istactivityclass
die voll qualifizierte Klassenname der Aktivität, die die Intent-Nachricht von der Kontakte-App des Geräts erhalten soll. -
Wenn Sie eine Ihrer Aktivitäten registrieren möchten, um zu verarbeiten, wenn der Nutzer in der Kontakte-App des Geräts auf ein Stream-Foto klickt, fügen Sie dem Element das Attribut
viewStreamItemPhotoActivity="activityclass"
hinzu. Dabei istactivityclass
die voll qualifizierte Klassenname der Aktivität, die die Intent-Aktion von der Kontakte-App des Geräts erhalten soll.
Das Element <ContactsAccountType>
wird im Abschnitt <ContactsAccountType>-Element ausführlicher beschrieben.
Die eingehende Intent-Nachricht enthält den Inhalts-URI des Artikels oder Fotos, auf das der Nutzer geklickt hat. Wenn Sie separate Aktivitäten für Textelemente und Fotos haben möchten, verwenden Sie beide Attribute in derselben Datei.
Interaktionen mit Ihrem sozialen Netzwerk
Nutzer müssen die Kontakte-App des Geräts nicht verlassen, um einen Kontakt zu Ihrer Social-Networking-Website einzuladen. Stattdessen können Sie die Kontakte-App des Geräts so konfigurieren, dass sie eine Intent sendet, um den Kontakt zu einer Ihrer Aktivitäten einzuladen. So richtest du ein Konto mit Elternaufsicht ein:
-
Erstellen Sie im Verzeichnis
res/xml/
Ihres Projekts eine Datei mit dem Namencontacts.xml
. Wenn Sie diese Datei bereits haben, können Sie diesen Schritt überspringen. -
Fügen Sie in dieser Datei das Element
<ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android">
hinzu. Wenn dieses Element bereits vorhanden ist, können Sie diesen Schritt überspringen. -
Fügen Sie die folgenden Attribute hinzu:
inviteContactActivity="activityclass"
-
inviteContactActionLabel="@string/invite_action_label"
activityclass
ist der voll qualifizierte Klassenname der Aktivität, die die Absicht erhalten soll. Der Wertinvite_action_label
ist ein Textstring, der in der Kontakte-App des Geräts im Menü Verbindung hinzufügen angezeigt wird.
Hinweis:ContactsSource
ist ein veralteter Tag-Name für ContactsAccountType
.
Referenz zu contacts.xml
Die Datei contacts.xml
enthält XML-Elemente, die die Interaktion Ihres Synchronisierungsadapters und Ihrer Anwendung mit der Kontaktanwendung und dem Kontaktdatenanbieter steuern. Diese Elemente werden in den folgenden Abschnitten beschrieben.
<ContactsAccountType>-Element
Das Element <ContactsAccountType>
steuert die Interaktion Ihrer Anwendung mit der Kontakte-App. Die Syntax lautet:
<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">
enthalten in:
res/xml/contacts.xml
kann Folgendes enthalten:
<ContactsDataKind>
Beschreibung:
Hiermit werden Android-Komponenten und UI-Labels deklariert, mit denen Nutzer einen ihrer Kontakte zu einem sozialen Netzwerk einladen, Nutzer benachrichtigen können, wenn einer ihrer Streams in sozialen Netzwerken aktualisiert wird usw.
Das Attributpräfix android:
ist für die Attribute von <ContactsAccountType>
nicht erforderlich.
Attribute:
inviteContactActivity
- Der vollqualifizierte Klassenname der Aktivität in Ihrer Anwendung, die aktiviert werden soll, wenn der Nutzer in der Kontaktanwendung des Geräts Verbindung hinzufügen auswählt.
inviteContactActionLabel
-
Ein Textstring, der für die in
inviteContactActivity
angegebene Aktivität im Menü Verbindung hinzufügen angezeigt wird. Sie können beispielsweise den String „In meinem Netzwerk folgen“ verwenden. Sie können für dieses Label eine Stringressourcen-ID verwenden. viewContactNotifyService
- Der vollständig qualifizierte Klassenname eines Dienstes in Ihrer Anwendung, der Benachrichtigungen erhalten soll, wenn sich der Nutzer einen Kontakt ansieht. Diese Benachrichtigung wird von der Kontakte-App des Geräts gesendet. So kann Ihre Anwendung datenintensive Vorgänge bis zur tatsächlichen Verwendung verschieben. Ihre App kann beispielsweise auf diese Benachrichtigung reagieren, indem sie das hochauflösende Foto des Kontakts und die neuesten Elemente aus dem Social-Media-Stream einliest und anzeigt. Diese Funktion wird im Abschnitt Interaktionen im Social Stream ausführlicher beschrieben.
viewGroupActivity
- Der vollständig qualifizierte Klassenname einer Aktivität in Ihrer Anwendung, für die Gruppeninformationen angezeigt werden können. Wenn der Nutzer in der Kontakte-App des Geräts auf das Gruppenlabel klickt, wird die Benutzeroberfläche für diese Aktivität angezeigt.
viewGroupActionLabel
-
Das Label, das die Kontaktanwendung für ein Steuerelement der Benutzeroberfläche anzeigt, mit dem sich Nutzer Gruppen in Ihrer Anwendung ansehen können.
Für dieses Attribut ist eine Stringressourcen-ID zulässig.
viewStreamItemActivity
- Der voll qualifizierte Klassenname einer Aktivität in Ihrer Anwendung, die die Kontakte-App des Geräts startet, wenn der Nutzer auf ein Streamelement für einen Rohkontakt klickt.
viewStreamItemPhotoActivity
- Der voll qualifizierte Klassenname einer Aktivität in Ihrer Anwendung, die die Kontakte-App des Geräts startet, wenn der Nutzer auf ein Foto im Streamelement für einen Rohkontakt klickt.
<ContactsDataKind>-Element
Mit dem Element <ContactsDataKind>
wird die Anzeige der benutzerdefinierten Datenzeilen Ihrer Anwendung in der Benutzeroberfläche der Kontakte-App gesteuert. Die Syntax lautet:
<ContactsDataKind android:mimeType="MIMEtype" android:icon="icon_resources" android:summaryColumn="column_name" android:detailColumn="column_name">
enthalten in:
<ContactsAccountType>
Beschreibung:
Mit diesem Element können Sie festlegen, dass die Kontakte App den Inhalt einer benutzerdefinierten Datenzeile als Teil der Details eines Rohkontakts anzeigt. Jedes untergeordnete Element <ContactsDataKind>
von <ContactsAccountType>
steht für eine Art benutzerdefinierter Datenzeile, die der Synchronisierungsadapter der Tabelle ContactsContract.Data
hinzufügt. Fügen Sie für jeden verwendeten benutzerdefinierten MIME-Typ ein <ContactsDataKind>
-Element hinzu. Sie müssen das Element nicht hinzufügen, wenn Sie eine benutzerdefinierte Datenzeile haben, für die keine Daten angezeigt werden sollen.
Attribute:
android:mimeType
-
Der benutzerdefinierte MIME-Typ, den Sie für einen der benutzerdefinierten Datenzeilentypen in der Tabelle
ContactsContract.Data
definiert haben. Der Wertvnd.android.cursor.item/vnd.example.locationstatus
kann beispielsweise ein benutzerdefinierter MIME-Typ für eine Datenzeile sein, in der der letzte bekannte Standort eines Kontakts gespeichert ist. android:icon
- Eine Android-Zeichnen-Ressource, die in der Kontakte App neben Ihren Daten angezeigt wird. Hiermit weisen Sie den Nutzer darauf hin, dass die Daten aus Ihrem Dienst stammen.
android:summaryColumn
- Der Spaltenname für den ersten von zwei Werten, die aus der Datenzeile abgerufen wurden. Der Wert wird als erste Zeile des Eintrags für diese Datenzeile angezeigt. Die erste Zeile dient als Zusammenfassung der Daten, ist aber optional. Weitere Informationen finden Sie unter android:detailColumn.
android:detailColumn
-
Der Name der Spalte für den zweiten von zwei Werten, die aus der Datenzeile abgerufen wurden. Der Wert wird als zweite Zeile des Eintrags für diese Datenzeile angezeigt. Siehe auch
android:summaryColumn
.
Zusätzliche Funktionen des Contacts Providers
Neben den in den vorherigen Abschnitten beschriebenen Hauptfunktionen bietet der Kontaktdatenanbieter folgende nützliche Funktionen für die Arbeit mit Kontaktdaten:
- Kontaktgruppen
- Fotofunktionen
Kontaktgruppen
Der Kontaktdatenanbieter kann Sammlungen ähnlicher Kontakte optional mit Gruppendaten labeln. Wenn der mit einem Nutzerkonto verknüpfte Server Gruppen verwalten möchte, sollte der Synchronisierungsadapter für den Kontotyp des Kontos Gruppendaten zwischen dem Kontaktdatenanbieter und dem Server übertragen. Wenn Nutzer dem Server einen neuen Kontakt hinzufügen und diesen Kontakt dann einer neuen Gruppe zuweisen, muss der Synchronisierungsadapter die neue Gruppe der Tabelle ContactsContract.Groups
hinzufügen. Die Gruppe oder Gruppen, zu denen ein Rohkontakt gehört, werden in der Tabelle ContactsContract.Data
mit dem MIME-Typ ContactsContract.CommonDataKinds.GroupMembership
gespeichert.
Wenn Sie einen Synchronisierungsadapter entwerfen, der dem Kontaktdatenanbieter Rohkontaktdaten vom Server hinzufügt, und keine Gruppen verwenden, müssen Sie dem Anbieter mitteilen, dass Ihre Daten sichtbar gemacht werden sollen. Aktualisieren Sie im Code, der ausgeführt wird, wenn ein Nutzer dem Gerät ein Konto hinzufügt, die Zeile ContactsContract.Settings
, die der Kontaktanbieter für das Konto hinzufügt. Legen Sie in dieser Zeile den Wert der Spalte Settings.UNGROUPED_VISIBLE
auf 1 fest. In diesem Fall macht der Kontaktanbieter Ihre Kontaktdaten immer sichtbar, auch wenn Sie keine Gruppen verwenden.
Kontaktfotos
In der Tabelle ContactsContract.Data
werden Fotos als Zeilen mit dem MIME-Typ Photo.CONTENT_ITEM_TYPE
gespeichert. Die Spalte CONTACT_ID
der Zeile ist mit der Spalte _ID
des Rohkontakts verknüpft, zu dem sie gehört.
Die Klasse ContactsContract.Contacts.Photo
definiert eine untergeordnete Tabelle von ContactsContract.Contacts
, die Fotoinformationen für das Hauptfoto eines Kontakts enthält. Das ist das Hauptfoto des primären Rohkontakts des Kontakts. Ähnlich definiert die Klasse ContactsContract.RawContacts.DisplayPhoto
eine untergeordnete Tabelle von ContactsContract.RawContacts
, die Fotoinformationen für das Hauptfoto eines Rohkontakts enthält.
Die Referenzdokumentation zu ContactsContract.Contacts.Photo
und ContactsContract.RawContacts.DisplayPhoto
enthält Beispiele zum Abrufen von Fotoinformationen. Es gibt keine praktische Klasse zum Abrufen des primären Thumbnails für einen Rohkontakt. Sie können jedoch eine Abfrage an die Tabelle ContactsContract.Data
senden und die Spalten _ID
, Photo.CONTENT_ITEM_TYPE
und IS_PRIMARY
auswählen, um die Zeile mit dem primären Foto des Rohkontakts zu finden.
Social-Stream-Daten für eine Person können auch Fotos enthalten. Sie werden in der Tabelle „android.provider.ContactsContract.StreamItemPhotos“ gespeichert, die im Abschnitt Fotos im Social Stream ausführlicher beschrieben wird.