Dimitri Payet
Dimitri Payet

Reputation: 197

Fetching and saving Contacts to SQLite database blocking the UI for long time

I am using below code to get all Contacts and saving in Local database.

Getting contacts:

ArrayList<ContactHolder> contactList = new ArrayList<ContactHolder>();


    String order = ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME + " ASC";
    Cursor managedCursor = mContext.getContentResolver().query(
            ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null,
            null, order);

    int _number = managedCursor
            .getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
    int _name = managedCursor
            .getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME);
    int _id = managedCursor
            .getColumnIndex(ContactsContract.CommonDataKinds.Phone._ID);



    while (managedCursor.moveToNext()) {

        ContactHolder holder = new ContactHolder();
        //String mData = Utility.filterLocaleWithAppropreateFlagsTest(mContext, managedCursor.getString(_number));
        holder.setNumber(managedCursor.getString(_number).replaceAll("\\s+",""));
        holder.setName(managedCursor.getString(_name));
        holder.setImageUrl(managedCursor.getString(_name));
        contactList.add(holder);
    }

Saving in SQLite DB:

 public List<Contact> insertContactDetails() {

    ArrayList<ContactHolder> arr = Utility.getContactDetails(context);
    List<Contact> serverArr = new ArrayList<Contact>();
    List<ContentValues> list = new ArrayList<ContentValues>();
    for (int i = 0; i < arr.size(); i++) {
        String mLocaleNo = "";

        ContactHolder mObject = arr.get(i);
        String mData = Utility.filterLocaleWithAppropreateFlagsTest(context, mObject.getNumber().replaceAll("\\s+",""));
        if (mData != null) {
            Contact mContact = new Contact();

            ContentValues values = new ContentValues();
            values.put(CONTACT_NAME, mObject.getName());
            mContact.setName(mObject.getName());

            values.put(CONTACT_MOBILE_NUMBER, mObject.getNumber().replaceAll("\\s+",""));


            values.put(CONTACT_IMAGE, mObject.getImageUrl());
            ArrayList<String> extra = new ArrayList<String>();
            StringTokenizer token = new StringTokenizer(mData);

            while (token.hasMoreElements()) {
                extra.add(String.valueOf(token.nextElement()));
            }

            mLocaleNo = extra.get(0);
            int mLocaleFlag = Integer.parseInt(extra.get(1));

            values.put(LOCALE_NUMBER, mLocaleNo);
            mContact.setPhNo(mLocaleNo);
            values.put(LOCALE_FLAG, mLocaleFlag);
            values.put(REG_STATUS, "0");

            list.add(values);
            serverArr.add(mContact);
        }
        /*
        arr.get(i).setContactStatus("0");
        arr.get(i).setLocaleNumber(mLocaleNo);*/
    }

    ourDatabase.beginTransaction();
    for (ContentValues entry : list) {

        try{
            long id = ourDatabase.insertOrThrow(CONTACT_TABLE, null, entry);
        }catch(Exception e){
            e.printStackTrace();
        }
    }
    ourDatabase.setTransactionSuccessful();
    ourDatabase.endTransaction();

    return serverArr;

}  

Everything is working as expected but if the contact size is huge then the actual problem comes to the picture which makes the screen white for a long.

I still not found any proper solution to handle this.

Upvotes: 2

Views: 1250

Answers (2)

Ranjit
Ranjit

Reputation: 5150

I can recommend to use only ContentValues as an model to handle contacts from source to dest.

If you will dig little, you can able to find out the actual issue. You are using contactHolder to store and again retrive and again store in ContentValues which is not necessary I think. Just change the code as below and see the improvements.

I simply added your code in a method and add some changes in it.

Fetch contacts:

 public static ArrayList<ContentValues> getContactDetails(final Context mContext){
    ArrayList<ContentValues> contactList = new ArrayList<ContentValues>();


    String order = ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME + " ASC";
    Cursor managedCursor = mContext.getContentResolver().query(
            ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null,
            null, order);

    int _number = managedCursor
            .getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
    int _name = managedCursor
            .getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME);
    int _id = managedCursor
            .getColumnIndex(ContactsContract.CommonDataKinds.Phone._ID);



    while (managedCursor.moveToNext()) {

        ContentValues values = new ContentValues();
        Contact mContact = new Contact();


            values.put(ContactClass.CONTACT_NAME, managedCursor.getString(_name));
            values.put(ContactClass.CONTACT_MOBILE_NUMBER, managedCursor.getString(_number).replaceAll("\\s+",""));
            mContact.setPhNo(managedCursor.getString(_number).replaceAll("\\s+",""));
            mContact.setName(managedCursor.getString(_name));

            contactList.add(values);
            serverContactList.add(mContact);

        }
    }

    return contactList;
}

Here I simply use same ContentValues class as a model to hold contacts inside collection and simply adding the same data for your Contacts (server stuffs) class which are saving in a static collection (serverContactList) which you can direct access from anywhere.

NB: ContactClass.CONTACT_MOBILE_NUMBER is your column name.

Insert Contacts to DB:

public List<Contact> insertContactDetails() {

    List<ContentValues> list = Utility.getContactDetails(context);

    ourDatabase.beginTransaction();
    for (ContentValues entry : list) {

        try{
            long id = ourDatabase.insertOrThrow(CONTACT_TABLE, null, entry);
        }catch(Exception e){
            e.printStackTrace();
        }
    }
    ourDatabase.setTransactionSuccessful();
    ourDatabase.endTransaction();

    return Utility.serverContactList;

}

It will make your performance more better if you will do all these stuffs in a background thread as others suggested here.

Upvotes: 2

Hemant Parmar
Hemant Parmar

Reputation: 3976

You should use CursorLoader for save/retrieve if data is huge.

There are three key benefits of using a CursorLoader:

  1. The query is handled on a background thread for you (courtesy of being built on AsyncTaskLoader) so that large data queries do not block the UI. This is something the docs recommended for you to do when you’re using a plain Cursor, but now it's done under the hood.
  2. CursorLoader is auto-updating. In addition to performing the initial query, CursorLoader also registers a ContentObserver with the dataset you requested and calls forceLoad() on itself when the data set changes.
  3. This results in getting async callbacks anytime the data changes in order to update the view.

For more read this Cursor Loader.

Upvotes: 0

Related Questions