前言

今天整理一下关于在Contacts中通过Setting进行Import/Export SIM卡联系人的过程。同样,源码分析基于Android8高通平台。

Import过程

Import过程是指将SIM卡中的联系人导入(copy)到本地账号中,需要强调的一点是,当设备未登陆google账号之前,存入手机本地的联系人的Account为null,如果登陆了google账号,存入本地的联系人的Account变为com.google,并且之前存入的联系人的Account也会变为新的google账号。先简单介绍一下操作手法:打开Contacts.apk ->功能按钮 ->Settings ->Import ->选择SIM card ->勾选需要import的联系人 -> IMPORT ->弹出Toast&发送通知表示copy成功。

代码逻辑如下:

android联系人最新 android手机联系人导入方式_import


打开Contacts的功能属性页面,点击Settings按钮,点击Import。

final DialogInterface.OnClickListener clickListener =
    new DialogInterface.OnClickListener() {
    @Override
    public void onClick(DialogInterface dialog, int which) {
        final int resId = adapter.getItem(which).mChoiceResourceId;
        if (resId == R.string.import_from_sim) {
            handleSimImportRequest(adapter.getItem(which).mSim);
        } else if (resId == R.string.import_from_vcf_file) {
            handleImportRequest(resId, SimCard.NO_SUBSCRIPTION_ID);
        } else {
            Log.e(TAG, "Unexpected resource: "+ getActivity().getResources().getResourceEntryName(resId));
        }
        dialog.dismiss();
    }
};

会弹出对话框供你选择,从.vcf文件中copy联系人还是从SIM卡中copy,本文主要介绍从SIM卡中copy联系人。

private void handleSimImportRequest(SimCard sim) {
     startActivity(new Intent(getActivity(), SimImportActivity.class)
        .putExtra(SimImportActivity.EXTRA_SUBSCRIPTION_ID, sim.getSubscriptionId()));
}

在SimImportActivity.class中构建一个fragment(SimImportFragment.java),其中包含个ListView和几个操作功能按钮。ListView设置为多选模式setChoiceMode(AbsListView.CHOICE_MODE_MULTIPLE);勾选了需要copy的联系人之后,点击Import按钮。

mImportButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
           importCurrentSelections();
           // Do we wait for import to finish?
           getActivity().setResult(Activity.RESULT_OK);
           getActivity().finish();
    }
});
private void importCurrentSelections() {
    final SparseBooleanArray checked = mListView.getCheckedItemPositions();
    final ArrayList<SimContact> importableContacts = new ArrayList<>(checked.size());
    for (int i = 0; i < checked.size(); i++) {
    // It's possible for existing contacts to be "checked" but we only want to import the ones that don't already exist
        if (checked.valueAt(i) && !mAdapter.existsInCurrentAccount(i)) {
            importableContacts.add(mAdapter.getItem(checked.keyAt(i)));
        }
    }
    SimImportService.startImport(getContext(), mSubscriptionId, importableContacts,
        mAccountHeaderPresenter.getCurrentAccount());
}

packages/apps/Contacts/src/com/android/contacts/SimImportService.java
public static void startImport(Context context, int subscriptionId,
        ArrayList<SimContact> contacts, AccountWithDataSet targetAccount) {
    context.startService(new Intent(context, SimImportService.class)
        .putExtra(EXTRA_SIM_CONTACTS, contacts)  //选中的联系人
        .putExtra(EXTRA_SIM_SUBSCRIPTION_ID, subscriptionId)
        .putExtra(EXTRA_ACCOUNT, targetAccount));
}
@Override
public int onStartCommand(Intent intent, int flags, final int startId) {
    ContactsNotificationChannelsUtil.createDefaultChannel(this);
    final ImportTask task = createTaskForIntent(intent, startId);
        if (task == null) {
            new StopTask(this, startId).executeOnExecutor(mExecutor);
            return START_NOT_STICKY;
        }
    sPending.add(task);
    task.executeOnExecutor(mExecutor);
    notifyStateChanged();
    return START_REDELIVER_INTENT;
}

配置好ImportTask并开始执行

@Override
protected Boolean doInBackground(Void... params) {
    final TimingLogger timer = new TimingLogger(TAG, "import");
    try {
        // Just import them all at once.
        // Experimented with using smaller batches (e.g. 25 and 50) so that percentage
        // progress could be displayed however this slowed down the import by over a factor
        // of 2. If the batch size is over a 100 then most cases will only require a single
        // batch so we don't even worry about displaying accurate progress
        mDao.importContacts(mContacts, mTargetAccount);
        mDao.persistSimState(mSim.withImportedState(true));
        timer.addSplit("done");
        timer.dumpToLog();
    } catch (RemoteException|OperationApplicationException e) {
        FeedbackHelper.sendFeedback(SimImportService.this, TAG,
                 "Failed to import contacts from SIM card", e);
        return false;
    }
     return true;
}
packages/apps/Contacts/src/com/android/contacts/database/SimContactDaoImpl.java
@Override
public ContentProviderResult[] importContacts(List<SimContact> contacts,
        AccountWithDataSet targetAccount) throws RemoteException, OperationApplicationException {
    if (contacts.size() < IMPORT_MAX_BATCH_SIZE) {
        return importBatch(contacts, targetAccount);
    }
    final List<ContentProviderResult> results = new ArrayList<>();
    for (int i = 0; i < contacts.size(); i += IMPORT_MAX_BATCH_SIZE) {
        results.addAll(Arrays.asList(importBatch(
            contacts.subList(i, Math.min(contacts.size(), i + IMPORT_MAX_BATCH_SIZE)),targetAccount)));
    }
    return results.toArray(new ContentProviderResult[results.size()]);
}
 private ContentProviderResult[] importBatch(List<SimContact> contacts,
      AccountWithDataSet targetAccount) throws RemoteException, OperationApplicationException {
    final ArrayList<ContentProviderOperation> ops = createImportOperations(contacts, targetAccount);
    return mResolver.applyBatch(ContactsContract.AUTHORITY, ops);  //执行事务化处理
}

在代码中createImportOperations()方法中具体决定了对数据库执行哪种操作

private ArrayList<ContentProviderOperation> createImportOperations(List<SimContact> contacts,
      AccountWithDataSet targetAccount) {
    final ArrayList<ContentProviderOperation> ops = new ArrayList<>();
    for (SimContact contact : contacts) {
        contact.appendCreateContactOperations(ops, targetAccount);
    }
    return ops;
}
packages/apps/Contacts/src/com/android/contacts/model/SimContact.java
public void appendCreateContactOperations(List<ContentProviderOperation> ops,
      AccountWithDataSet targetAccount) {
    // There is nothing to save so skip it.
    if (!hasName() && !hasPhone() && !hasEmails() && !hasAnrs()) return; 
    final int rawContactOpIndex = ops.size();
    ops.add(ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)  //创建插入数据库操作
       .withYieldAllowed(true)
       .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, targetAccount.name)
       .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, targetAccount.type)
       .withValue(ContactsContract.RawContacts.DATA_SET, targetAccount.dataSet)
       .build());
    if (mName != null) {
        ops.add(createInsertOp(rawContactOpIndex, StructuredName.CONTENT_ITEM_TYPE,
              StructuredName.DISPLAY_NAME, mName));
    }
    if (!mPhone.isEmpty()) {
        ops.add(createInsertOp(rawContactOpIndex, Phone.CONTENT_ITEM_TYPE,
              Phone.NUMBER, mPhone));
    }
    if (mEmails != null) {
        for (String email : mEmails) {
            ops.add(createInsertOp(rawContactOpIndex, Email.CONTENT_ITEM_TYPE,
                  Email.ADDRESS, email));
        }
    }
    if (mAnrs != null) {
        for (String anr : mAnrs) {
            ops.add(createInsertOp(rawContactOpIndex, Phone.CONTENT_ITEM_TYPE,
                  Phone.NUMBER, anr));
        }
    }
}

至此,SIM卡中被选中的联系人已经import到本地的联系人账户中,ImportTask执行完毕后,将会有一些通知表明操作是否成功。

packages/apps/Contacts/src/com/android/contacts/SimImportService.java
@Override
protected void onPostExecute(Boolean success) {
    super.onPostExecute(success);
    stopSelf(mStartId);
    Intent result;
    final Notification notification;
    if (success) {
        result = new Intent(BROADCAST_SIM_IMPORT_COMPLETE)
                    .putExtra(EXTRA_RESULT_CODE, RESULT_SUCCESS)
                    .putExtra(EXTRA_RESULT_COUNT, mContacts.size())
                    .putExtra(EXTRA_OPERATION_REQUESTED_AT_TIME, mStartTime)
                    .putExtra(EXTRA_SIM_SUBSCRIPTION_ID, mSim.getSubscriptionId());
        notification = getCompletedNotification(); //发送import成功的通知
    } else {
        result = new Intent(BROADCAST_SIM_IMPORT_COMPLETE)
                    .putExtra(EXTRA_RESULT_CODE, RESULT_FAILURE)
                    .putExtra(EXTRA_OPERATION_REQUESTED_AT_TIME, mStartTime)
                    .putExtra(EXTRA_SIM_SUBSCRIPTION_ID, mSim.getSubscriptionId());
        notification = getFailedNotification(); //发送import失败的通知
    }
    LocalBroadcastManager.getInstance(SimImportService.this).sendBroadcast(result);  //发送相关状态的广播
    sPending.remove(this);
    // Only notify of completion if all the import requests have finished. We're using
    // the same notification for imports so in the rare case that a user has started
    // multiple imports the notification won't go away until all of them complete.
    if (sPending.isEmpty()) {
        stopForeground(false);
        mNotificationManager.notify(NOTIFICATION_ID, notification);
    }
    notifyStateChanged();
}
packages/apps/Contacts/src/com/android/contacts/preference/DisplayOptionsPreferenceFragment.java
private class SaveServiceResultListener extends BroadcastReceiver {//接收广播
    @Override
    public void onReceive(Context context, Intent intent) {
        final long now = System.currentTimeMillis();
        final long opStart = intent.getLongExtra(
              SimImportService.EXTRA_OPERATION_REQUESTED_AT_TIME, now);
        // If it's been over 30 seconds the user is likely in a different context so suppress the toast message.
        if (now - opStart > 30*1000) return;
        final int code = intent.getIntExtra(SimImportService.EXTRA_RESULT_CODE,
              SimImportService.RESULT_UNKNOWN);
        final int count = intent.getIntExtra(SimImportService.EXTRA_RESULT_COUNT, -1);
        if (code == SimImportService.RESULT_SUCCESS && count > 0) {
            Snackbar.make(mRootView, getResources().getQuantityString(
                  R.plurals.sim_import_success_toast_fmt, count, count),
                  Snackbar.LENGTH_LONG).show();  //弹出import成功的Toast
        } else if (code == SimImportService.RESULT_FAILURE) {
            Snackbar.make(mRootView, R.string.sim_import_failed_toast,
                  Snackbar.LENGTH_LONG).show();  //弹出import失败的Toast
        }
    }
}

Export

是指将本地的联系人Export到SIM卡中,也就是说将设备中的联系人copy到SIM卡中,成功后发送Toast通知用户,照惯例先贴上逻辑流程图:

android联系人最新 android手机联系人导入方式_export_02


在点击Export选项之前的逻辑是一样的,我们看看之后的代码:

else if (KEY_EXPORT.equals(prefKey)) {
    ExportDialogFragment.show(getFragmentManager(), ContactsPreferenceActivity.class,
          ExportDialogFragment.EXPORT_MODE_ALL_CONTACTS);
    return true;
}
packages/apps/Contacts/src/com/android/contacts/interactions/ExportDialogFragment.java
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    //......省略的代码
    final DialogInterface.OnClickListener clickListener =
          new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            boolean dismissDialog;
            final int resId = adapter.getItem(which).mChoiceResourceId;
            if (resId == R.string.export_to_vcf_file) { //Export 联系人 to Vcard
                dismissDialog = true;
                final Intent exportIntent = new Intent(getActivity(), ExportVCardActivity.class);
                exportIntent.putExtra(VCardCommonArguments.ARG_CALLING_ACTIVITY,callingActivity);
                getActivity().startActivity(exportIntent);
            } else if (resId == R.string.share_contacts) { //分享联系人
                dismissDialog = true;
                if (mExportMode == EXPORT_MODE_FAVORITES) {
                    doShareFavoriteContacts();
                } else { // EXPORT_MODE_ALL_CONTACTS
                    final Intent exportIntent = new Intent(getActivity(), ShareVCardActivity.class);
                    exportIntent.putExtra(VCardCommonArguments.ARG_CALLING_ACTIVITY,callingActivity);
                    getActivity().startActivity(exportIntent);
                }
            } else if (resId == R.string.export_to_sim) { //Export 联系人 to SIM
                dismissDialog = true;
                int sub = adapter.getItem(which).mSim.getSubscriptionId();
                Intent pickContactIntent = new Intent(
                       SimContactsConstants.ACTION_MULTI_PICK_CONTACT,Contacts.CONTENT_URI);
                pickContactIntent.putExtra("exportSub", sub);
                getActivity().startActivity(pickContactIntent);
            } else {
                dismissDialog = true;
                Log.e(TAG, "Unexpected resource: "
                       + getActivity().getResources().getResourceEntryName(resId));
            }
            if (dismissDialog) {
                dialog.dismiss();
            }
        }
    };
    //......省略的代码
}

点击Export contacts SIM card选项,会进入到选择要导入的联系人页面MultiPickContactsActivity.java,勾选需要Export的联系人后,点击OK按钮:

@Override
public void onClick(View v) {
    int id = v.getId();
    switch (id) {
        case R.id.btn_ok:
            if (mPickMode.isSearchMode()) {
                exitSearchMode();
            }
            if (mDelete) {
                showDialog(R.id.dialog_delete_contact_confirmation);
            } else if(mExportSub > -1) {
                new ExportToSimThread().start();
            } 
             //......省略的代码 
    }
}
public class ExportToSimThread extends Thread {
    private int slot;  //SIM卡数
    private boolean canceled = false;
    private int freeSimCount = 0;  //SIM还可存储的人数
    private ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>();
    private Account account;
    final int BATCH_INSERT_NUMBER = 400;  //可Export的最大人数
    public ExportToSimThread() {
        slot = ContactUtils.getActiveSlotId(mContext, mExportSub);
        account = ContactUtils.getAcount(mContext, slot);
        showExportProgressDialog();
    }
    @Override
    public void run() {
        boolean isSimCardFull = false;
        // in case export is stopped, record the count of inserted successfully
        int insertCount = 0;
        freeSimCount = ContactUtils.getSimFreeCount(mContext, slot);
        boolean canSaveAnr = ContactUtils.canSaveAnr(mContext, slot);  //SIM卡中是否还可再存入Anr
        boolean canSaveEmail = ContactUtils.canSaveEmail(mContext, slot);  //SIM卡中是否还可再存入Email
        int emailCountInOneSimContact = ContactUtils.getOneSimEmailCount(mContext, slot);
        int phoneCountInOneSimContact = ContactUtils.getOneSimAnrCount(mContext, slot) + 1;
        int emptyAnr = ContactUtils.getSpareAnrCount(mContext, slot);
        int emptyEmail = ContactUtils.getSpareEmailCount(mContext, slot);
        int emptyNumber = freeSimCount + emptyAnr;
        Log.d(TAG, "freeSimCount = " + freeSimCount);
        Bundle choiceSet = (Bundle) mChoiceSet.clone(); //获得bundle中的数据
        Set<String> set = choiceSet.keySet();//获得bundle中的数据的键值对
        Iterator<String> i = set.iterator();
        while (i.hasNext() && !canceled) {
            String id = String.valueOf(i.next());
            String name = "";
            ArrayList<String> arrayNumber = new ArrayList<String>();
            ArrayList<String> arrayEmail = new ArrayList<String>();
            Uri dataUri = Uri.withAppendedPath(
                ContentUris.withAppendedId(Contacts.CONTENT_URI,Long.parseLong(id)),
                Contacts.Data.CONTENT_DIRECTORY);
            final String[] projection = new String[] { Contacts._ID,Contacts.Data.MIMETYPE, Contacts.Data.DATA1, };
            Cursor c = mContext.getContentResolver().query(dataUri,projection, null, null, null);
            try {
                if (c != null && c.moveToFirst()) {
                    do {
                        String mimeType = c.getString(1);
                        if (StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) { //获取name
                            name = c.getString(2);
                        }
                        if (Phone.CONTENT_ITEM_TYPE.equals(mimeType)) { //获取number
                            String number = c.getString(2);
                            if (!TextUtils.isEmpty(number)&& emptyNumber-- > 0) {
                                arrayNumber.add(number);
                            }
                        }
                        if (canSaveEmail) {
                            if (Email.CONTENT_ITEM_TYPE.equals(mimeType)) {  //获取Email
                                String email = c.getString(2);
                                if (!TextUtils.isEmpty(email)&& emptyEmail-- > 0) {
                                    arrayEmail.add(email);
                                }
                            }
                        }
                    } while (c.moveToNext());
                }
            } finally {
                if (c != null) {
                    c.close();
                }
            }
            if (freeSimCount > 0 && 0 == arrayNumber.size()&& 0 == arrayEmail.size()) {
                mToastHandler.sendMessage(mToastHandler.obtainMessage(
                        TOAST_EXPORT_NO_PHONE_OR_EMAIL, name));
                continue;
            }
            int nameCount = (name != null && !name.equals("")) ? 1 : 0;
            int groupNumCount = (arrayNumber.size() % phoneCountInOneSimContact) != 0 ?
                    (arrayNumber.size() / phoneCountInOneSimContact + 1)
                    : (arrayNumber.size() / phoneCountInOneSimContact);
            int groupEmailCount = emailCountInOneSimContact == 0 ? 0
                    : ((arrayEmail.size() % emailCountInOneSimContact) != 0 ? (arrayEmail
                            .size() / emailCountInOneSimContact + 1)
                            : (arrayEmail.size() / emailCountInOneSimContact));
            // recalute the group when spare anr is not enough
            if (canSaveAnr && emptyAnr >= 0 && emptyAnr <= groupNumCount) {
                groupNumCount = arrayNumber.size() - emptyAnr;
            }
            int groupCount = Math.max(groupEmailCount,
                    Math.max(nameCount, groupNumCount));
            Uri result = null;     
            for (int k = 0; k < groupCount; k++) {
                if (freeSimCount > 0) {
                    String num = arrayNumber.size() > 0 ? arrayNumber.remove(0) : null;
                    StringBuilder anrNum = new StringBuilder();
                    StringBuilder email = new StringBuilder();
                    if (canSaveAnr) {
                       for (int j = 1; j < phoneCountInOneSimContact; j++) {
                            if (arrayNumber.size() > 0 && emptyAnr-- > 0) {
                                String s = arrayNumber.remove(0);
                                anrNum.append(s);
                                anrNum.append(SimContactsConstants.ANR_SEP);
                            }
                        }
                    }
                    if (canSaveEmail) {
                        for (int j = 0; j < emailCountInOneSimContact; j++) {
                            if (arrayEmail.size() > 0) {
                                String s = arrayEmail.remove(0);
                                email.append(s);
                                email.append(SimContactsConstants.EMAIL_SEP);
                            }
                        }
                    }
                    result = ContactUtils.insertToCard(mContext, name,
                            num, email.toString(), anrNum.toString(), slot, false); //将选中联系人存入SIM卡中
                    if (null == result) {
                        // Failed to insert to SIM card
                        int anrNumber = 0;
                        if (!TextUtils.isEmpty(anrNum)) {
                            anrNumber += anrNum.toString().split(
                                   SimContactsConstants.ANR_SEP).length;
                        }                      
                        emptyAnr += anrNumber;
                        emptyNumber += anrNumber;
                        if (!TextUtils.isEmpty(num)) {
                            emptyNumber++;
                        }
                        if (!TextUtils.isEmpty(email)) {
                            emptyEmail += email.toString().split(SimContactsConstants.EMAIL_SEP).length;
                        }
                        mToastHandler.sendMessage(mToastHandler.obtainMessage(
                                        TOAST_SIM_EXPORT_FAILED, new String[] { name, num, email.toString() }));
                    } else {                 
                        insertCount++;
                        freeSimCount--;
                        batchInsert(name, num, anrNum.toString(),email.toString()); //同时将数据添加到数据库中
                    }
                } else {
                    isSimCardFull = true;
                    mToastHandler.sendMessage(mToastHandler.obtainMessage(TOAST_SIM_CARD_FULL, insertCount, 0));
                    break;
                }
            }
            if (isSimCardFull) { //如果SIM卡中存储已满,直接break
                break;
            }
        }
        if (operationList.size() > 0) {
            try {
                mContext.getContentResolver().applyBatch(
                       android.provider.ContactsContract.AUTHORITY,operationList);  //批量执行事务
            } catch (Exception e) {
                Log.e(TAG,String.format("%s: %s", e.toString(),e.getMessage()));
            } finally {
                operationList.clear();
            }
        }
        if (!isSimCardFull) {
            // if canceled, show toast indicating export is interrupted.
            if (canceled) {                    
                mToastHandler.sendMessage(mToastHandler.obtainMessage(TOAST_EXPORT_CANCELED,insertCount, 0));
            } else {
                mToastHandler.sendEmptyMessage(TOAST_EXPORT_FINISHED);  //发送Toast提示用户Export完成
            }
        }
        finish();
    }
}

ExportToSimThread 线程中主要完成两件事:一将勾选的联系人的信息Copy到SIM卡中;二将这些联系人的设置为SIM卡联系人的格式(仅含有姓名,电话,email,ANR字段且满组字段长度要求),并存入数据库中:

private void batchInsert(String name, String phoneNumber, String anrs,String emailAddresses) {
    final String[] emailAddressArray;
    final String[] anrArray;
    if (!TextUtils.isEmpty(emailAddresses)) {
        emailAddressArray = emailAddresses.split(",");
    } else {
        emailAddressArray = null;
    }
    if (!TextUtils.isEmpty(anrs)) {
        anrArray = anrs.split(SimContactsConstants.ANR_SEP);
    } else {
        anrArray = null;
    }
    Log.d(TAG, "insertToPhone: name= " + name + ", phoneNumber= " + phoneNumber
                    + ", emails= " + emailAddresses + ", anrs= " + anrs + ", account= " + account);
    //创建用于执行插入操作的Builder
    ContentProviderOperation.Builder builder = ContentProviderOperation.newInsert(RawContacts.CONTENT_URI);
    builder.withValue(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
    int ref = operationList.size();
    if (account != null) {  //Builder中添加Account类型
        builder.withValue(RawContacts.ACCOUNT_NAME, account.name);
        builder.withValue(RawContacts.ACCOUNT_TYPE, account.type);
    }
    operationList.add(builder.build());   //Builder添加到operationList事务中
    // do not allow empty value insert into database.
    if (!TextUtils.isEmpty(name)) {  //Builder中添加联系人姓名
        builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
        builder.withValueBackReference(StructuredName.RAW_CONTACT_ID, ref);
        builder.withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
        builder.withValue(StructuredName.GIVEN_NAME, name);
        operationList.add(builder.build());  //Builder添加到operationList事务中
    }
    if (!TextUtils.isEmpty(phoneNumber)) {  //Builder中添加联系人电话
        builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
        builder.withValueBackReference(Phone.RAW_CONTACT_ID, ref);
        builder.withValue(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
        builder.withValue(Phone.TYPE, Phone.TYPE_MOBILE);
        builder.withValue(Phone.NUMBER, phoneNumber);
        builder.withValue(Data.IS_PRIMARY, 1);
        operationList.add(builder.build());    //Builder添加到operationList事务中
    }
    if (anrArray != null) {
        for (String anr : anrArray) {
            if (!TextUtils.isEmpty(anr)) {   //Builder中添加联系人another电话
                builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
                builder.withValueBackReference(Phone.RAW_CONTACT_ID, ref);
                builder.withValue(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
                builder.withValue(Phone.TYPE, Phone.TYPE_HOME);
                builder.withValue(Phone.NUMBER, anr);
                operationList.add(builder.build());   //Builder添加到operationList事务中
            }
        }
    }
    if (emailAddressArray != null) {
        for (String emailAddress : emailAddressArray) {
            if (!TextUtils.isEmpty(emailAddress)) {  //Builder中添加联系人邮箱
                builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
                builder.withValueBackReference(Email.RAW_CONTACT_ID, ref);
                builder.withValue(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
                builder.withValue(Email.TYPE, Email.TYPE_MOBILE);
                builder.withValue(Email.ADDRESS, emailAddress);
                operationList.add(builder.build());  //Builder添加到operationList事务中
            }
        }
    }
    if (BATCH_INSERT_NUMBER - operationList.size() < 10) {
        try {
            mContext.getContentResolver().applyBatch(
                    android.provider.ContactsContract.AUTHORITY,operationList);
        } catch (Exception e) {
            Log.e(TAG,String.format("%s: %s", e.toString(),e.getMessage()));
        } finally {
            operationList.clear();
        }
    }
}

至此,Import & Export 关于SIM卡的操作分析完成。