Details zu einem Kontakt abrufen

In dieser Lektion erfahren Sie, wie Sie Detaildaten für einen Kontakt abrufen, z. B. E-Mail-Adressen, Telefonnummern usw. Das sind die Details, nach denen Nutzer suchen, wenn sie einen Kontakt abrufen. Sie können alle Details für einen Kontakt angeben oder nur Details eines bestimmten Typs anzeigen lassen, z. B. E-Mail-Adressen.

Bei den Schritten in dieser Lektion wird davon ausgegangen, dass Sie bereits eine Zeile vom Typ ContactsContract.Contacts für einen Kontakt haben, den der Nutzer ausgewählt hat. In der Lektion Kontaktnamen abrufen erfahren Sie, wie Sie eine Liste mit 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 die LOOKUP_KEY des Kontakts enthalten. Diese Spalte ist in der Tabelle ContactsContract.Data verfügbar, da der Contacts Provider 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:Wenn alle Details für einen Kontakt abgerufen werden, sinkt die Leistung des Geräts, da alle Spalten in der Tabelle ContactsContract.Data abgerufen werden müssen. Berücksichtigen Sie die Auswirkungen auf die Leistung, bevor Sie diese Methode verwenden.

Berechtigungen anfordern

Damit Ihre App Daten vom Kontaktdatenanbieter lesen kann, benötigt sie die Berechtigung READ_CONTACTS. Wenn Sie diese Berechtigung anfordern möchten, fügen Sie Ihrer Manifestdatei das folgende untergeordnete Element von <manifest> hinzu:

    <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 ermitteln können. Beispiel:

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
            };

Bei dieser Projektion werden alle Spalten für eine Zeile in der Tabelle ContactsContract.Data anhand der in der Klasse ContactsContract.Data definierten Spaltennamen abgerufen.

Optional können Sie auch andere Spaltenkonstanten verwenden, die in der Klasse ContactsContract.Data definiert oder von ihr übernommen wurden. Die Spalten SYNC1 bis SYNC4 sind jedoch für Synchronisierungsadapter vorgesehen und ihre Daten sind daher nicht nützlich.

Auswahlkriterien definieren

Definieren Sie eine Konstante für die Auswahlklausel, ein Array für Auswahlargumente und eine Variable für den Auswahlwert. Suchen Sie in der Spalte Contacts.LOOKUP_KEY nach dem Kontakt. Beispiel:

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. Dadurch wird die Möglichkeit einer schädlichen SQL-Injection ausgeschlossen.

Sortierreihenfolge definieren

Definieren Sie die gewünschte Sortierreihenfolge im resultierenden Cursor. Wenn Sie alle Zeilen für einen bestimmten Datentyp zusammenhalten möchten, sortieren Sie nach Data.MIMETYPE. Mit diesem Abfrageargument werden alle E-Mail-Zeilen, alle Telefonnummernzeilen usw. gruppiert. Beispiel:

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:Für einige Datentypen wird kein Untertyp verwendet, sodass Sie nicht nach Untertyp sortieren können. Stattdessen müssen Sie das zurückgegebene Cursor durchlaufen, den Datentyp der aktuellen Zeile bestimmen und Daten für Zeilen speichern, die einen Untertyp verwenden. Wenn Sie mit dem Lesen des Cursors fertig sind, können Sie jeden Datentyp nach Untertyp sortieren und die Ergebnisse anzeigen.

Loader initialisieren

Führe Abrufe vom Kontaktdatenanbieter (und allen anderen Inhaltsanbietern) immer in einem Hintergrund-Thread durch. Verwenden Sie das Loader-Framework, das von der Klasse LoaderManager und der Schnittstelle LoaderManager.LoaderCallbacks definiert wird, um Hintergrundabrufe durchzuführen.

Wenn Sie bereit sind, die Zeilen abzurufen, initialisieren Sie das Loader-Framework, indem Sie initLoader() aufrufen. Übergeben Sie der Methode eine Ganzzahl-ID. Diese ID wird an die LoaderManager.LoaderCallbacks-Methoden übergeben. Mit der Kennung können Sie mehrere Loader in einer App verwenden, da Sie sie voneinander unterscheiden können.

Das folgende Snippet zeigt, wie das Loader-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(). Sie wird vom Lade-Framework sofort nach dem Aufruf von initLoader() aufgerufen. Diese Methode gibt eine CursorLoader zurück. Da Sie in der Tabelle ContactsContract.Data suchen, verwenden Sie die Konstante Data.CONTENT_URI als Inhalts-URI. Beispiel:

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 Kontaktdatenanbieter die Ergebnisse der Abfrage zurückgibt. Beispiel:

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 geändert haben, die dem Ergebnis Cursor zugrunde liegen. Entfernen Sie jetzt alle vorhandenen Verweise auf die Cursor, indem Sie sie auf „null“ setzen. Andernfalls wird die alte Cursor vom Loader-Framework nicht zerstört und es kommt zu einem Speicherleck. Beispiel:

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 Details zu einem Kontakt abrufen

Das Abrufen eines bestimmten Datentyps für einen Kontakt, z. B. aller E-Mails, erfolgt nach demselben Muster wie das Abrufen aller Details. Dies sind die einzigen Änderungen, die Sie an dem Code vornehmen müssen, der unter Alle Details für einen Kontakt abrufen aufgeführt ist:

Projektion
Ändern Sie die Projektion so, dass die für den Datentyp spezifischen Spalten abgerufen werden. Ändern Sie außerdem die Projektion so, dass die Spaltennamenkonstanten verwendet werden, die in der abgeleiteten Klasse ContactsContract.CommonDataKinds definiert sind, die dem Datentyp entsprechen.
Auswahl
Ändern Sie den Auswahltext, um nach dem MIMETYPE-Wert zu suchen, der für Ihren Datentyp spezifisch ist.
Sortierreihenfolge
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 Spalten, die Sie abrufen möchten, indem Sie die Spaltennamenkonstanten in der Unterklasse von ContactsContract.CommonDataKinds für den Datentyp verwenden. Wenn Sie Ihre Cursor an eine ListView binden möchten, müssen Sie die Spalte _ID abrufen. Wenn Sie beispielsweise E-Mail-Daten abrufen möchten, definieren Sie die folgende Projektion:

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 in dieser Projektion die in der Klasse ContactsContract.CommonDataKinds.Email definierten Spaltennamen anstelle der in der Klasse ContactsContract.Data definierten Spaltennamen verwendet werden. Die Verwendung der e-Mail-spezifischen Spaltennamen macht den Code übersichtlicher.

In der Projektion können Sie auch eine der anderen Spalten verwenden, die in der ContactsContract.CommonDataKinds-Unterklasse definiert sind.

Auswahlkriterien definieren

Definieren Sie einen Suchtextausdruck, mit dem Zeilen für die LOOKUP_KEY eines bestimmten Kontakts und die Data.MIMETYPE der gewünschten Details abgerufen werden. Setzen Sie den Wert MIMETYPE in einfache Anführungszeichen, indem Sie ein '-Zeichen (einfaches Anführungszeichen) an den Anfang und das Ende der Konstante verketten. Andernfalls interpretiert der Anbieter die Konstante als Variablennamen und nicht als Stringwert. Für diesen Wert ist kein Platzhalter erforderlich, da Sie keine vom Nutzer bereitgestellten Werte, sondern eine Konstante verwenden. Beispiel:

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

Definiert eine Sortierreihenfolge für die zurückgegebene Cursor. Da Sie einen bestimmten Datentyp abrufen, lassen Sie die Sortierung nach MIMETYPE weg. Wenn die Art der Detaildaten, nach denen Sie suchen, einen Untertyp enthält, sollten Sie nach diesem sortieren. Bei E-Mail-Daten können Sie beispielsweise nach den folgenden Email.TYPE sortieren:

Kotlin

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

Java

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