生活中常有人因为更换手机而丢失联系人信息,又需要重新一个个去找亲戚朋友获取。
这样的情况下,联系人备份与恢复功能就显得非常实用。所以我学习了相关内容,并进行了适当整合,在这里整理出来。
本篇博客两个重点
- 使用VCard库进行联系人备份与恢复
- 异步进行备份与恢复操作
为什么要用VCard?
VCard是用于联系人数据存储的标准数据格式,且有一套已经成熟的库可以使用,通用于手机,也通用于邮件等,所以使用VCard进行联系人的数据存储与备份格式是非常好的选择。当然是用XML格式去存储也是可以的。
下载android-vcard.jar请猛戳这里
为什么要异步进行操作?
联系人备份与恢复都涉及到了数据库读写,而数据库的操作一向都是比较费时的,更何况是大量数据的情况下,所以将这些操作进行异步处理是一个非常有必要的事情,否则容易造成UI主线程卡顿。异步的另一个好处是同时能够在UI界面上给用户一些过程进度上的反馈,这个在重视产品交互体验的今天是非常重要的。
一个异步操作UI效果库,源自GitHub请猛戳这里
VCard本身库比较复杂,不再多说,有兴趣的可以研究源码。这里贴上一个已经做过一层封装的联系人处理类,直接集成调用即可。
封装好的联系人处理类model
package com.pku.codingma.backupandrecovercontactdemo;import android.app.Activity;import android.content.ContentUris;import android.content.ContentValues;import android.database.Cursor;import android.net.Uri;import android.os.Environment;import android.provider.ContactsContract;import java.io.BufferedReader;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStreamReader;import java.io.OutputStreamWriter;import java.io.UnsupportedEncodingException;import java.util.ArrayList;import java.util.List;import a_vcard.android.provider.Contacts;import a_vcard.android.syncml.pim.VDataBuilder;import a_vcard.android.syncml.pim.VNode;import a_vcard.android.syncml.pim.vcard.ContactStruct;import a_vcard.android.syncml.pim.vcard.VCardComposer;import a_vcard.android.syncml.pim.vcard.VCardException;import a_vcard.android.syncml.pim.vcard.VCardParser;/** * Created by ma on 2016/4/1. */public class ContactInfo { /** * MUST exist */ private String name; // 姓名 /** * 联系人电话信息 */ public static class PhoneInfo { /** * 联系电话类型 */ public int type; /** * 联系电话 */ public String number; } /** * 联系人邮箱信息 */ public static class EmailInfo { /** * 邮箱类型 */ public int type; /** * 邮箱 */ public String email; } private List<PhoneInfo> phoneList = new ArrayList<PhoneInfo>(); // 联系号码 private List<EmailInfo> email = new ArrayList<EmailInfo>(); // Email /** * 构造联系人信息 * * @param name 联系人姓名 */ public ContactInfo(String name) { this.name = name; } /** * 姓名 */ public String getName() { return name; } /** * 姓名 */ public ContactInfo setName(String name) { this.name = name; return this; } /** * 联系电话信息 */ public List<PhoneInfo> getPhoneList() { return phoneList; } /** * 联系电话信息 */ public ContactInfo setPhoneList(List<PhoneInfo> phoneList) { this.phoneList = phoneList; return this; } /** * 邮箱信息 */ public List<EmailInfo> getEmail() { return email; } /** * 邮箱信息 */ public ContactInfo setEmail(List<EmailInfo> email) { this.email = email; return this; } @Override public String toString() { return "{name: " + name + ", number: " + phoneList + ", email: " + email + "}"; }}
封装在ContactInfo内部的操作handler类
/** * 联系人 * 备份/还原操作 * */ public static class ContactHandler { private static ContactHandler instance_ = new ContactHandler(); /** * 获取实例 */ public static ContactHandler getInstance() { return instance_; } }
通过数据库获取手机中的联系人
/** * 获取联系人指定信息 * * @param projection 指定要获取的列数组, 获取全部列则设置为null * @return * @throws Exception */ public Cursor queryContact(Activity context, String[] projection) { // 获取联系人的所需信息 Cursor cur = context.getContentResolver().query(ContactsContract.Contacts.CONTENT_URI, projection, null, null, null); return cur; } /** * 获取联系人信息 * * @param context * @return */ public List<ContactInfo> getContactInfo(Activity context) { List<ContactInfo> infoList = new ArrayList<ContactInfo>(); Cursor cur = queryContact(context, null); if (cur.moveToFirst()) { do { // 获取联系人id号 String id = cur.getString(cur.getColumnIndex(ContactsContract.Contacts._ID)); // 获取联系人姓名 String displayName = cur.getString(cur.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME)); ContactInfo info = new ContactInfo(displayName);// 初始化联系人信息 // 查看联系人有多少电话号码, 如果没有返回0 int phoneCount = cur.getInt(cur.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER)); if (phoneCount > 0) { Cursor phonesCursor = context.getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, ContactsContract.CommonDataKinds.Phone.CONTACT_ID + "=" + id, null, null); if (phonesCursor.moveToFirst()) { List<PhoneInfo> phoneNumberList = new ArrayList<PhoneInfo>(); do { // 遍历所有电话号码 String phoneNumber = phonesCursor.getString(phonesCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)); // 对应的联系人类型 int type = phonesCursor.getInt(phonesCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.TYPE)); // 初始化联系人电话信息 ContactInfo.PhoneInfo phoneInfo = new ContactInfo.PhoneInfo(); phoneInfo.type = type; phoneInfo.number = phoneNumber; phoneNumberList.add(phoneInfo); } while (phonesCursor.moveToNext()); // 设置联系人电话信息 info.setPhoneList(phoneNumberList); } } // 获得联系人的EMAIL Cursor emailCur = context.getContentResolver().query(ContactsContract.CommonDataKinds.Email.CONTENT_URI, null, ContactsContract.CommonDataKinds.Email.CONTACT_ID + "=" + id, null, null); if (emailCur.moveToFirst()) { List<EmailInfo> emailList = new ArrayList<EmailInfo>(); do { // 遍历所有的email String email = emailCur.getString(emailCur.getColumnIndex(ContactsContract.CommonDataKinds.Email.DATA1)); int type = emailCur.getInt(emailCur.getColumnIndex(ContactsContract.CommonDataKinds.Email.TYPE)); // 初始化联系人邮箱信息 ContactInfo.EmailInfo emailInfo = new ContactInfo.EmailInfo(); emailInfo.type = type; // 设置邮箱类型 emailInfo.email = email; // 设置邮箱地址 emailList.add(emailInfo); } while (emailCur.moveToNext()); info.setEmail(emailList); } //Cursor postalCursor = getContentResolver().query(ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_URI, null, ContactsContract.CommonDataKinds.StructuredPostal.CONTACT_ID + "=" + id, null, null); infoList.add(info); } while (cur.moveToNext()); } cur.close(); return infoList; }
将获取到的联系人数据通过VCard数据格式备份
/** * 备份联系人 */ public void backupContacts(Activity context, List<ContactInfo> infos) { try { String path = Environment.getExternalStorageDirectory() + "/contacts.vcf"; OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(path), "UTF-8"); VCardComposer composer = new VCardComposer(); for (ContactInfo info : infos) { ContactStruct contact = new ContactStruct(); contact.name = info.getName(); // 获取联系人电话信息, 添加至 ContactStruct List<PhoneInfo> numberList = info .getPhoneList(); for (ContactInfo.PhoneInfo phoneInfo : numberList) { contact.addPhone(phoneInfo.type, phoneInfo.number, null, true); } // 获取联系人Email信息, 添加至 ContactStruct List<EmailInfo> emailList = info.getEmail(); for (ContactInfo.EmailInfo emailInfo : emailList) { contact.addContactmethod(Contacts.KIND_EMAIL, emailInfo.type, emailInfo.email, null, true); } String vcardString = composer.createVCard(contact, VCardComposer.VERSION_VCARD30_INT); writer.write(vcardString); writer.write("\n"); writer.flush(); } writer.close(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (VCardException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }
获取VCard文件中的联系人信息
/** * 获取vCard文件中的联系人信息 * * @return List<ContactInfo> */ public List<ContactInfo> restoreContacts() throws Exception { List<ContactInfo> contactInfoList = new ArrayList<ContactInfo>(); VCardParser parse = new VCardParser(); VDataBuilder builder = new VDataBuilder(); String file = Environment.getExternalStorageDirectory() + "/contacts2.vcf"; BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8")); String vcardString = ""; String line; while ((line = reader.readLine()) != null) { vcardString += line + "\n"; } reader.close(); boolean parsed = parse.parse(vcardString, "UTF-8", builder); if (!parsed) { throw new VCardException("Could not parse vCard file: " + file); } List<VNode> pimContacts = builder.vNodeList; for (VNode contact : pimContacts) { ContactStruct contactStruct = ContactStruct.constructContactFromVNode(contact, 1); // 获取备份文件中的联系人电话信息 List<ContactStruct.PhoneData> phoneDataList = contactStruct.phoneList; List<PhoneInfo> phoneInfoList = new ArrayList<PhoneInfo>(); for (ContactStruct.PhoneData phoneData : phoneDataList) { ContactInfo.PhoneInfo phoneInfo = new ContactInfo.PhoneInfo(); phoneInfo.number = phoneData.data; phoneInfo.type = phoneData.type; phoneInfoList.add(phoneInfo); } // 获取备份文件中的联系人邮箱信息 List<ContactStruct.ContactMethod> emailList = contactStruct.contactmethodList; List<EmailInfo> emailInfoList = new ArrayList<EmailInfo>(); // 存在 Email 信息 if (null != emailList) { for (ContactStruct.ContactMethod contactMethod : emailList) { if (Contacts.KIND_EMAIL == contactMethod.kind) { ContactInfo.EmailInfo emailInfo = new ContactInfo.EmailInfo(); emailInfo.email = contactMethod.data; emailInfo.type = contactMethod.type; emailInfoList.add(emailInfo); } } } ContactInfo info = new ContactInfo(contactStruct.name).setPhoneList(phoneInfoList).setEmail(emailInfoList); contactInfoList.add(info); } return contactInfoList; }
将从VCard中获取的联系人数据插入到手机中
/** * 向手机中录入联系人信息 * * @param info 要录入的联系人信息 */ public void addContacts(Activity context, ContactInfo info) { ContentValues values = new ContentValues(); //首先向RawContacts.CONTENT_URI执行一个空值插入,目的是获取系统返回的rawContactId Uri rawContactUri = context.getContentResolver().insert(ContactsContract.RawContacts.CONTENT_URI, values); long rawContactId = ContentUris.parseId(rawContactUri); //往data表入姓名数据 values.clear(); values.put(ContactsContract.RawContacts.Data.RAW_CONTACT_ID, rawContactId); values.put(ContactsContract.RawContacts.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE); values.put(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, info.getName()); context.getContentResolver().insert( ContactsContract.Data.CONTENT_URI, values); // 获取联系人电话信息 List<PhoneInfo> phoneList = info.getPhoneList(); /** 录入联系电话 */ for (ContactInfo.PhoneInfo phoneInfo : phoneList) { values.clear(); values.put(ContactsContract.Contacts.Data.RAW_CONTACT_ID, rawContactId); values.put(ContactsContract.RawContacts.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE); // 设置录入联系人电话信息 values.put(ContactsContract.CommonDataKinds.Phone.NUMBER, phoneInfo.number); values.put(ContactsContract.CommonDataKinds.Phone.TYPE, phoneInfo.type); // 往data表入电话数据 context.getContentResolver().insert( ContactsContract.Data.CONTENT_URI, values); } // 获取联系人邮箱信息 List<EmailInfo> emailList = info.getEmail(); /** 录入联系人邮箱信息 */ for (ContactInfo.EmailInfo email : emailList) { values.clear(); values.put(ContactsContract.Contacts.Data.RAW_CONTACT_ID, rawContactId); values.put(ContactsContract.RawContacts.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE); // 设置录入的邮箱信息 values.put(ContactsContract.CommonDataKinds.Email.DATA, email.email); values.put(ContactsContract.CommonDataKinds.Email.TYPE, email.type); // 往data表入Email数据 context.getContentResolver().insert( ContactsContract.Data.CONTENT_URI, values); } } }
异步处理与功能使用DEMO
package com.pku.codingma.backupandrecovercontactdemo;import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.support.v4.app.Fragment;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import com.dd.CircularProgressButton;import java.util.List;/** * A placeholder fragment containing a simple view. */public class BackupAndRecoverContactActivityFragment extends Fragment implements View.OnClickListener{ CircularProgressButton mBackupContactButton; CircularProgressButton mRecoverContactButton; //标记消息的来源 public final int BACKUP_WHAT = 0; public final int RECOVER_WHAT = 1; //标记成功还是失败 public final int SUCCESS_FLAG = 1; public final int FAIL_FLAG = 0; //用于进行备份和还原操作的ContactHandler内部类 ContactInfo.ContactHandler handler = ContactInfo.ContactHandler.getInstance(); public BackupAndRecoverContactActivityFragment() { } Handler mBackupAndRecoverProcessHandler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); if (msg.what == BACKUP_WHAT){ //add your action if (msg.arg1 == SUCCESS_FLAG){ mBackupContactButton.setProgress(100); }else { mBackupContactButton.setProgress(-1); } }else if (msg.what == RECOVER_WHAT){ //add your action if (msg.arg1 == SUCCESS_FLAG){ mRecoverContactButton.setProgress(100); }else { mRecoverContactButton.setProgress(-1); } } ShowTipTool.showTip(getActivity(), msg.obj.toString()); } }; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_backup_and_recover_contact, container, false); mBackupContactButton = (CircularProgressButton) view.findViewById(R.id.backup_contact_button); mRecoverContactButton = (CircularProgressButton) view.findViewById(R.id.recover_contact_button); initEvent(); return view; } private void initEvent() { mRecoverContactButton.setOnClickListener(this); mBackupContactButton.setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()){ case R.id.backup_contact_button: backup_contact(); break; case R.id.recover_contact_button: recover_contact(); break; default: break; } } public void backup_contact(){ //让按钮进入工作状态 mBackupContactButton.setIndeterminateProgressMode(true); mBackupContactButton.setProgress(50); new Thread(new Runnable() { @Override public void run() { //新建一条Handler处理的消息 Message message = new Message(); try{ // 进行备份联系人信息动作 handler.backupContacts(getActivity(), handler.getContactInfo(getActivity())); //如果顺利,则将消息的参数设置为成功 message.obj = "backup success"; message.arg1 = SUCCESS_FLAG; }catch (Exception e){ //如果出现异常,则将消息的参数设置为失败 message.obj = "backup fail"; message.arg1 = FAIL_FLAG; e.printStackTrace(); }finally { //最后设置消息来源并发送 message.what = BACKUP_WHAT; mBackupAndRecoverProcessHandler.sendMessage(message); } } }).start(); } //与backup基本相同,不再注释 public void recover_contact(){ mRecoverContactButton.setIndeterminateProgressMode(true); mRecoverContactButton.setProgress(50); new Thread(new Runnable() { @Override public void run() { Message message = new Message(); try { // 获取要恢复的联系人信息 List<ContactInfo> infoList = handler.restoreContacts(); for (ContactInfo contactInfo : infoList) { // 恢复联系人 handler.addContacts(getActivity(), contactInfo); } message.obj = "recover success"; message.arg1 = SUCCESS_FLAG; } catch (Exception e) { message.obj = "recover fail"; message.arg1 = FAIL_FLAG; e.printStackTrace(); }finally { message.what = RECOVER_WHAT; mBackupAndRecoverProcessHandler.sendMessage(message); } } }).start(); }}
添加读写权限
<uses-permission android:name="android.permission.READ_CONTACTS"/><uses-permission android:name="android.permission.WRITE_CONTACTS"/><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
效果图若干张
依次为初始状态,处理状态,完成状态,异常出错状态
Demo完整代码请猛戳这里
文章原始来源与转载请标明的出处http://blog.csdn.net/u012145166/article/details/51052880
参考http://www.cnblogs.com/lw900320/archive/2013/01/10/2855145.html