studio - ¿Cómo obtener ID de contacto, correo electrónico, número de teléfono en una consulta SQLite? Contactos Optimización de Android
programacion android pdf 2018 (2)
Quiero obtener Todos los contactos al menos con un número de teléfono, también quiero todos los números de teléfono y todos los correos electrónicos para cada contacto.
Código actual:
// To get All Contacts having atleast one phone number.
Uri uri = ContactsContract.Contacts.CONTENT_URI;
String selection = ContactsContract.Contacts.HAS_PHONE_NUMBER + " > ?";
String[] selectionArgs = new String[] {"0"};
Cursor cu = applicationContext.getContentResolver().query(uri,
null, selection, selectionArgs, null);
// For getting All Phone Numbers and Emails further queries :
while(cu.moveToNext()){
String id = cu.getString(cu.getColumnIndex(ContactsContract.Contacts._ID));
// To get Phone Numbers of Contact
Cursor pCur = context.getContentResolver().query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null,ContactsContract.CommonDataKinds.Phone.CONTACT_ID + "=?",
new String[]{id}, null);
// To get Email ids of Contact
Cursor emailCur = context.getContentResolver().query(
ContactsContract.CommonDataKinds.Email.CONTENT_URI, null,
ContactsContract.CommonDataKinds.Email.CONTACT_ID + " = ?",
new String[]{id}, null);
// Iterate through these cursors to get Phone numbers and Emails
}
Si hay más de 1000 contactos en mi dispositivo, está tomando demasiado tiempo. ¿Cómo puedo obtener todos los datos en una sola consulta, en lugar de hacer dos consultas adicionales para cada contacto?
¿O hay alguna otra forma de optimizar?
Gracias de antemano.
ICS: cuando consulta desde Data.CONTENT_URI
, tiene todas las filas del Contact
asociado ya unido, es decir, esto funcionaría:
ContentResolver resolver = getContentResolver();
Cursor c = resolver.query(
Data.CONTENT_URI,
null,
Data.HAS_PHONE_NUMBER + "!=0 AND (" + Data.MIMETYPE + "=? OR " + Data.MIMETYPE + "=?)",
new String[]{Email.CONTENT_ITEM_TYPE, Phone.CONTENT_ITEM_TYPE},
Data.CONTACT_ID);
while (c.moveToNext()) {
long id = c.getLong(c.getColumnIndex(Data.CONTACT_ID));
String name = c.getString(c.getColumnIndex(Data.DISPLAY_NAME));
String data1 = c.getString(c.getColumnIndex(Data.DATA1));
System.out.println(id + ", name=" + name + ", data1=" + data1);
}
Si está apuntando a la versión 2.3, debe tener en cuenta el hecho de que HAS_PHONE_NUMBER
no está disponible a través de las combinaciones utilizadas al consultar los Data
.
Diversión
Esto podría, por ejemplo, solucionarse omitiendo su requisito de que el contacto debe tener un número de teléfono y en su lugar se conforma con "cualquier contacto con al menos un número de teléfono o una dirección de correo electrónico":
Cursor c = resolver.query(
Data.CONTENT_URI,
null,
Data.MIMETYPE + "=? OR " + Data.MIMETYPE + "=?",
new String[]{Email.CONTENT_ITEM_TYPE, Phone.CONTENT_ITEM_TYPE},
Data.CONTACT_ID);
Si esa no es una opción, siempre puede optar por una sub-selección horriblemente hacky :
Cursor c = resolver.query(
Data.CONTENT_URI,
null,
"(" + Data.MIMETYPE + "=? OR " + Data.MIMETYPE + "=?) AND " +
Data.CONTACT_ID + " IN (SELECT " + Contacts._ID + " FROM contacts WHERE " + Contacts.HAS_PHONE_NUMBER + "!=0)",
new String[]{Email.CONTENT_ITEM_TYPE, Phone.CONTENT_ITEM_TYPE}, Data.CONTACT_ID);
o resuélvelo usando dos Cursor
s:
Cursor contacts = resolver.query(Contacts.CONTENT_URI,
null, Contacts.HAS_PHONE_NUMBER + " != 0", null, Contacts._ID + " ASC");
Cursor data = resolver.query(Data.CONTENT_URI, null,
Data.MIMETYPE + "=? OR " + Data.MIMETYPE + "=?",
new String[]{Email.CONTENT_ITEM_TYPE, Phone.CONTENT_ITEM_TYPE},
Data.CONTACT_ID + " ASC");
int idIndex = contacts.getColumnIndexOrThrow(Contacts._ID);
int nameIndex = contacts.getColumnIndexOrThrow(Contacts.DISPLAY_NAME);
int cidIndex = data.getColumnIndexOrThrow(Data.CONTACT_ID);
int data1Index = data.getColumnIndexOrThrow(Data.DATA1);
boolean hasData = data.moveToNext();
while (contacts.moveToNext()) {
long id = contacts.getLong(idIndex);
System.out.println("Contact(" + id + "): " + contacts.getString(nameIndex));
if (hasData) {
long cid = data.getLong(cidIndex);
while (cid <= id && hasData) {
if (cid == id) {
System.out.println("/t(" + cid + "/" + id + ").data1:" +
data.getString(data1Index));
}
hasData = data.moveToNext();
if (hasData) {
cid = data.getLong(cidIndex);
}
}
}
}
Pasé por el mismo problema exacto. Desde entonces, construí mi propia solución que está inspirada en esta publicación, aunque un poco diferente. Ahora me gustaría compartirlo como mi primera respuesta de :-)
Es bastante similar al enfoque de doble cursor sugerido por Jens. La idea es
1- obtener contacto relevante de la tabla de contactos
2- obtener información relevante de contactos (correo, teléfono, ...)
3- combina estos resultados
El "relevante" depende de usted, por supuesto, ¡pero el punto importante es el rendimiento! Además, estoy seguro de que otras soluciones que utilizan una consulta SQL adecuada pueden hacer el trabajo, pero aquí solo quiero usar el proveedor de contenido de Android. Aquí está el código:
Algunas constantes
public static String CONTACT_ID_URI = ContactsContract.Contacts._ID;
public static String DATA_CONTACT_ID_URI = ContactsContract.Data.CONTACT_ID;
public static String MIMETYPE_URI = ContactsContract.Data.MIMETYPE;
public static String EMAIL_URI = ContactsContract.CommonDataKinds.Email.DATA;
public static String PHONE_URI = ContactsContract.CommonDataKinds.Phone.DATA;
public static String NAME_URI = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) ? ContactsContract.Data.DISPLAY_NAME_PRIMARY : ContactsContract.Data.DISPLAY_NAME;
public static String PICTURE_URI = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) ? ContactsContract.Contacts.PHOTO_THUMBNAIL_URI : ContactsContract.Contacts.PHOTO_ID;
public static String MAIL_TYPE = ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE;
public static String PHONE_TYPE = ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE;
1 contacto
Aquí requiero que los Contactos deben tener DISPLAY_NAME libre de "@" y que sus informaciones coincidan con una cadena dada (este requisito, por supuesto, puede modificarse). El resultado del siguiente método es el primer cursor:
public Cursor getContactCursor(String stringQuery, String sortOrder) {
Logger.i(TAG, "+++++++++++++++++++++++++++++++++++++++++++++++++++");
Logger.e(TAG, "ContactCursor search has started...");
Long t0 = System.currentTimeMillis();
Uri CONTENT_URI;
if (stringQuery == null)
CONTENT_URI = ContactsContract.Contacts.CONTENT_URI;
else
CONTENT_URI = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_FILTER_URI, Uri.encode(stringQuery));
String[] PROJECTION = new String[]{
CONTACT_ID_URI,
NAME_URI,
PICTURE_URI
};
String SELECTION = NAME_URI + " NOT LIKE ?";
String[] SELECTION_ARGS = new String[]{"%" + "@" + "%"};
Cursor cursor = sContext.getContentResolver().query(CONTENT_URI, PROJECTION, SELECTION, SELECTION_ARGS, sortOrder);
Long t1 = System.currentTimeMillis();
Logger.e(TAG, "ContactCursor finished in " + (t1 - t0) / 1000 + " secs");
Logger.e(TAG, "ContactCursor found " + cursor.getCount() + " contacts");
Logger.i(TAG, "+++++++++++++++++++++++++++++++++++++++++++++++++++");
return cursor;
}
Esta consulta es bastante eficaz como verás!
2 Datos de contacto
Ahora vamos a buscar información de contacto. En este punto, no hago ningún vínculo entre el contacto ya recuperado y la información recuperada: solo obtengo toda la información de la tabla de datos ... Sin embargo, para evitar información inútil, todavía necesito DISPLAY_NAMES libre de "@" y, desde entonces, Estoy interesado en el correo electrónico y el teléfono. Requiero que los datos MIMETYPE sean MAIL_TYPE o PHONE_TYPE (consulte Constantes). Aquí está el código:
public Cursor getContactDetailsCursor() {
Logger.i(TAG, "+++++++++++++++++++++++++++++++++++++++++++++++++++");
Logger.e(TAG, "ContactDetailsCursor search has started...");
Long t0 = System.currentTimeMillis();
String[] PROJECTION = new String[]{
DATA_CONTACT_ID_URI,
MIMETYPE_URI,
EMAIL_URI,
PHONE_URI
};
String SELECTION = ContactManager.NAME_URI + " NOT LIKE ?" + " AND " + "(" + MIMETYPE_URI + "=? " + " OR " + MIMETYPE_URI + "=? " + ")";
String[] SELECTION_ARGS = new String[]{"%" + "@" + "%", ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE};
Cursor cursor = sContext.getContentResolver().query(
ContactsContract.Data.CONTENT_URI,
PROJECTION,
SELECTION,
SELECTION_ARGS,
null);
Long t1 = System.currentTimeMillis();
Logger.e(TAG, "ContactDetailsCursor finished in " + (t1 - t0) / 1000 + " secs");
Logger.e(TAG, "ContactDetailsCursor found " + cursor.getCount() + " contacts");
Logger.i(TAG, "+++++++++++++++++++++++++++++++++++++++++++++++++++");
return cursor;
}
Una vez más verás que esta consulta es bastante rápida ...
3 combinando
Ahora vamos a combinar tanto el contacto como sus respectivas informaciones. La idea es usar HashMap (Key, String) donde Key es la identificación de contacto y String es lo que quieras (nombre, correo electrónico, ...).
Primero, paso por el cursor de contacto (ordenado alfabéticamente) y almaceno los nombres y el uri de la imagen en dos HashMap diferentes. Tenga en cuenta también que almaceno todas las ID de contacto en una lista en el mismo orden en que aparecen los contactos en el cursor. Llamemos a esta lista contactListId
Hago lo mismo con las informaciones de contacto (correo y correo electrónico). Pero ahora me ocupo de la correlación entre los dos cursores: si el CONTACT_ID de un correo electrónico o teléfono no aparece en contactListId, se pone a un lado. También reviso si el correo electrónico ya ha sido encontrado. Tenga en cuenta que esta selección adicional puede introducir asimetrías entre el contenido de Nombre / Imagen y el contenido de Email / Phone HashMap, pero no se preocupe.
Finalmente, recorro la lista contactListId y construyo una lista de Objeto de contacto que se ocupa del hecho de que: un contacto debe tener información (condición KeySet) y que el contacto debe tener al menos un correo o un correo electrónico (el caso donde correo = = nulo && teléfono == nulo puede aparecer si el contacto es un contacto de Skype, por ejemplo). Y aquí está el código:
public List<Contact> getDetailedContactList(String queryString) {
/**
* First we fetch the contacts name and picture uri in alphabetical order for
* display purpose and store these data in HashMap.
*/
Cursor contactCursor = getContactCursor(queryString, NAME_URI);
List<Integer> contactIds = new ArrayList<>();
if (contactCursor.moveToFirst()) {
do {
contactIds.add(contactCursor.getInt(contactCursor.getColumnIndex(CONTACT_ID_URI)));
} while (contactCursor.moveToNext());
}
HashMap<Integer, String> nameMap = new HashMap<>();
HashMap<Integer, String> pictureMap = new HashMap<>();
int idIdx = contactCursor.getColumnIndex(CONTACT_ID_URI);
int nameIdx = contactCursor.getColumnIndex(NAME_URI);
int pictureIdx = contactCursor.getColumnIndex(PICTURE_URI);
if (contactCursor.moveToFirst()) {
do {
nameMap.put(contactCursor.getInt(idIdx), contactCursor.getString(nameIdx));
pictureMap.put(contactCursor.getInt(idIdx), contactCursor.getString(pictureIdx));
} while (contactCursor.moveToNext());
}
/**
* Then we get the remaining contact information. Here email and phone
*/
Cursor detailsCursor = getContactDetailsCursor();
HashMap<Integer, String> emailMap = new HashMap<>();
HashMap<Integer, String> phoneMap = new HashMap<>();
idIdx = detailsCursor.getColumnIndex(DATA_CONTACT_ID_URI);
int mimeIdx = detailsCursor.getColumnIndex(MIMETYPE_URI);
int mailIdx = detailsCursor.getColumnIndex(EMAIL_URI);
int phoneIdx = detailsCursor.getColumnIndex(PHONE_URI);
String mailString;
String phoneString;
if (detailsCursor.moveToFirst()) {
do {
/**
* We forget all details which are not correlated with the contact list
*/
if (!contactIds.contains(detailsCursor.getInt(idIdx))) {
continue;
}
if(detailsCursor.getString(mimeIdx).equals(MAIL_TYPE)){
mailString = detailsCursor.getString(mailIdx);
/**
* We remove all double contact having the same email address
*/
if(!emailMap.containsValue(mailString.toLowerCase()))
emailMap.put(detailsCursor.getInt(idIdx), mailString.toLowerCase());
} else {
phoneString = detailsCursor.getString(phoneIdx);
phoneMap.put(detailsCursor.getInt(idIdx), phoneString);
}
} while (detailsCursor.moveToNext());
}
contactCursor.close();
detailsCursor.close();
/**
* Finally the contact list is build up
*/
List<Contact> contacts = new ArrayList<>();
Set<Integer> detailsKeySet = emailMap.keySet();
for (Integer key : contactIds) {
if(!detailsKeySet.contains(key) || (emailMap.get(key) == null && phoneMap.get(key) == null))
continue;
contacts.add(new Contact(String.valueOf(key), pictureMap.get(key), nameMap.get(key), emailMap.get(key), phoneMap.get(key)));
}
return contacts;
}
La definición del objeto de contacto depende de usted.
Espero que esto ayude y gracias por el post anterior.
Corrección / Mejora
Olvidé verificar el conjunto de teclas del teléfono: debería verse como
!mailKeySet.contains(key)
reemplazado por
(!mailKeySet.contains(key) && !phoneKeySet.contains(key))
con el teléfono keySet
Set<Integer> phoneKeySet = phoneMap.keySet();
¿Por qué no añadir un cursor de contacto vacío?
if(contactCursor.getCount() == 0){
contactCursor.close();
return new ArrayList<>();
}
Justo después de la llamada getContactCursor