Details zu einem Kontakt abrufen

In dieser Lektion erfahren Sie, wie Sie Detaildaten für einen Kontakt abrufen, z. B. E-Mail-Adressen und Telefonnummern. Es sind die Details, nach denen Nutzer beim Abrufen eines Kontakts suchen. Sie können alle Details zu einem Kontakt angeben oder nur Details eines bestimmten Typs anzeigen, z. B. E-Mail-Adressen.

Bei den Schritten in dieser Lektion wird davon ausgegangen, dass Sie bereits eine ContactsContract.Contacts-Zeile für einen Kontakt haben, den der Nutzer ausgewählt hat. In der Lektion Kontaktnamen abrufen erfahren Sie, wie Sie eine Liste von Kontakten abrufen.

Alle Details zu einem Kontakt abrufen

Wenn Sie alle Details zu einem Kontakt abrufen möchten, suchen Sie in der Tabelle ContactsContract.Data nach Zeilen, die den LOOKUP_KEY des Kontakts enthalten. Diese Spalte ist in der Tabelle ContactsContract.Data verfügbar, da der Kontaktanbieter einen impliziten Join zwischen der Tabelle ContactsContract.Contacts und der Tabelle ContactsContract.Data vornimmt. Die Spalte LOOKUP_KEY wird in der Lektion Kontaktnamen abrufen ausführlicher beschrieben.

Hinweis: Das Abrufen aller Details zu einem Kontakt reduziert die Leistung eines Geräts, da dafür alle Spalten in der Tabelle ContactsContract.Data abgerufen werden müssen. Berücksichtigen Sie die Auswirkungen auf die Leistung, bevor Sie dieses Verfahren verwenden.

Berechtigungen anfordern

Zum Lesen aus dem Contacts Provider muss Ihre App die Berechtigung READ_CONTACTS haben. Fügen Sie der Manifestdatei das folgende untergeordnete Element von <manifest> hinzu, um diese Berechtigung anzufordern:

    <uses-permission android:name="android.permission.READ_CONTACTS" />

Projektion einrichten

Je nach Datentyp einer Zeile werden möglicherweise nur wenige oder viele Spalten verwendet. Außerdem befinden sich die Daten je nach Datentyp in verschiedenen Spalten. Damit Sie alle möglichen Spalten für alle möglichen Datentypen erhalten, müssen Sie Ihrer Projektion alle Spaltennamen hinzufügen. Rufen Sie immer Data._ID ab, wenn Sie das Ergebnis-Cursor an eine ListView binden. Andernfalls funktioniert die Bindung nicht. Rufen Sie auch Data.MIMETYPE ab, damit Sie den Datentyp jeder abgerufenen Zeile identifizieren können. Beispiele:

Kotlin

private val PROJECTION: Array<out String> = arrayOf(
        ContactsContract.Data._ID,
        ContactsContract.Data.MIMETYPE,
        ContactsContract.Data.DATA1,
        ContactsContract.Data.DATA2,
        ContactsContract.Data.DATA3,
        ContactsContract.Data.DATA4,
        ContactsContract.Data.DATA5,
        ContactsContract.Data.DATA6,
        ContactsContract.Data.DATA7,
        ContactsContract.Data.DATA8,
        ContactsContract.Data.DATA9,
        ContactsContract.Data.DATA10,
        ContactsContract.Data.DATA11,
        ContactsContract.Data.DATA12,
        ContactsContract.Data.DATA13,
        ContactsContract.Data.DATA14,
        ContactsContract.Data.DATA15
)

Java

    private static final String[] PROJECTION =
            {
                ContactsContract.Data._ID,
                ContactsContract.Data.MIMETYPE,
                ContactsContract.Data.DATA1,
                ContactsContract.Data.DATA2,
                ContactsContract.Data.DATA3,
                ContactsContract.Data.DATA4,
                ContactsContract.Data.DATA5,
                ContactsContract.Data.DATA6,
                ContactsContract.Data.DATA7,
                ContactsContract.Data.DATA8,
                ContactsContract.Data.DATA9,
                ContactsContract.Data.DATA10,
                ContactsContract.Data.DATA11,
                ContactsContract.Data.DATA12,
                ContactsContract.Data.DATA13,
                ContactsContract.Data.DATA14,
                ContactsContract.Data.DATA15
            };

Diese Projektion ruft alle Spalten einer Zeile in der Tabelle ContactsContract.Data ab. Dabei werden die in der Klasse ContactsContract.Data definierten Spaltennamen verwendet.

Optional können Sie auch alle anderen Spaltenkonstanten verwenden, die in der Klasse ContactsContract.Data definiert oder von ihr übernommen wurden. Beachten Sie jedoch, dass die Spalten SYNC1 bis SYNC4 von Synchronisierungsadaptern verwendet werden sollen, sodass deren Daten nicht hilfreich sind.

Auswahlkriterien definieren

Definieren Sie eine Konstante für die Auswahlklausel, ein Array für die Auswahlargumente und eine Variable für den Auswahlwert. Verwenden Sie die Spalte Contacts.LOOKUP_KEY, um den Kontakt zu finden. Beispiele:

Kotlin

// Defines the selection clause
private const val SELECTION: String = "${ContactsContract.Data.LOOKUP_KEY} = ?"
...
// Defines the array to hold the search criteria
private val selectionArgs: Array<String> = arrayOf("")
/*
 * Defines a variable to contain the selection value. Once you
 * have the Cursor from the Contacts table, and you've selected
 * the desired row, move the row's LOOKUP_KEY value into this
 * variable.
 */
private var lookupKey: String? = null

Java

    // Defines the selection clause
    private static final String SELECTION = Data.LOOKUP_KEY + " = ?";
    // Defines the array to hold the search criteria
    private String[] selectionArgs = { "" };
    /*
     * Defines a variable to contain the selection value. Once you
     * have the Cursor from the Contacts table, and you've selected
     * the desired row, move the row's LOOKUP_KEY value into this
     * variable.
     */
    private lateinit var lookupKey: String

Wenn Sie „?“ als Platzhalter in Ihrem Auswahltextausdruck verwenden, wird die resultierende Suche durch Bindung und nicht durch SQL-Kompilierung generiert. Dieser Ansatz eliminiert die Möglichkeit einer schädlichen SQL-Einschleusung.

Sortierreihenfolge definieren

Definieren Sie die gewünschte Sortierreihenfolge für die Ergebnis-Cursor. Damit alle Zeilen für einen bestimmten Datentyp zusammengehalten werden, sortieren Sie nach Data.MIMETYPE. Dieses Abfrageargument gruppiert alle E-Mail-Zeilen, alle Telefonzeilen usw. Beispiele:

Kotlin

/*
 * Defines a string that specifies a sort order of MIME type
 */
private const val SORT_ORDER = ContactsContract.Data.MIMETYPE

Java

    /*
     * Defines a string that specifies a sort order of MIME type
     */
    private static final String SORT_ORDER = ContactsContract.Data.MIMETYPE;

Hinweis:Bei einigen Datentypen wird kein Untertyp verwendet. Sie können also nicht nach Untertyp sortieren. Stattdessen müssen Sie die zurückgegebenen Cursor durchlaufen, den Datentyp der aktuellen Zeile ermitteln und Daten für Zeilen speichern, die einen Untertyp verwenden. Nachdem Sie den Cursor gelesen haben, können Sie jeden Datentyp nach Untertyp sortieren und die Ergebnisse anzeigen lassen.

Loader initialisieren

Führen Sie Abrufe von Contact Provider (und allen anderen Contentanbietern) immer in einem Hintergrundthread aus. Verwenden Sie das von der Klasse LoaderManager definierte Lade-Framework und die LoaderManager.LoaderCallbacks-Schnittstelle, um Hintergrundabfragen auszuführen.

Wenn Sie die Zeilen abrufen möchten, initialisieren Sie das Lade-Framework, indem Sie initLoader() aufrufen. Übergeben Sie eine Ganzzahl-ID an die Methode, die dann an LoaderManager.LoaderCallbacks-Methoden übergeben wird. Mithilfe der ID können Sie mehrere Loader in einer App verwenden und zwischen ihnen unterscheiden.

Das folgende Snippet zeigt, wie das Lade-Framework initialisiert wird:

Kotlin

// Defines a constant that identifies the loader
private const val DETAILS_QUERY_ID: Int = 0

class DetailsFragment : Fragment(), LoaderManager.LoaderCallbacks<Cursor> {
    ...
    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        // Initializes the loader framework
        loaderManager.initLoader(DETAILS_QUERY_ID, null, this)

Java

public class DetailsFragment extends Fragment implements
        LoaderManager.LoaderCallbacks<Cursor> {
    ...
    // Defines a constant that identifies the loader
    static int DETAILS_QUERY_ID = 0;
    ...
    @Override
    public void onCreate(Bundle savedInstanceState) {
        ...
        // Initializes the loader framework
        getLoaderManager().initLoader(DETAILS_QUERY_ID, null, this);

onCreateLoader() implementieren

Implementieren Sie die Methode onCreateLoader(), die vom Lade-Framework sofort nach dem Aufruf von initLoader() aufgerufen wird. Geben Sie ein CursorLoader von dieser Methode zurück. Da Sie in der Tabelle ContactsContract.Data suchen, verwenden Sie die Konstante Data.CONTENT_URI als Inhalts-URI. Beispiele:

Kotlin

override fun onCreateLoader(loaderId: Int, args: Bundle?): Loader<Cursor> {
    // Choose the proper action
    mLoader = when(loaderId) {
        DETAILS_QUERY_ID -> {
            // Assigns the selection parameter
            selectionArgs[0] = lookupKey
            // Starts the query
            activity?.let {
                CursorLoader(
                        it,
                        ContactsContract.Data.CONTENT_URI,
                        PROJECTION,
                        SELECTION,
                        selectionArgs,
                        SORT_ORDER
                )
            }
        }
        ...
    }
    return mLoader
}

Java

@Override
    public Loader<Cursor> onCreateLoader(int loaderId, Bundle args) {
        // Choose the proper action
        switch (loaderId) {
            case DETAILS_QUERY_ID:
            // Assigns the selection parameter
            selectionArgs[0] = lookupKey;
            // Starts the query
            CursorLoader mLoader =
                    new CursorLoader(
                            getActivity(),
                            ContactsContract.Data.CONTENT_URI,
                            PROJECTION,
                            SELECTION,
                            selectionArgs,
                            SORT_ORDER
                    );
    }

onLoadFinished() und onLoaderReset() implementieren

Implementieren Sie die Methode onLoadFinished(). Das Lade-Framework ruft onLoadFinished() auf, wenn der Contacts Provider die Ergebnisse der Abfrage zurückgibt. Beispiele:

Kotlin

    override fun onLoadFinished(loader: Loader<Cursor>, data: Cursor) {
        when(loader.id) {
            DETAILS_QUERY_ID -> {
                /*
                 * Process the resulting Cursor here.
                 */
            }
            ...
        }
    }

Java

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
        switch (loader.getId()) {
            case DETAILS_QUERY_ID:
                    /*
                     * Process the resulting Cursor here.
                     */
                }
                break;
            ...
        }
    }

Die Methode onLoaderReset() wird aufgerufen, wenn das Lade-Framework erkennt, dass sich die Daten, auf denen das Ergebnis Cursor basiert, geändert haben. An dieser Stelle entfernen Sie alle vorhandenen Verweise auf die Cursor, indem Sie sie auf null setzen. Andernfalls löscht das Lade-Framework das alte Cursor nicht und es kommt zu einem Speicherleck. Beispiele:

Kotlin

    override fun onLoaderReset(loader: Loader<Cursor>) {
        when (loader.id) {
            DETAILS_QUERY_ID -> {
                /*
                 * If you have current references to the Cursor,
                 * remove them here.
                 */
            }
            ...
        }
    }

Java

    @Override
    public void onLoaderReset(Loader<Cursor> loader) {
        switch (loader.getId()) {
            case DETAILS_QUERY_ID:
                /*
                 * If you have current references to the Cursor,
                 * remove them here.
                 */
                }
                break;
    }

Bestimmte Informationen zu einem Kontakt abrufen

Der Abruf eines bestimmten Datentyps für einen Kontakt, z. B. alle E-Mails, erfolgt nach dem gleichen Muster wie das Abrufen aller Details. Sie müssen nur die folgenden Änderungen am Code vornehmen, der unter Alle Details für einen Kontakt abrufen aufgeführt ist:

Prognose
Ändern Sie die Projektion so, dass die für den Datentyp spezifischen Spalten abgerufen werden. Ändern Sie auch die Projektion so, dass die Spaltennamenkonstanten verwendet werden, die in der abgeleiteten ContactsContract.CommonDataKinds-Klasse definiert sind, die dem Datentyp entsprechen.
Auswahl
Ändern Sie den Auswahltext so, dass nach dem für Ihren Datentyp spezifischen MIMETYPE-Wert gesucht wird.
Reihenfolge
Da Sie nur einen einzelnen Detailtyp auswählen, gruppieren Sie die zurückgegebenen Cursor nicht nach Data.MIMETYPE.

Diese Änderungen werden in den folgenden Abschnitten beschrieben.

Projektion definieren

Definieren Sie die abzurufenden Spalten mithilfe der Spaltennamenkonstanten in der abgeleiteten Klasse von ContactsContract.CommonDataKinds für den Datentyp. Wenn Sie planen, Cursor an eine ListView zu binden, müssen Sie die Spalte _ID abrufen. Definieren Sie beispielsweise die folgende Projektion, um E-Mail-Daten abzurufen:

Kotlin

private val PROJECTION: Array<String> = arrayOf(
        ContactsContract.CommonDataKinds.Email._ID,
        ContactsContract.CommonDataKinds.Email.ADDRESS,
        ContactsContract.CommonDataKinds.Email.TYPE,
        ContactsContract.CommonDataKinds.Email.LABEL
)

Java

    private static final String[] PROJECTION =
            {
                ContactsContract.CommonDataKinds.Email._ID,
                ContactsContract.CommonDataKinds.Email.ADDRESS,
                ContactsContract.CommonDataKinds.Email.TYPE,
                ContactsContract.CommonDataKinds.Email.LABEL
            };

Beachten Sie, dass diese Projektion die Spaltennamen verwendet, die in der Klasse ContactsContract.CommonDataKinds.Email definiert sind, anstelle der Spaltennamen, die in der Klasse ContactsContract.Data definiert sind. Die Verwendung der E-Mail-spezifischen Spaltennamen macht den Code lesbarer.

In der Projektion können Sie auch alle anderen Spalten verwenden, die in der abgeleiteten Klasse ContactsContract.CommonDataKinds definiert sind.

Auswahlkriterien definieren

Definieren Sie einen Suchtextausdruck, der Zeilen für die LOOKUP_KEY eines bestimmten Kontakts und den Data.MIMETYPE der gewünschten Details abruft. Setzen Sie den MIMETYPE-Wert in einfache Anführungszeichen. Verketten Sie dazu ein '-Zeichen (einfaches Anführungszeichen) mit dem Anfang und Ende der Konstante. Andernfalls interpretiert der Anbieter die Konstante als Variablennamen und nicht als Stringwert. Sie müssen für diesen Wert keinen Platzhalter verwenden, da Sie eine Konstante und keinen vom Nutzer bereitgestellten Wert verwenden. Beispiele:

Kotlin

/*
 * Defines the selection clause. Search for a lookup key
 * and the Email MIME type
 */
private const val SELECTION =
        "${ContactsContract.Data.LOOKUP_KEY} = ? AND " +
        "${ContactsContract.Data.MIMETYPE} = '${Email.CONTENT_ITEM_TYPE}'"
...
// Defines the array to hold the search criteria
private val selectionArgs: Array<String> = arrayOf("")

Java

    /*
     * Defines the selection clause. Search for a lookup key
     * and the Email MIME type
     */
    private static final String SELECTION =
            Data.LOOKUP_KEY + " = ?" +
            " AND " +
            Data.MIMETYPE + " = " +
            "'" + Email.CONTENT_ITEM_TYPE + "'";
    // Defines the array to hold the search criteria
    private String[] selectionArgs = { "" };

Sortierreihenfolge definieren

Definieren Sie eine Sortierreihenfolge für die zurückgegebenen Cursor. Da Sie einen bestimmten Datentyp abrufen, lassen Sie die Sortierung bei MIMETYPE weg. Wenn der Typ der Detaildaten, nach denen Sie suchen, einen Untertyp umfasst, sortieren Sie danach. Bei E-Mail-Daten können Sie beispielsweise nach Email.TYPE sortieren:

Kotlin

private const val SORT_ORDER: String = "${Email.TYPE} ASC"

Java

    private static final String SORT_ORDER = Email.TYPE + " ASC ";