Details zu einem Kontakt abrufen

In dieser Lektion wird gezeigt, wie Sie Detaildaten für einen Kontakt abrufen, z. B. E‑Mail-Adressen und Telefon nummern. Das sind die Details, nach denen Nutzer suchen, wenn sie einen Kontakt abrufen. Sie können ihnen alle Details zu einem Kontakt zur Verfügung stellen 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 vom Nutzer ausgewählten Kontakt haben. In der Lektion Kontaktnamen abrufen wird gezeigt, wie Sie eine Liste von Kontakten abrufen.

Alle Details für einen Kontakt abrufen

Wenn Sie alle Details für einen Kontakt abrufen möchten, suchen Sie in der ContactsContract.Data Tabelle nach Zeilen, die den LOOKUP_KEY des Kontakts enthalten. Diese Spalte ist in der ContactsContract.Data Tabelle verfügbar, da der Kontakteanbieter eine implizite Verknüpfung zwischen der ContactsContract.Contacts Tabelle und der ContactsContract.Data Tabelle erstellt. Die LOOKUP_KEY Spalte wird in der Lektion Kontaktnamen abrufen ausführlicher beschrieben.

Hinweis: Wenn Sie alle Details für einen Kontakt abrufen, wird die Leistung eines Geräts beeinträchtigt, da alle Spalten in der ContactsContract.Data Tabelle abgerufen werden müssen. Berücksichtigen Sie die Auswirkungen auf die Leistung, bevor Sie diese Methode verwenden.

Berechtigungen anfordern

Wenn Ihre App Daten vom Contacts Provider lesen soll, muss sie die Berechtigung READ_CONTACTS haben. Fügen Sie dazu 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 unterschiedlichen Spalten. Damit Sie alle möglichen Spalten für alle möglichen Datentypen erhalten, müssen Sie alle die Spaltennamen zu Ihrer Projektion 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. 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
            };

Diese Projektion ruft alle Spalten für eine Zeile in der ContactsContract.Data Tabelle ab. Dabei werden die Spaltennamen verwendet, die in der ContactsContract.Data Klasse definiert sind.

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

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 Contacts.LOOKUP_KEY Spalte, um den Kontakt zu finden. 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 im Auswahltextausdruck verwenden, wird die resultierende Suche durch Bindung und nicht durch SQL-Kompilierung generiert. Dieser Ansatz schließt die Möglichkeit böswilliger SQL-Injection aus.

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. Dieses Abfrageargument gruppiert alle E‑Mail-Zeilen zusammen, alle Telefonzeilen zusammen usw. 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:Einige Datentypen verwenden keinen Untertyp, daher können Sie nicht nach Untertyp sortieren. Stattdessen müssen Sie den zurückgegebenen Cursor, den Datentyp der aktuellen Zeile bestimmen und Daten für Zeilen speichern, die einen Untertyp verwenden. Wenn Sie den Cursor gelesen haben, können Sie die einzelnen Datentypen nach Untertyp sortieren und die Ergebnisse anzeigen.

Loader initialisieren

Abrufe vom Contacts Provider (und allen anderen Content-Anbietern) sollten immer in einem Hintergrundthread erfolgen. Verwenden Sie das Loader-Framework, das durch die LoaderManager Klasse und die LoaderManager.LoaderCallbacks Schnittstelle definiert wird, um Abrufe im Hintergrund durchzuführen.

Wenn Sie die Zeilen abrufen möchten, initialisieren Sie das Loader-Framework mit Aufruf von initLoader(). Übergeben Sie eine Ganzzahl-ID an die Methode. Diese ID wird an die LoaderManager.LoaderCallbacks Methoden übergeben. Mit der ID können Sie mehrere Loader in einer App verwenden, da Sie sie voneinander unterscheiden können.

Das folgende Snippet zeigt, wie Sie das Loader-Framework initialisieren:

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 Loader-Framework unmittelbar nach dem Aufruf von initLoader() aufgerufen wird. Geben Sie ein CursorLoader von dieser Methode zurück. Da Sie in der ContactsContract.Data Tabelle suchen, verwenden Sie die Konstante Data.CONTENT_URI als Content-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 onLoadFinished() Methode. Das Loader-Framework ruft onLoadFinished() auf, wenn der Contacts Provider 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 Loader-Framework feststellt, dass sich die Daten, die den Ergebnis Cursor unterstützen, geändert haben. Entfernen Sie an dieser Stelle alle vorhandenen Verweise auf den Cursor indem Sie sie auf null setzen. Andernfalls wird der alte Cursor nicht vom Loader Framework zerstört und es kommt zu einem Speicher leck. 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 für einen Kontakt abrufen

Das Abrufen eines bestimmten Datentyps für einen Kontakt, z. B. aller E‑Mails, folgt demselben Muster wie das Abrufen aller Details. Das sind die einzigen Änderungen, die Sie am Code unter Alle Details für einen Kontakt abrufen vornehmen müssen:

Projektion
Ändern Sie die Projektion, um die Spalten abzurufen, die für den Datentyp spezifisch sind. Ändern Sie die Projektion auch so, dass die Spaltennamenkonstanten verwendet werden, die in der ContactsContract.CommonDataKinds Unterklasse definiert sind, die dem Datentyp entspricht.
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 den zurückgegebenen Cursor nach Data.MIMETYPE.

Diese Änderungen werden in den folgenden Abschnitten beschrieben.

Projektion definieren

Definieren Sie die Spalten, die Sie abrufen möchten, mit den Spaltennamenkonstanten in der Unterklasse von ContactsContract.CommonDataKinds für den Datentyp. Wenn Sie den Cursor an eine ListView, rufen Sie die Spalte _ID ab. 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 diese Projektion die Spaltennamen verwendet, die in der Klasse ContactsContract.CommonDataKinds.Email definiert sind, anstatt 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 ContactsContract.CommonDataKinds Unterklasse definiert sind.

Auswahlkriterien definieren

Definieren Sie einen Suchtextausdruck, der Zeilen für den LOOKUP_KEY eines bestimmten Kontakts und den Data.MIMETYPE der gewünschten Details abruft. Setzen Sie den MIMETYPE-Wert in einfache Anführungszeichen, indem Sie am Anfang und Ende der Konstante ein "'" (einfaches Anführungszeichen) hinzufügen. Andernfalls interpretiert der Anbieter die Konstante als Variablennamen und nicht als Stringwert. Für diesen Wert ist kein Platzhalter erforderlich, da Sie eine Konstante und keinen vom Nutzer angegebenen Wert 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

Definieren Sie eine Sortierreihenfolge für den zurückgegebenen Cursor. Da Sie einen bestimmten Datentyp abrufen, lassen Sie die Sortierung nach MIMETYPE weg. Wenn der Typ der Detaildaten, nach denen Sie suchen, einen Untertyp enthält, sortieren Sie stattdessen danach. Für 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 ";