Android loading contacts is too slow

2019-02-19 12:26发布

in my app there are 5 spinners populated with the contacts in the phone. I am using the code below to populate an array with the contacts and then populate the spinners with the items of the array (just 1 spinner here).

When user opens the app the spinners are populated so the user can select one name for each spinner. Thing is, it takes around 3 secs to load the layout with the spinners when user opens the app. After playing with the code i figured out that no matter how may spinners are in the app, the source of the problem is the code that populates the array with the contacts. I also found the cause of the slowliness: i ran the app on my brothers phone and there it opened as fast as it should. He has only 30 contacts in the phone, while I have around 500, most of them are Facebook contacts. So how can I help this situation?

Here is the problem, independently of the number of spinners. In case of too many contacts, this gets slow:

 contactName = null;
        final Context context = getApplicationContext();

        ContentResolver cr = getContentResolver();
        Cursor cur = cr.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
        if (cur.getCount() > 0) 
        {
            while (cur.moveToNext()) {
            String idc = cur.getString(cur.getColumnIndex(ContactsContract.Contacts._ID));
            String namec = cur.getString(cur.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));

            if (Integer.parseInt(cur.getString(cur.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER))) > 0) 
            {
                Cursor pCur = cr.query(
                ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, 
                ContactsContract.CommonDataKinds.Phone.CONTACT_ID +" = ?", new String[]{idc}, null);
                while (pCur.moveToNext()) {
                    contactName  = pCur.getString(pCur.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME)); 

                    myArr.add(contactName);
                } 
                pCur.close();
                }

            }
        }

        myArr.add("");
        Collections.sort(myArr);

This is how I populate each spinner (and make them show the earlier selected name):

        Spinner sp1 = (Spinner) findViewById(R.id.Spinner01);
        ArrayAdapter<String> adapter1 = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, myArr);
        sp1.setAdapter(adapter1);
        sp1.setOnItemSelectedListener(new MyOnItemSelectedListener1());


        mprefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
        val1 = mprefs.getString("value1", "No name");
        if (!val1.equals("No name"))  
        {
            for (int i=0; i<myArr.size(); i++)
            {
                if (val1.equals(myArr.get(i))) 
                {
                    num = i;
                }
            }
            sp1.setSelection(num);
        }
        else
        {
            sp1.setSelection(0);
        }

Solution based on the idea of Jesse van Assen:

Turned out that the problem was the second query. I deleted this one and created a "projection" variable for the columns i needed:

final String[] projection = new String[] {
                ContactsContract.Contacts._ID,
                ContactsContract.Contacts.DISPLAY_NAME,
                ContactsContract.Contacts.HAS_PHONE_NUMBER
        };
        String selection = ContactsContract.Contacts.HAS_PHONE_NUMBER + "='1'";

        ContentResolver cr = getContentResolver();
        Cursor cur = cr.query(ContactsContract.Contacts.CONTENT_URI, projection, selection, null, null);
        if (cur.getCount() > 0) 
        {
            while (cur.moveToNext()) {
            String idc = cur.getString(cur.getColumnIndex(ContactsContract.Contacts._ID));
            String namec = cur.getString(cur.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
            myArr.add(namec);
            }
        }

So now I have one query that collects only the needed records. However it was slow even with the queried dataset AND with the second query. The real solution was to remove the second query, however collecting less record than the whole database is a great idea.

2条回答
2楼-- · 2019-02-19 13:00

I would say that the way you query your contacts data is wrong.

It looks like you're fetching ALL your contacts with all the contact's data from the database, and then filtering the contacts in a while loop. I would suggest doing this from within the query, something like this (not tested):

Cursor cur = cr.query(
    ContactsContract.Contacts.CONTENT_URI, 
    new String[] { "_ID", "DISPLAY_NAME" },
    "HAS_PHONE_NUMBER = ?", new String[] { "1" }, 
    null);
查看更多
别忘想泡老子
3楼-- · 2019-02-19 13:05

Your Spinners would populate a lot faster if you used a break to jump out of your for-loop once you assign

num = i
查看更多
登录 后发表回答