【问题标题】:How to check contacts present on the app?如何检查应用程序上存在的联系人?
【发布时间】:2019-03-31 07:19:46
【问题描述】:

Play 商店中有许多应用程序,显示联系人(在获得用户许可后)和每个用户末尾的公司徽标,表示用户是否在应用程序上。 此流程有助于邀请该组用户。 当我实现上述所有内容时,事情变得非常复杂,假设底部导航有一个专用操作说 Contacts,当我从已知方法(内容解析器查询)获取联系人时,首先它太慢了,所以我把联系人放到了sqlite db,但还是很慢。 如何有效地提取联系人?。就像 OYO ROOMS APP 有一个专门的底部导航操作 邀请和赚取,这正是我想要的。如果有人知道如何有效地做到这一点,请告诉我。

private void fetchContactsFromSystem() {
    ContentResolver cr = getContext().getContentResolver();
    Cursor cursor = cr.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, PROJECTION, null, null, ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME + " ASC");
    if (cursor != null) {
        try {
            final int nameIndex = cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME);
            final int numberIndex = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
            String name, number;
            while (cursor.moveToNext()) {
                name = cursor.getString(nameIndex);
                number = cursor.getString(numberIndex);
                number = number.replaceAll(" ", "");
                if (!phoneNumberSet.contains(number.trim())) {
                    sqliteDatabaseHelper.insertData(name.trim(), number.trim());
                    Contact contact = new Contact();
                    contact.setName(name);
                    contact.setPhone(number);
                    contact.setInvited(false);
                    contactList.add(contact);
                    phoneNumberSet.add(number);
                }
            }
        } finally {
            cursor.close();
        }
    }
}

投影字符串数组

private static final String[] PROJECTION = new String[]{
        ContactsContract.CommonDataKinds.Phone.CONTACT_ID,
        ContactsContract.Contacts.DISPLAY_NAME,
        ContactsContract.CommonDataKinds.Phone.NUMBER
};

从 sqlite 获取数据

    private void setUpFromSqliteDatabase(Cursor cr) {
    String name, number;
    boolean isInvite;
    while (cr.moveToNext()) {
        name = cr.getString(0);
        number = cr.getString(1);
        isInvite = cr.getInt(2) == 1;
        Contact contact = new Contact();
        contact.setName(name);
        contact.setPhone(number);
        contact.setInvited(isInvite);
        contactList.add(contact);
    }
    cr.close();
}

SQLite 帮助类

public class SqliteDatabaseHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "contacts.db";
private static final String TABLE_NAME = "contacts";
public static final String NAME = "NAME";
public static final String PHONE_NUMBER = "PHONE_NUMBER";
public static final String IS_INVITED = "IS_INVITED";

public SqliteDatabaseHelper(Context context) {
    super(context, DATABASE_NAME, null, 1);
}

@Override
public void onCreate(SQLiteDatabase db) {
    db.execSQL("create table " + TABLE_NAME +" (NAME TEXT, PHONE_NUMBER TEXT PRIMARY KEY, IS_INVITED INTEGER DEFAULT 0)");
}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    db.execSQL("DROP TABLE IF EXISTS "+TABLE_NAME);
    onCreate(db);
}

public boolean insertData(String name,String phone) {
    SQLiteDatabase db = this.getWritableDatabase();
    ContentValues contentValues = new ContentValues();
    contentValues.put(NAME,name);
    contentValues.put(PHONE_NUMBER,phone);
    long result = db.insert(TABLE_NAME,null ,contentValues);
    db.close();
    return result != -1;
}

public void updateInvitedData(String phoneNumber) {
    SQLiteDatabase db = this.getWritableDatabase();
    ContentValues cv = new ContentValues();
    cv.put(IS_INVITED, 1);
    db.update(TABLE_NAME, cv, "PHONE_NUMBER = ?",new String[] {phoneNumber});
    db.close();
}

public Cursor getAllData() {
    SQLiteDatabase db = this.getWritableDatabase();
    return db.rawQuery("select * from "+TABLE_NAME,null);
}

}

【问题讨论】:

  • “首先它太慢了” - 不是我的经验。我自己测量过一次,使用 ContentResolver 查询 10k 联系人花了 200 毫秒。
  • 好吧,我不知道你是怎么做到的,如果你会发布一些东西,那真的很有帮助,我确实使用了 Content Resolver 和查询返回光标,我使用光标提取用户的姓名和电话号码。所有这一切都在另一个线程中。但它很慢,这就是我问的原因。有人说批量提取联系人。不知道怎么批量提取联系人,请帮帮我。
  • 如果您发布了用于执行查询并构建“联系人”POJO 的代码(或者您在 Java 代码中使用它们),将会有所帮助。您是否也进行了任何类型的分析以查看瓶颈在哪里?
  • @PPartisan 我已经更新了问题并发布了使用的代码,函数 fetchContactFromSystem() 在异步线程中运行。
  • 这看起来很简单。我预计最昂贵的工作将是replaceAll()contains()(你不需要打电话给contains 或者Set,你可以做if(set.add(...)) {/*This item is being added for the first time, so update your db here*/}) - 你有多少联系人查询,你发现这个过程需要多长时间?另外,insert 可能会减慢你的速度 - 批处理这些并在最后做一个批量插入会更有效。

标签: android sqlite android-contacts


【解决方案1】:

如果预期结果是一组电话,则不需要按DISPLAY_NAME 排序,排序通常是 SQLite 中昂贵的功能。

另外,我不会将结果存储在本地 SQLite 中,当尝试保持本地副本和更新的联系人数据库同步时(例如,当用户添加或删除联系人时)会导致问题。

【讨论】:

  • 有什么有效的方法吗?查询本地 SQLite 仅显示 720 个联系人实际上需要 20 秒。
【解决方案2】:

有几个小方面可以提高效率,主要的部分来自批量插入。下面是一个示例,说明如何修改现有代码以实现此目的。

private void fetchContactsFromSystem() {
    final ContentResolver cr = getContext().getContentResolver();
    final String sort = String.format("%s ASC", Phone.DISPLAY_NAME);
    try(Cursor cursor = cr.query(Phone.CONTENT_URI, PROJECTION, null, null, sort)) {
        if(cursor == null || !cursor.moveToFirst()) return;

        final List<Contact> insert = new LinkedList<>();
        final int nameIndex = cursor.getColumnIndex(Contacts.DISPLAY_NAME);
        final int numberIndex = cursor.getColumnIndex(Phone.NUMBER);

        do {
            final String name = cursor.getString(nameIndex);
            final String number = cursor.getString(numberIndex).replaceAll("\\s+","");

            //"true" if number doesn't already exist in the Set
            if(phoneNumberSet.add(number)) {
                //Can make this immutable and pass info in constructor
                final Contact contact = new Contact(name, number, false);
                contactList.add(contact);
                insert.add(contact);  
            }       
        } while(cursor.moveToNext());
        //Conduct bulk insert of "insert" list here
        bulkInsert(insert);
    }

}

private void bulkInsert(List<Contact> contacts) {
    final SQLiteDatabase db = sqliteDatabaseHelper.getWritableDatabase();
    db.beginTransaction();
    try {
        for (Contact contact : contacts) {
            final ContentValues cv = new ContentValues(2);
            cv.put(NAME_ID, contact.name());
            cv.put(NUMBER_ID, contact.number());
            db.insert(TABLE_CONTACTS, null, values);
        }
        db.setTransactionSuccessful();
    } finally {
        db.endTransaction();
    }
}

【讨论】:

  • 嗯,这对于第一次在屏幕上的用户来说肯定有效,并将所有联系人插入到 sqlite。这是我承认的错误。但是在那之后,用户进入屏幕,我查询了 sqlite,但是对于大约 720 的联系人来说,这个过程也很慢
  • 您能否将查询这些联系人的方式添加到您的问题中?
  • 我已经添加了 sqlite 辅助类。
  • 对我来说看起来不错 - 有些事情我会改变,但不会对性能产生重大影响。我建议您尝试进行一些分析,因为我预计您的性能问题发生在其他地方 - 720 个联系人一无所获,并且查询应该几乎是即时的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-01-16
  • 1970-01-01
  • 1970-01-01
  • 2012-03-12
  • 1970-01-01
  • 1970-01-01
  • 2011-12-29
相关资源
最近更新 更多